[clang] [Clang] Implement C++26’s P2893R3 ‘Variadic friends’ (PR #101448)

via cfe-commits cfe-commits at lists.llvm.org
Tue Aug 13 04:14:54 PDT 2024


https://github.com/Sirraide updated https://github.com/llvm/llvm-project/pull/101448

>From 1fd8db659961fd1702c0c786feb71ad6c5998aa8 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Wed, 31 Jul 2024 21:44:02 +0200
Subject: [PATCH 01/20] [Parser] Parse variadic friends

---
 clang/include/clang/AST/DeclFriend.h          | 15 +++-
 .../clang/Basic/DiagnosticSemaKinds.td        |  2 +
 clang/include/clang/Sema/Sema.h               |  6 +-
 clang/lib/AST/ASTImporter.cpp                 |  7 +-
 clang/lib/AST/DeclFriend.cpp                  | 14 +--
 clang/lib/AST/DeclPrinter.cpp                 |  3 +
 clang/lib/AST/ODRHash.cpp                     |  1 +
 clang/lib/AST/TextNodeDumper.cpp              |  2 +
 clang/lib/Parse/ParseDeclCXX.cpp              | 89 +++++++++++++++++++
 clang/lib/Sema/SemaDecl.cpp                   |  8 +-
 clang/lib/Sema/SemaDeclCXX.cpp                | 26 ++++--
 .../lib/Sema/SemaTemplateInstantiateDecl.cpp  |  3 +
 clang/lib/Serialization/ASTReaderDecl.cpp     |  1 +
 clang/lib/Serialization/ASTWriterDecl.cpp     |  1 +
 .../Parser/cxx2c-variadic-friends-errors.cpp  | 63 +++++++++++++
 clang/test/Parser/cxx2c-variadic-friends.cpp  | 60 +++++++++++++
 16 files changed, 278 insertions(+), 23 deletions(-)
 create mode 100644 clang/test/Parser/cxx2c-variadic-friends-errors.cpp
 create mode 100644 clang/test/Parser/cxx2c-variadic-friends.cpp

diff --git a/clang/include/clang/AST/DeclFriend.h b/clang/include/clang/AST/DeclFriend.h
index 9789282f351a55..1c2c86d3ef3849 100644
--- a/clang/include/clang/AST/DeclFriend.h
+++ b/clang/include/clang/AST/DeclFriend.h
@@ -70,6 +70,9 @@ class FriendDecl final
   // Location of the 'friend' specifier.
   SourceLocation FriendLoc;
 
+  // Location of the '...', if present.
+  SourceLocation EllipsisLoc;
+
   /// True if this 'friend' declaration is unsupported.  Eventually we
   /// will support every possible friend declaration, but for now we
   /// silently ignore some and set this flag to authorize all access.
@@ -82,10 +85,11 @@ class FriendDecl final
   unsigned NumTPLists : 31;
 
   FriendDecl(DeclContext *DC, SourceLocation L, FriendUnion Friend,
-             SourceLocation FriendL,
+             SourceLocation FriendL, SourceLocation EllipsisLoc,
              ArrayRef<TemplateParameterList *> FriendTypeTPLists)
       : Decl(Decl::Friend, DC, L), Friend(Friend), FriendLoc(FriendL),
-        UnsupportedFriend(false), NumTPLists(FriendTypeTPLists.size()) {
+        EllipsisLoc(EllipsisLoc), UnsupportedFriend(false),
+        NumTPLists(FriendTypeTPLists.size()) {
     for (unsigned i = 0; i < NumTPLists; ++i)
       getTrailingObjects<TemplateParameterList *>()[i] = FriendTypeTPLists[i];
   }
@@ -110,7 +114,7 @@ class FriendDecl final
 
   static FriendDecl *
   Create(ASTContext &C, DeclContext *DC, SourceLocation L, FriendUnion Friend_,
-         SourceLocation FriendL,
+         SourceLocation FriendL, SourceLocation EllipsisLoc = {},
          ArrayRef<TemplateParameterList *> FriendTypeTPLists = std::nullopt);
   static FriendDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID,
                                         unsigned FriendTypeNumTPLists);
@@ -143,6 +147,9 @@ class FriendDecl final
     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 (NamedDecl *ND = getFriendDecl()) {
@@ -177,6 +184,8 @@ class FriendDecl final
     UnsupportedFriend = Unsupported;
   }
 
+  bool isVariadic() const { return EllipsisLoc.isValid(); }
+
   // Implement isa/cast/dyncast/etc.
   static bool classof(const Decl *D) { return classofKind(D->getKind()); }
   static bool classofKind(Kind K) { return K == Decl::Friend; }
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 581434d33c5c9a..8daa2386ef64f5 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -1738,6 +1738,8 @@ def ext_friend_tag_redecl_outside_namespace : ExtWarn<
   "enclosing namespace is a Microsoft extension; add a nested name specifier">,
   InGroup<MicrosoftUnqualifiedFriend>;
 def err_pure_friend : Error<"friend declaration cannot have a pure-specifier">;
+def err_friend_template_decl_multiple_specifiers: Error<
+  "a friend declaration that befriends a template must contain exactly one type-specifier">;
 
 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 2ec6367eccea01..07c8c2cef9cc30 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -3800,7 +3800,8 @@ class Sema final : public SemaBase {
                                    const ParsedAttributesView &DeclAttrs,
                                    MultiTemplateParamsArg TemplateParams,
                                    bool IsExplicitInstantiation,
-                                   RecordDecl *&AnonRecord);
+                                   RecordDecl *&AnonRecord,
+                                   SourceLocation FriendEllipsisLoc = {});
 
   /// BuildAnonymousStructOrUnion - Handle the declaration of an
   /// anonymous structure or union. Anonymous unions are a C++ feature
@@ -5538,7 +5539,8 @@ class Sema final : public SemaBase {
   /// parameters present at all, require proper matching, i.e.
   ///   template <> template \<class T> friend class A<int>::B;
   Decl *ActOnFriendTypeDecl(Scope *S, const DeclSpec &DS,
-                            MultiTemplateParamsArg TemplateParams);
+                            MultiTemplateParamsArg TemplateParams,
+                            SourceLocation FriendEllipsisLoc);
   NamedDecl *ActOnFriendFunctionDecl(Scope *S, Declarator &D,
                                      MultiTemplateParamsArg TemplateParams);
 
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 103235547f482e..b0043eb8761833 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -4424,11 +4424,14 @@ ExpectedDecl ASTNodeImporter::VisitFriendDecl(FriendDecl *D) {
   auto FriendLocOrErr = import(D->getFriendLoc());
   if (!FriendLocOrErr)
     return FriendLocOrErr.takeError();
+  auto EllipsisLocOrErr = import(D->getEllipsisLoc());
+  if (!EllipsisLocOrErr)
+    return EllipsisLocOrErr.takeError();
 
   FriendDecl *FrD;
   if (GetImportedOrCreateDecl(FrD, D, Importer.getToContext(), DC,
-                              *LocationOrErr, ToFU,
-                              *FriendLocOrErr, ToTPLists))
+                              *LocationOrErr, ToFU, *FriendLocOrErr,
+                              *EllipsisLocOrErr, ToTPLists))
     return FrD;
 
   FrD->setAccess(D->getAccess());
diff --git a/clang/lib/AST/DeclFriend.cpp b/clang/lib/AST/DeclFriend.cpp
index 04b9b93699f36c..8b285bfce8d522 100644
--- a/clang/lib/AST/DeclFriend.cpp
+++ b/clang/lib/AST/DeclFriend.cpp
@@ -31,11 +31,11 @@ FriendDecl *FriendDecl::getNextFriendSlowCase() {
                            NextFriend.get(getASTContext().getExternalSource()));
 }
 
-FriendDecl *FriendDecl::Create(ASTContext &C, DeclContext *DC,
-                               SourceLocation L,
-                               FriendUnion Friend,
-                               SourceLocation FriendL,
-                          ArrayRef<TemplateParameterList *> FriendTypeTPLists) {
+FriendDecl *
+FriendDecl::Create(ASTContext &C, DeclContext *DC, SourceLocation L,
+                   FriendUnion Friend, SourceLocation FriendL,
+                   SourceLocation EllipsisLoc,
+                   ArrayRef<TemplateParameterList *> FriendTypeTPLists) {
 #ifndef NDEBUG
   if (Friend.is<NamedDecl *>()) {
     const auto *D = Friend.get<NamedDecl*>();
@@ -56,8 +56,8 @@ FriendDecl *FriendDecl::Create(ASTContext &C, DeclContext *DC,
   std::size_t Extra =
       FriendDecl::additionalSizeToAlloc<TemplateParameterList *>(
           FriendTypeTPLists.size());
-  auto *FD = new (C, DC, Extra) FriendDecl(DC, L, Friend, FriendL,
-                                           FriendTypeTPLists);
+  auto *FD = new (C, DC, Extra)
+      FriendDecl(DC, L, Friend, FriendL, EllipsisLoc, FriendTypeTPLists);
   cast<CXXRecordDecl>(DC)->pushFriendDecl(FD);
   return FD;
 }
diff --git a/clang/lib/AST/DeclPrinter.cpp b/clang/lib/AST/DeclPrinter.cpp
index 26773a69ab9acf..1a8ac103841ec3 100644
--- a/clang/lib/AST/DeclPrinter.cpp
+++ b/clang/lib/AST/DeclPrinter.cpp
@@ -885,6 +885,9 @@ void DeclPrinter::VisitFriendDecl(FriendDecl *D) {
     Out << "friend ";
     VisitRedeclarableTemplateDecl(CTD);
   }
+
+  if (D->isVariadic())
+    Out << "...";
 }
 
 void DeclPrinter::VisitFieldDecl(FieldDecl *D) {
diff --git a/clang/lib/AST/ODRHash.cpp b/clang/lib/AST/ODRHash.cpp
index fbfe92318dc5ee..65a02a6b661495 100644
--- a/clang/lib/AST/ODRHash.cpp
+++ b/clang/lib/AST/ODRHash.cpp
@@ -461,6 +461,7 @@ class ODRDeclVisitor : public ConstDeclVisitor<ODRDeclVisitor> {
     } else {
       AddDecl(D->getFriendDecl());
     }
+    Hash.AddBoolean(D->isVariadic());
   }
 
   void VisitTemplateTypeParmDecl(const TemplateTypeParmDecl *D) {
diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp
index 5ba9523504258e..585d88e2e031ee 100644
--- a/clang/lib/AST/TextNodeDumper.cpp
+++ b/clang/lib/AST/TextNodeDumper.cpp
@@ -2694,6 +2694,8 @@ void TextNodeDumper::VisitAccessSpecDecl(const AccessSpecDecl *D) {
 void TextNodeDumper::VisitFriendDecl(const FriendDecl *D) {
   if (TypeSourceInfo *T = D->getFriendType())
     dumpType(T->getType());
+  if (D->isVariadic())
+    OS << " variadic";
 }
 
 void TextNodeDumper::VisitObjCIvarDecl(const ObjCIvarDecl *D) {
diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index ce827c689beb73..d5c4390e221e53 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -2816,6 +2816,7 @@ void Parser::MaybeParseAndDiagnoseDeclSpecAfterCXX11VirtSpecifierSeq(
 ///       member-declaration:
 ///         decl-specifier-seq[opt] member-declarator-list[opt] ';'
 ///         function-definition ';'[opt]
+/// [C++26] friend-type-declaration
 ///         ::[opt] nested-name-specifier template[opt] unqualified-id ';'[TODO]
 ///         using-declaration                                            [TODO]
 /// [C++0x] static_assert-declaration
@@ -2848,6 +2849,18 @@ void Parser::MaybeParseAndDiagnoseDeclSpecAfterCXX11VirtSpecifierSeq(
 ///       constant-initializer:
 ///         '=' constant-expression
 ///
+///       friend-type-declaration:
+///         'friend' friend-type-specifier-list ;
+///
+///       friend-type-specifier-list:
+///         friend-type-specifier ...[opt]
+///         friend-type-specifier-list , friend-type-specifier ...[opt]
+///
+///       friend-type-specifier:
+///         simple-type-specifier
+///         elaborated-type-specifier
+///         typename-specifier
+///
 Parser::DeclGroupPtrTy Parser::ParseCXXClassMemberDeclaration(
     AccessSpecifier AS, ParsedAttributes &AccessAttrs,
     ParsedTemplateInfo &TemplateInfo, ParsingDeclRAIIObject *TemplateDiags) {
@@ -3049,6 +3062,82 @@ Parser::DeclGroupPtrTy Parser::ParseCXXClassMemberDeclaration(
   if (DS.hasTagDefinition())
     Actions.ActOnDefinedDeclarationSpecifier(DS.getRepAsDecl());
 
+  // Handle C++26's variadic friend declarations. These don't even have
+  // declarators, so we get them out of the way early here.
+  //
+  // C++26 [class.mem.general]p10: If a name-declaration matches the
+  // syntactic requirements of friend-type-declaration, it is a
+  // friend-type-declaration.
+  //
+  // This means that e.g. 'friend int, long;' is valid, but
+  // 'int friend, long;' is not.
+  //
+  // TODO: Do we want to expose this in earlier language modes?
+  if (DS.isFriendSpecifiedFirst() && getLangOpts().CPlusPlus26 &&
+      Tok.isOneOf(tok::comma, tok::ellipsis)) {
+    SourceLocation FriendLoc = DS.getFriendSpecLoc();
+    SmallVector<Decl *> Decls;
+
+    // Handles a single friend-type-specifier.
+    auto ParsedFriendDecl = [&](ParsingDeclSpec &DeclSpec) {
+      bool Variadic = Tok.is(tok::ellipsis);
+      RecordDecl *AnonRecord = nullptr;
+
+      Decl *D = Actions.ParsedFreeStandingDeclSpec(
+          getCurScope(), AS, DeclSpec, DeclAttrs, TemplateParams, false,
+          AnonRecord, Variadic ? Tok.getLocation() : SourceLocation());
+      DeclSpec.complete(D);
+      if (!D) {
+        SkipUntil(tok::semi);
+        return true;
+      }
+
+      // Eat the '...'.
+      if (Variadic)
+        ConsumeToken();
+
+      Decls.push_back(D);
+      return false;
+    };
+
+    if (ParsedFriendDecl(DS))
+      return nullptr;
+
+    // TODO: It seems like this case is already being caught somewhere else,
+    // so maybe we don't need this check here at all?
+    /*// CWG 2917: In a template-declaration whose declaration is a
+    // friend-type-declaration, the friend-type-specifier-list shall
+    // consist of exactly one friend-type-specifier.
+    //
+    // Essentially, the following is obviously nonsense, so disallow it:
+    //
+    //   template <typename>
+    //   friend class S, int;
+    if (!TemplateParams.empty() && Tok.is(tok::comma)) {
+      Diag(Decls.front()->getLocation(),
+           diag::err_friend_template_decl_multiple_specifiers)
+          << Decls.front()->getSourceRange();
+      SkipUntil(tok::semi);
+      return nullptr;
+    }*/
+
+    while (TryConsumeToken(tok::comma)) {
+      ParsingDeclSpec DeclSpec(*this, TemplateDiags);
+      const char *PrevSpec = nullptr;
+      unsigned DiagId = 0;
+      DeclSpec.SetFriendSpec(FriendLoc, PrevSpec, DiagId);
+      ParseDeclarationSpecifiers(DeclSpec, TemplateInfo, AS,
+                                 DeclSpecContext::DSC_class, nullptr);
+      if (ParsedFriendDecl(DeclSpec))
+        return nullptr;
+    }
+
+    ExpectAndConsume(tok::semi, diag::err_expected_semi_after_stmt,
+                     "friend declaration");
+
+    return Actions.BuildDeclaratorGroup(Decls);
+  }
+
   ParsingDeclarator DeclaratorInfo(*this, DS, DeclAttrs,
                                    DeclaratorContext::Member);
   if (TemplateInfo.TemplateParams)
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 694a754646f274..3b8c07a893eced 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -5000,7 +5000,8 @@ Decl *Sema::ParsedFreeStandingDeclSpec(Scope *S, AccessSpecifier AS,
                                        const ParsedAttributesView &DeclAttrs,
                                        MultiTemplateParamsArg TemplateParams,
                                        bool IsExplicitInstantiation,
-                                       RecordDecl *&AnonRecord) {
+                                       RecordDecl *&AnonRecord,
+                                       SourceLocation FriendEllipsisLoc) {
   Decl *TagD = nullptr;
   TagDecl *Tag = nullptr;
   if (DS.getTypeSpecType() == DeclSpec::TST_class ||
@@ -5067,9 +5068,12 @@ Decl *Sema::ParsedFreeStandingDeclSpec(Scope *S, AccessSpecifier AS,
     // whatever routines created it handled the friendship aspect.
     if (TagD && !Tag)
       return nullptr;
-    return ActOnFriendTypeDecl(S, DS, TemplateParams);
+    return ActOnFriendTypeDecl(S, DS, TemplateParams, FriendEllipsisLoc);
   }
 
+  assert(FriendEllipsisLoc.isInvalid() &&
+         "Friend ellipsis but not friend-specified?");
+
   // Track whether this decl-specifier declares anything.
   bool DeclaresAnything = true;
 
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 5782daa041f32d..f19a7f4c636a08 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -17435,8 +17435,9 @@ DeclResult Sema::ActOnTemplatedFriendTag(
       TL.getNamedTypeLoc().castAs<TypeSpecTypeLoc>().setNameLoc(NameLoc);
     }
 
-    FriendDecl *Friend = FriendDecl::Create(Context, CurContext, NameLoc,
-                                            TSI, FriendLoc, TempParamLists);
+    FriendDecl *Friend =
+        FriendDecl::Create(Context, CurContext, NameLoc, TSI, FriendLoc,
+                           /*EllipsisLoc=*/SourceLocation(), TempParamLists);
     Friend->setAccess(AS_public);
     CurContext->addDecl(Friend);
     return Friend;
@@ -17459,8 +17460,9 @@ DeclResult Sema::ActOnTemplatedFriendTag(
   TL.setQualifierLoc(SS.getWithLocInContext(Context));
   TL.setNameLoc(NameLoc);
 
-  FriendDecl *Friend = FriendDecl::Create(Context, CurContext, NameLoc,
-                                          TSI, FriendLoc, TempParamLists);
+  FriendDecl *Friend =
+      FriendDecl::Create(Context, CurContext, NameLoc, TSI, FriendLoc,
+                         /*EllipsisLoc=*/SourceLocation(), TempParamLists);
   Friend->setAccess(AS_public);
   Friend->setUnsupportedFriend(true);
   CurContext->addDecl(Friend);
@@ -17468,7 +17470,8 @@ DeclResult Sema::ActOnTemplatedFriendTag(
 }
 
 Decl *Sema::ActOnFriendTypeDecl(Scope *S, const DeclSpec &DS,
-                                MultiTemplateParamsArg TempParams) {
+                                MultiTemplateParamsArg TempParams,
+                                SourceLocation FriendEllipsisLoc) {
   SourceLocation Loc = DS.getBeginLoc();
   SourceLocation FriendLoc = DS.getFriendSpecLoc();
 
@@ -17509,8 +17512,17 @@ Decl *Sema::ActOnFriendTypeDecl(Scope *S, const DeclSpec &DS,
   if (TheDeclarator.isInvalidType())
     return nullptr;
 
-  if (DiagnoseUnexpandedParameterPack(Loc, TSI, UPPC_FriendDeclaration))
+  // If '...' is present, the type must contain an unexpanded parameter
+  // pack, and vice versa.
+  if (FriendEllipsisLoc.isInvalid() &&
+      DiagnoseUnexpandedParameterPack(Loc, TSI, UPPC_FriendDeclaration))
     return nullptr;
+  if (FriendEllipsisLoc.isValid() &&
+      !TSI->getType()->containsUnexpandedParameterPack()) {
+    Diag(FriendEllipsisLoc, diag::err_pack_expansion_without_parameter_packs)
+        << TSI->getTypeLoc().getSourceRange();
+    FriendEllipsisLoc = SourceLocation();
+  }
 
   if (!T->isElaboratedTypeSpecifier()) {
     if (TempParams.size()) {
@@ -17560,7 +17572,7 @@ Decl *Sema::ActOnFriendTypeDecl(Scope *S, const DeclSpec &DS,
                                    FriendLoc);
   else
     D = FriendDecl::Create(Context, CurContext, TSI->getTypeLoc().getBeginLoc(),
-                           TSI, FriendLoc);
+                           TSI, FriendLoc, FriendEllipsisLoc);
 
   if (!D)
     return nullptr;
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index f93cd113988ae4..879b32a6f4f5eb 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -1431,6 +1431,9 @@ Decl *TemplateDeclInstantiator::VisitIndirectFieldDecl(IndirectFieldDecl *D) {
 }
 
 Decl *TemplateDeclInstantiator::VisitFriendDecl(FriendDecl *D) {
+  assert(D->getEllipsisLoc().isInvalid() &&
+         "TODO: Instantiate variadic friend decl");
+
   // Handle friend type expressions by simply substituting template
   // parameters into the pattern type and checking the result.
   if (TypeSourceInfo *Ty = D->getFriendType()) {
diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp
index 31ab6c651d59f4..5791cef8f08889 100644
--- a/clang/lib/Serialization/ASTReaderDecl.cpp
+++ b/clang/lib/Serialization/ASTReaderDecl.cpp
@@ -2354,6 +2354,7 @@ void ASTDeclReader::VisitFriendDecl(FriendDecl *D) {
   D->NextFriend = readDeclID().getRawValue();
   D->UnsupportedFriend = (Record.readInt() != 0);
   D->FriendLoc = readSourceLocation();
+  D->EllipsisLoc = readSourceLocation();
 }
 
 void ASTDeclReader::VisitFriendTemplateDecl(FriendTemplateDecl *D) {
diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp
index 17c774038571ef..8862ced487d13e 100644
--- a/clang/lib/Serialization/ASTWriterDecl.cpp
+++ b/clang/lib/Serialization/ASTWriterDecl.cpp
@@ -1654,6 +1654,7 @@ void ASTDeclWriter::VisitFriendDecl(FriendDecl *D) {
   Record.AddDeclRef(D->getNextFriend());
   Record.push_back(D->UnsupportedFriend);
   Record.AddSourceLocation(D->FriendLoc);
+  Record.AddSourceLocation(D->EllipsisLoc);
   Code = serialization::DECL_FRIEND;
 }
 
diff --git a/clang/test/Parser/cxx2c-variadic-friends-errors.cpp b/clang/test/Parser/cxx2c-variadic-friends-errors.cpp
new file mode 100644
index 00000000000000..dfbe9ad1f6f68b
--- /dev/null
+++ b/clang/test/Parser/cxx2c-variadic-friends-errors.cpp
@@ -0,0 +1,63 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++2c %s
+
+template <typename> struct TS; // #template
+
+// CHECK-LABEL: CXXRecordDecl {{.*}} struct Errors
+struct Errors {
+  // We simply ignore the '...' here.
+  // CHECK: FriendDecl {{.*}} 'float'
+  friend float...; // expected-error {{pack expansion does not contain any unexpanded parameter packs}}
+
+  // CHECK-NEXT: FriendDecl {{.*}} 'short'
+  // CHECK-NEXT: FriendDecl {{.*}} 'unsigned int'
+  // CHECK-NEXT: FriendDecl {{.*}} 'unsigned short'
+  friend short..., unsigned, unsigned short...; // expected-error 2 {{pack expansion does not contain any unexpanded parameter packs}}
+
+  // FIXME: This is a pretty bad diagnostic.
+  template <typename>
+  friend struct TS, int; // expected-error {{cannot be referenced with the 'struct' specifier}}
+                         // expected-note@#template {{declared here}}
+
+  double friend; // expected-error {{'friend' must appear first in a non-function declaration}}
+  double friend, double; // expected-error {{expected member name or ';' after declaration specifiers}}
+};
+
+struct C { template<class T> class Nested; }; // expected-note 2 {{'C::Nested' declared here}}
+struct S {
+  template<class T>
+  friend class C::Nested;
+};
+
+template<class... Ts>
+struct VS {
+  template<class... Us>
+  friend Us...; // expected-error {{friend type templates must use an elaborated type}}
+
+  template<class... Us> // expected-note {{is declared here}}
+  friend class Us...; // expected-error {{declaration of 'Us' shadows template parameter}}
+                      // expected-error at -1 {{pack expansion does not contain any unexpanded parameter packs}}
+
+  template<class U>
+  friend class C<Ts>::Nested<U>...; // expected-error {{explicit specialization of non-template class 'C'}}
+                                    // expected-error at -1 {{no template named 'Nested' in the global namespace}}
+                                    // expected-error at -2 {{friends can only be classes or functions}}
+                                    // expected-error at -3 {{expected ';' at end of declaration list}}
+
+  template<class... Us>
+  friend class C<Ts...>::Nested<Us>...; // expected-error {{explicit specialization of non-template class 'C'}}
+                                        // expected-error at -1 {{no template named 'Nested' in the global namespace}}
+                                        // expected-error at -2 {{friends can only be classes or functions}}
+                                        // expected-error at -3 {{expected ';' at end of declaration list}}
+
+  template<class... Us>
+  friend class C<Us>::Nested...; // expected-error {{explicit specialization of non-template class 'C'}}
+                                 // expected-error at -1 {{friends can only be classes or functions}}
+                                 // expected-error at -2 {{expected ';' at end of declaration list}}
+};
+
+
+template<class... Ts> // expected-note {{template parameter is declared here}}
+struct S2 {
+  friend class Ts...; // expected-error {{declaration of 'Ts' shadows template parameter}}
+                      // expected-error at -1 {{pack expansion does not contain any unexpanded parameter packs}}
+};
\ No newline at end of file
diff --git a/clang/test/Parser/cxx2c-variadic-friends.cpp b/clang/test/Parser/cxx2c-variadic-friends.cpp
new file mode 100644
index 00000000000000..85f696ebe28375
--- /dev/null
+++ b/clang/test/Parser/cxx2c-variadic-friends.cpp
@@ -0,0 +1,60 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++2c %s
+// RUN: %clang_cc1 -fsyntax-only -ast-dump -std=c++2c %s | FileCheck %s
+// expected-no-diagnostics
+
+struct S;
+template <typename> struct TS; // #template
+
+// CHECK-LABEL: CXXRecordDecl {{.*}} struct Friends
+struct Friends {
+  // CHECK: FriendDecl {{.*}} 'int'
+  // CHECK-NEXT: FriendDecl {{.*}} 'long'
+  friend int, long;
+
+  // CHECK-NEXT: FriendDecl {{.*}} 'int'
+  // CHECK-NEXT: FriendDecl {{.*}} 'long'
+  // CHECK-NEXT: FriendDecl {{.*}} 'char'
+  friend int, long, char;
+
+  // CHECK-NEXT: FriendDecl {{.*}} 'S'
+  friend S;
+
+  // CHECK-NEXT: FriendDecl {{.*}} 'S'
+  // CHECK-NEXT: FriendDecl {{.*}} 'S'
+  // CHECK-NEXT: FriendDecl {{.*}} 'S'
+  friend S, S, S;
+
+  // CHECK-NEXT: FriendDecl
+  // CHECK-NEXT: ClassTemplateDecl {{.*}} friend TS
+  template <typename>
+  friend struct TS;
+};
+
+namespace specialisations {
+template<class T>
+struct C {
+  template<class U> struct Nested;
+};
+
+struct N {
+  template<class U> class C;
+};
+
+// CHECK-LABEL: ClassTemplateDecl {{.*}} Variadic
+// CHECK: FriendDecl {{.*}} 'Pack' variadic
+// CHECK-NEXT: FriendDecl {{.*}} 'TS<Pack>' variadic
+template <typename ...Pack>
+struct Variadic {
+  friend Pack...;
+  friend TS<Pack>...;
+};
+
+// CHECK-LABEL: ClassTemplateDecl {{.*}} S2
+// CHECK: FriendDecl {{.*}} 'class C<Ts>':'C<Ts>' variadic
+// CHECK-NEXT: FriendDecl {{.*}} 'class N::C<Ts>':'C<Ts>' variadic
+template<class... Ts>
+struct S2 {
+  friend class C<Ts>...;
+  friend class N::C<Ts>...;
+};
+}

>From ddb83c24d535c4518a0d765989ba7eb6a35f4cf6 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Wed, 31 Jul 2024 23:38:01 +0200
Subject: [PATCH 02/20] [Sema] Instantiate variadic friends

---
 clang/include/clang/AST/DeclFriend.h          |  68 +++++++--
 clang/include/clang/AST/RecursiveASTVisitor.h |   2 +
 clang/include/clang/Basic/DeclNodes.td        |   1 +
 .../include/clang/Serialization/ASTBitCodes.h |   3 +
 clang/lib/AST/ASTImporter.cpp                 |  26 ++++
 clang/lib/AST/DeclBase.cpp                    |   1 +
 clang/lib/AST/DeclFriend.cpp                  |  20 +++
 clang/lib/CodeGen/CGDecl.cpp                  |   1 +
 .../lib/Sema/SemaTemplateInstantiateDecl.cpp  |  54 ++++++-
 clang/lib/Serialization/ASTCommon.cpp         |   1 +
 clang/lib/Serialization/ASTReaderDecl.cpp     |  11 ++
 clang/lib/Serialization/ASTWriterDecl.cpp     |   9 ++
 .../Parser/cxx2c-variadic-friends-errors.cpp  |   5 +-
 clang/test/SemaCXX/cxx2c-variadic-friends.cpp | 136 ++++++++++++++++++
 clang/tools/libclang/CIndex.cpp               |   1 +
 15 files changed, 324 insertions(+), 15 deletions(-)
 create mode 100644 clang/test/SemaCXX/cxx2c-variadic-friends.cpp

diff --git a/clang/include/clang/AST/DeclFriend.h b/clang/include/clang/AST/DeclFriend.h
index 1c2c86d3ef3849..19dd531e89ccf9 100644
--- a/clang/include/clang/AST/DeclFriend.h
+++ b/clang/include/clang/AST/DeclFriend.h
@@ -152,6 +152,19 @@ class FriendDecl final
 
   /// Retrieves the source range for the friend declaration.
   SourceRange getSourceRange() const override LLVM_READONLY {
+    if (TypeSourceInfo *TInfo = getFriendType()) {
+      SourceLocation StartL =
+          (NumTPLists == 0) ? getFriendLoc()
+                            : getTrailingObjects<TemplateParameterList *>()[0]
+                                  ->getTemplateLoc();
+      SourceLocation EndL =
+          isVariadic() ? getEllipsisLoc() : TInfo->getTypeLoc().getEndLoc();
+      return SourceRange(StartL, EndL);
+    }
+
+    if (isVariadic())
+      return SourceRange(getFriendLoc(), getEllipsisLoc());
+
     if (NamedDecl *ND = getFriendDecl()) {
       if (const auto *FD = dyn_cast<FunctionDecl>(ND))
         return FD->getSourceRange();
@@ -165,15 +178,8 @@ class FriendDecl final
       }
       return SourceRange(getFriendLoc(), ND->getEndLoc());
     }
-    else if (TypeSourceInfo *TInfo = getFriendType()) {
-      SourceLocation StartL =
-          (NumTPLists == 0) ? getFriendLoc()
-                            : getTrailingObjects<TemplateParameterList *>()[0]
-                                  ->getTemplateLoc();
-      return SourceRange(StartL, TInfo->getTypeLoc().getEndLoc());
-    }
-    else
-      return SourceRange(getFriendLoc(), getLocation());
+
+    return SourceRange(getFriendLoc(), getLocation());
   }
 
   /// Determines if this friend kind is unsupported.
@@ -191,6 +197,50 @@ class FriendDecl final
   static bool classofKind(Kind K) { return K == Decl::Friend; }
 };
 
+class FriendPackDecl final
+    : public Decl,
+      private llvm::TrailingObjects<FriendPackDecl, FriendDecl *> {
+  FriendDecl *InstantiatedFrom;
+
+  /// The number of friend-declarations created by this pack expansion.
+  unsigned NumExpansions;
+
+  FriendPackDecl(DeclContext *DC, FriendDecl *InstantiatedFrom,
+                 ArrayRef<FriendDecl *> FriendDecls)
+      : Decl(FriendPack, DC,
+             InstantiatedFrom ? InstantiatedFrom->getLocation()
+                              : SourceLocation()),
+        InstantiatedFrom(InstantiatedFrom), NumExpansions(FriendDecls.size()) {
+    std::uninitialized_copy(FriendDecls.begin(), FriendDecls.end(),
+                            getTrailingObjects<FriendDecl *>());
+  }
+
+public:
+  friend class ASTDeclReader;
+  friend class ASTDeclWriter;
+  friend TrailingObjects;
+
+  FriendDecl *getInstantiatedFromFriendDecl() const { return InstantiatedFrom; }
+
+  ArrayRef<FriendDecl *> expansions() const {
+    return llvm::ArrayRef(getTrailingObjects<FriendDecl *>(), NumExpansions);
+  }
+
+  static FriendPackDecl *Create(ASTContext &C, DeclContext *DC,
+                                FriendDecl *InstantiatedFrom,
+                                ArrayRef<FriendDecl *> FriendDecls);
+
+  static FriendPackDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID,
+                                            unsigned NumExpansions);
+
+  SourceRange getSourceRange() const override LLVM_READONLY {
+    return InstantiatedFrom->getSourceRange();
+  }
+
+  static bool classof(const Decl *D) { return classofKind(D->getKind()); }
+  static bool classofKind(Kind K) { return K == FriendPack; }
+};
+
 /// An iterator over the friend declarations of a class.
 class CXXRecordDecl::friend_iterator {
   friend class CXXRecordDecl;
diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h
index dcf5dbf449f8bf..e49fd3f58db572 100644
--- a/clang/include/clang/AST/RecursiveASTVisitor.h
+++ b/clang/include/clang/AST/RecursiveASTVisitor.h
@@ -1606,6 +1606,8 @@ DEF_TRAVERSE_DECL(FriendDecl, {
   }
 })
 
+DEF_TRAVERSE_DECL(FriendPackDecl, {})
+
 DEF_TRAVERSE_DECL(FriendTemplateDecl, {
   if (D->getFriendType())
     TRY_TO(TraverseTypeLoc(D->getFriendType()->getTypeLoc()));
diff --git a/clang/include/clang/Basic/DeclNodes.td b/clang/include/clang/Basic/DeclNodes.td
index 48396e85c5adac..8464cce4a9ddfc 100644
--- a/clang/include/clang/Basic/DeclNodes.td
+++ b/clang/include/clang/Basic/DeclNodes.td
@@ -98,6 +98,7 @@ def FileScopeAsm : DeclNode<Decl>;
 def TopLevelStmt : DeclNode<Decl>, DeclContext;
 def AccessSpec : DeclNode<Decl>;
 def Friend : DeclNode<Decl>;
+def FriendPack : DeclNode<Decl>;
 def FriendTemplate : DeclNode<Decl>;
 def StaticAssert : DeclNode<Decl>;
 def Block : DeclNode<Decl, "blocks">, DeclContext;
diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h
index 5dd0ba33f8a9c2..92e3fce78b6826 100644
--- a/clang/include/clang/Serialization/ASTBitCodes.h
+++ b/clang/include/clang/Serialization/ASTBitCodes.h
@@ -1377,6 +1377,9 @@ enum DeclCode {
   /// A FriendDecl record.
   DECL_FRIEND,
 
+  /// A FriendPackDecl record.
+  DECL_FRIEND_PACK,
+
   /// A FriendTemplateDecl record.
   DECL_FRIEND_TEMPLATE,
 
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index b0043eb8761833..46d07069af6ba4 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -536,6 +536,7 @@ namespace clang {
     ExpectedDecl VisitFieldDecl(FieldDecl *D);
     ExpectedDecl VisitIndirectFieldDecl(IndirectFieldDecl *D);
     ExpectedDecl VisitFriendDecl(FriendDecl *D);
+    ExpectedDecl VisitFriendPackDecl(FriendPackDecl *D);
     ExpectedDecl VisitObjCIvarDecl(ObjCIvarDecl *D);
     ExpectedDecl VisitVarDecl(VarDecl *D);
     ExpectedDecl VisitImplicitParamDecl(ImplicitParamDecl *D);
@@ -4440,6 +4441,31 @@ ExpectedDecl ASTNodeImporter::VisitFriendDecl(FriendDecl *D) {
   return FrD;
 }
 
+ExpectedDecl ASTNodeImporter::VisitFriendPackDecl(FriendPackDecl *D) {
+  // Import the major distinguishing characteristics of a declaration.
+  DeclContext *DC, *LexicalDC;
+  if (Error Err = ImportDeclContext(D, DC, LexicalDC))
+    return std::move(Err);
+
+  auto ToInstantiatedFromFriendOrErr =
+      Importer.Import(D->getInstantiatedFromFriendDecl());
+  if (!ToInstantiatedFromFriendOrErr)
+    return ToInstantiatedFromFriendOrErr.takeError();
+  SmallVector<FriendDecl *, 4> Expansions(D->expansions().size());
+  if (Error Err = ImportArrayChecked(D->expansions(), Expansions.begin()))
+    return std::move(Err);
+
+  FriendPackDecl *ToFriendPack;
+  if (GetImportedOrCreateDecl(ToFriendPack, D, Importer.getToContext(), DC,
+                              cast<FriendDecl>(*ToInstantiatedFromFriendOrErr),
+                              Expansions))
+    return ToFriendPack;
+
+  addDeclToContexts(D, ToFriendPack);
+
+  return ToFriendPack;
+}
+
 ExpectedDecl ASTNodeImporter::VisitObjCIvarDecl(ObjCIvarDecl *D) {
   // Import the major distinguishing characteristics of an ivar.
   DeclContext *DC, *LexicalDC;
diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp
index a1f70546bde426..ee856e73c0d62d 100644
--- a/clang/lib/AST/DeclBase.cpp
+++ b/clang/lib/AST/DeclBase.cpp
@@ -949,6 +949,7 @@ unsigned Decl::getIdentifierNamespaceForKind(Kind DeclKind) {
 
     // Never have names.
     case Friend:
+    case FriendPack:
     case FriendTemplate:
     case AccessSpec:
     case LinkageSpec:
diff --git a/clang/lib/AST/DeclFriend.cpp b/clang/lib/AST/DeclFriend.cpp
index 8b285bfce8d522..2662f3f634da6c 100644
--- a/clang/lib/AST/DeclFriend.cpp
+++ b/clang/lib/AST/DeclFriend.cpp
@@ -69,6 +69,26 @@ FriendDecl *FriendDecl::CreateDeserialized(ASTContext &C, GlobalDeclID ID,
   return new (C, ID, Extra) FriendDecl(EmptyShell(), FriendTypeNumTPLists);
 }
 
+FriendPackDecl *FriendPackDecl::Create(ASTContext &C, DeclContext *DC,
+                                       FriendDecl *InstantiatedFrom,
+                                       ArrayRef<FriendDecl *> FriendDecls) {
+  size_t Extra = additionalSizeToAlloc<FriendDecl *>(FriendDecls.size());
+  return new (C, DC, Extra) FriendPackDecl(DC, InstantiatedFrom, FriendDecls);
+}
+
+FriendPackDecl *FriendPackDecl::CreateDeserialized(ASTContext &C,
+                                                   GlobalDeclID ID,
+                                                   unsigned NumExpansions) {
+  size_t Extra = additionalSizeToAlloc<FriendDecl *>(NumExpansions);
+  auto *Result =
+      new (C, ID, Extra) FriendPackDecl(nullptr, nullptr, std::nullopt);
+  Result->NumExpansions = NumExpansions;
+  auto *Trail = Result->getTrailingObjects<FriendDecl *>();
+  for (unsigned I = 0; I != NumExpansions; ++I)
+    new (Trail + I) FriendDecl *(nullptr);
+  return Result;
+}
+
 FriendDecl *CXXRecordDecl::getFirstFriend() const {
   ExternalASTSource *Source = getParentASTContext().getExternalSource();
   Decl *First = data().FirstFriend.get(Source);
diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp
index 882dbad456379a..4aa8e8f321aade 100644
--- a/clang/lib/CodeGen/CGDecl.cpp
+++ b/clang/lib/CodeGen/CGDecl.cpp
@@ -96,6 +96,7 @@ void CodeGenFunction::EmitDecl(const Decl &D) {
   case Decl::FileScopeAsm:
   case Decl::TopLevelStmt:
   case Decl::Friend:
+  case Decl::FriendPack:
   case Decl::FriendTemplate:
   case Decl::Block:
   case Decl::Captured:
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 879b32a6f4f5eb..66ecee046c3b4c 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -1431,9 +1431,6 @@ Decl *TemplateDeclInstantiator::VisitIndirectFieldDecl(IndirectFieldDecl *D) {
 }
 
 Decl *TemplateDeclInstantiator::VisitFriendDecl(FriendDecl *D) {
-  assert(D->getEllipsisLoc().isInvalid() &&
-         "TODO: Instantiate variadic friend decl");
-
   // Handle friend type expressions by simply substituting template
   // parameters into the pattern type and checking the result.
   if (TypeSourceInfo *Ty = D->getFriendType()) {
@@ -1445,8 +1442,49 @@ Decl *TemplateDeclInstantiator::VisitFriendDecl(FriendDecl *D) {
     if (D->isUnsupportedFriend()) {
       InstTy = Ty;
     } else {
-      InstTy = SemaRef.SubstType(Ty, TemplateArgs,
-                                 D->getLocation(), DeclarationName());
+      if (D->isVariadic()) {
+        SmallVector<UnexpandedParameterPack, 2> Unexpanded;
+        SemaRef.collectUnexpandedParameterPacks(Ty->getTypeLoc(), Unexpanded);
+        assert(!Unexpanded.empty() && "Pack expansion without packs");
+
+        bool ShouldExpand = true;
+        bool RetainExpansion = false;
+        std::optional<unsigned> NumExpansions;
+        if (SemaRef.CheckParameterPacksForExpansion(
+                D->getEllipsisLoc(), D->getSourceRange(), Unexpanded,
+                TemplateArgs, ShouldExpand, RetainExpansion, NumExpansions))
+          return nullptr;
+
+        assert(!RetainExpansion &&
+               "should never retain an expansion for a FriendPackDecl");
+
+        if (ShouldExpand) {
+          SmallVector<FriendDecl *> Decls;
+          for (unsigned I = 0; I != *NumExpansions; I++) {
+            Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(SemaRef, I);
+            TypeSourceInfo *TSI = SemaRef.SubstType(
+                Ty, TemplateArgs, D->getEllipsisLoc(), DeclarationName());
+            if (!TSI)
+              return nullptr;
+
+            auto FD =
+                FriendDecl::Create(SemaRef.Context, Owner, D->getLocation(),
+                                   TSI, D->getFriendLoc());
+
+            FD->setAccess(AS_public);
+            Owner->addDecl(FD);
+            Decls.push_back(FD);
+          }
+
+          auto FPD = FriendPackDecl::Create(SemaRef.Context, Owner, D, Decls);
+          FPD->setAccess(AS_public);
+          Owner->addDecl(FPD);
+          return FPD;
+        }
+      }
+
+      InstTy = SemaRef.SubstType(Ty, TemplateArgs, D->getLocation(),
+                                 DeclarationName());
     }
     if (!InstTy)
       return nullptr;
@@ -1478,6 +1516,12 @@ Decl *TemplateDeclInstantiator::VisitFriendDecl(FriendDecl *D) {
   return FD;
 }
 
+Decl *TemplateDeclInstantiator::VisitFriendPackDecl(FriendPackDecl *D) {
+  // These only get created for fully unexpanded packs, at which point
+  // we should never perform any subsequent instantiation on them.
+  llvm_unreachable("instantiating FriendPackDecl?");
+}
+
 Decl *TemplateDeclInstantiator::VisitStaticAssertDecl(StaticAssertDecl *D) {
   Expr *AssertExpr = D->getAssertExpr();
 
diff --git a/clang/lib/Serialization/ASTCommon.cpp b/clang/lib/Serialization/ASTCommon.cpp
index 444a8a3d3a5143..47ceb03b4460b4 100644
--- a/clang/lib/Serialization/ASTCommon.cpp
+++ b/clang/lib/Serialization/ASTCommon.cpp
@@ -428,6 +428,7 @@ bool serialization::isRedeclarableDeclKind(unsigned Kind) {
   case Decl::TopLevelStmt:
   case Decl::AccessSpec:
   case Decl::Friend:
+  case Decl::FriendPack:
   case Decl::FriendTemplate:
   case Decl::StaticAssert:
   case Decl::Block:
diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp
index 5791cef8f08889..c23cb2a6b48efd 100644
--- a/clang/lib/Serialization/ASTReaderDecl.cpp
+++ b/clang/lib/Serialization/ASTReaderDecl.cpp
@@ -412,6 +412,7 @@ namespace clang {
     void VisitImportDecl(ImportDecl *D);
     void VisitAccessSpecDecl(AccessSpecDecl *D);
     void VisitFriendDecl(FriendDecl *D);
+    void VisitFriendPackDecl(FriendPackDecl *D);
     void VisitFriendTemplateDecl(FriendTemplateDecl *D);
     void VisitStaticAssertDecl(StaticAssertDecl *D);
     void VisitBlockDecl(BlockDecl *BD);
@@ -2357,6 +2358,13 @@ void ASTDeclReader::VisitFriendDecl(FriendDecl *D) {
   D->EllipsisLoc = readSourceLocation();
 }
 
+void ASTDeclReader::VisitFriendPackDecl(FriendPackDecl *D) {
+  D->InstantiatedFrom = readDeclAs<FriendDecl>();
+  auto **Expansions = D->getTrailingObjects<FriendDecl *>();
+  for (unsigned I = 0; I != D->NumExpansions; ++I)
+    Expansions[I] = readDeclAs<FriendDecl>();
+}
+
 void ASTDeclReader::VisitFriendTemplateDecl(FriendTemplateDecl *D) {
   VisitDecl(D);
   unsigned NumParams = Record.readInt();
@@ -3903,6 +3911,9 @@ Decl *ASTReader::ReadDeclRecord(GlobalDeclID ID) {
   case DECL_FRIEND:
     D = FriendDecl::CreateDeserialized(Context, ID, Record.readInt());
     break;
+  case DECL_FRIEND_PACK:
+    D = FriendPackDecl::CreateDeserialized(Context, ID, Record.readInt());
+    break;
   case DECL_FRIEND_TEMPLATE:
     D = FriendTemplateDecl::CreateDeserialized(Context, ID);
     break;
diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp
index 8862ced487d13e..56cb2c63b1c651 100644
--- a/clang/lib/Serialization/ASTWriterDecl.cpp
+++ b/clang/lib/Serialization/ASTWriterDecl.cpp
@@ -130,6 +130,7 @@ namespace clang {
     void VisitImportDecl(ImportDecl *D);
     void VisitAccessSpecDecl(AccessSpecDecl *D);
     void VisitFriendDecl(FriendDecl *D);
+    void VisitFriendPackDecl(FriendPackDecl *D);
     void VisitFriendTemplateDecl(FriendTemplateDecl *D);
     void VisitStaticAssertDecl(StaticAssertDecl *D);
     void VisitBlockDecl(BlockDecl *D);
@@ -1658,6 +1659,14 @@ void ASTDeclWriter::VisitFriendDecl(FriendDecl *D) {
   Code = serialization::DECL_FRIEND;
 }
 
+void ASTDeclWriter::VisitFriendPackDecl(FriendPackDecl *D) {
+  Record.push_back(D->NumExpansions);
+  Record.AddDeclRef(D->getInstantiatedFromFriendDecl());
+  for (auto *E : D->expansions())
+    Record.AddDeclRef(E);
+  Code = serialization::DECL_FRIEND_PACK;
+}
+
 void ASTDeclWriter::VisitFriendTemplateDecl(FriendTemplateDecl *D) {
   VisitDecl(D);
   Record.push_back(D->getNumTemplateParameters());
diff --git a/clang/test/Parser/cxx2c-variadic-friends-errors.cpp b/clang/test/Parser/cxx2c-variadic-friends-errors.cpp
index dfbe9ad1f6f68b..3373cf89e72fb5 100644
--- a/clang/test/Parser/cxx2c-variadic-friends-errors.cpp
+++ b/clang/test/Parser/cxx2c-variadic-friends-errors.cpp
@@ -60,4 +60,7 @@ template<class... Ts> // expected-note {{template parameter is declared here}}
 struct S2 {
   friend class Ts...; // expected-error {{declaration of 'Ts' shadows template parameter}}
                       // expected-error at -1 {{pack expansion does not contain any unexpanded parameter packs}}
-};
\ No newline at end of file
+
+  // TODO: Fix-it hint to insert '...'.
+  friend Ts; // expected-error {{friend declaration contains unexpanded parameter pack}}
+};
diff --git a/clang/test/SemaCXX/cxx2c-variadic-friends.cpp b/clang/test/SemaCXX/cxx2c-variadic-friends.cpp
new file mode 100644
index 00000000000000..f1213ce3e7e816
--- /dev/null
+++ b/clang/test/SemaCXX/cxx2c-variadic-friends.cpp
@@ -0,0 +1,136 @@
+// %clang_cc1 -fsyntax-only -verify -std=c++2c %s
+
+struct A;
+struct B;
+struct C;
+
+struct S {};
+template <typename> struct TS {};
+
+template <typename ...Pack>
+class X {
+  friend Pack...;
+  static void f() { } // expected-note {{declared private here}}
+};
+
+class Y {
+  friend A, B, C;
+  static void g() { } // expected-note {{declared private here}}
+};
+
+struct A {
+  A() {
+    X<A>::f();
+    Y::g();
+  };
+};
+
+struct B {
+  B() {
+    X<B, C>::f();
+    Y::g();
+  };
+};
+
+struct C {
+  C() {
+    X<A, B, C>::f();
+    Y::g();
+  };
+};
+
+struct D {
+  D() {
+    X<A, B, C>::f(); // expected-error {{'f' is a private member of 'X<A, B, C>'}}
+    Y::g(); // expected-error {{'g' is a private member of 'Y'}}
+  };
+};
+
+void f1() {
+  A a;
+  B b;
+  C c;
+  D d;
+}
+
+template <typename ...Pack>
+struct Z {
+  template <template <typename> class Template>
+  struct Inner {
+    friend Template<Pack>...;
+  };
+};
+
+void f2() {
+  Z<int, long, char> z;
+  Z<int, long, char>::Inner<TS> inner;
+}
+
+namespace p2893r3_examples {
+template<class... Ts>
+class Passkey {
+  friend Ts...;
+  Passkey() {} // expected-note {{declared private here}}
+};
+
+class Foo;
+class Bar;
+class Baz;
+
+class C {
+public:
+  void f(Passkey<Foo, Bar, Baz>);
+};
+
+class Foo {
+  Foo() { C c; c.f({}); }
+};
+
+class Bar {
+  Bar() { C c; c.f({}); }
+};
+
+class Baz {
+  Baz() { C c; c.f({}); }
+};
+
+class Quux {
+  Quux() { C c; c.f({}); } // expected-error {{calling a private constructor of class 'p2893r3_examples::Passkey<p2893r3_examples::Foo, p2893r3_examples::Bar, p2893r3_examples::Baz>'}}
+};
+
+template<class Derived, class MsgT>
+struct Receiver {
+  void receive(MsgT) {
+    static_cast<Derived*>(this)->private_ += 1;
+  }
+};
+
+template<class... MsgTs>
+struct Dispatcher : Receiver<Dispatcher<MsgTs...>, MsgTs>... {
+  using Receiver<Dispatcher, MsgTs>::receive...;
+  friend Receiver<Dispatcher, MsgTs>...;
+
+private:
+  int private_;
+};
+
+void f() {
+  Dispatcher<int, float> d;
+  d.receive(0);
+  d.receive(0.0f);
+}
+} // namespace p2893r3_examples
+
+namespace p2893r3_note {
+template <class... Ts> class R {
+  friend Ts...;
+};
+
+template <class... Ts, class... Us>
+class R<R<Ts...>, R<Us...>> {
+  friend Ts::Nested..., Us...;
+};
+
+struct E { struct Nested; };
+R<R<E>, R<C, int>> rr;
+} // namespace p2893r3_note
diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp
index 937d7ff09e4eef..cda7f6e90a1516 100644
--- a/clang/tools/libclang/CIndex.cpp
+++ b/clang/tools/libclang/CIndex.cpp
@@ -7032,6 +7032,7 @@ CXCursor clang_getCursorDefinition(CXCursor C) {
   case Decl::PragmaComment:
   case Decl::PragmaDetectMismatch:
   case Decl::UsingPack:
+  case Decl::FriendPack:
   case Decl::Concept:
   case Decl::ImplicitConceptSpecialization:
   case Decl::LifetimeExtendedTemporary:

>From 5f5eb7e33407295f0269d23c4aa30a3bc339fb00 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Thu, 1 Aug 2024 00:19:54 +0200
Subject: [PATCH 03/20] [ASTPrinter] Group friend decls

---
 clang/lib/AST/DeclPrinter.cpp                 | 37 ++++++++++++++++---
 clang/test/Parser/cxx2c-variadic-friends.cpp  | 36 ++++++++++++------
 clang/test/SemaCXX/cxx2c-variadic-friends.cpp |  2 +-
 3 files changed, 58 insertions(+), 17 deletions(-)

diff --git a/clang/lib/AST/DeclPrinter.cpp b/clang/lib/AST/DeclPrinter.cpp
index 1a8ac103841ec3..0b4faaf0b39efa 100644
--- a/clang/lib/AST/DeclPrinter.cpp
+++ b/clang/lib/AST/DeclPrinter.cpp
@@ -67,7 +67,7 @@ namespace {
     void VisitEnumConstantDecl(EnumConstantDecl *D);
     void VisitEmptyDecl(EmptyDecl *D);
     void VisitFunctionDecl(FunctionDecl *D);
-    void VisitFriendDecl(FriendDecl *D);
+    void VisitFriendDecl(FriendDecl *D, bool FirstInGroup = true);
     void VisitFieldDecl(FieldDecl *D);
     void VisitVarDecl(VarDecl *D);
     void VisitLabelDecl(LabelDecl *D);
@@ -487,7 +487,26 @@ void DeclPrinter::VisitDeclContext(DeclContext *DC, bool Indent) {
     }
 
     this->Indent();
-    Visit(*D);
+
+    // Group friend declarations if need be.
+    if (isa<FriendDecl>(*D)) {
+      auto *FD = cast<FriendDecl>(*D);
+      VisitFriendDecl(FD);
+      SourceLocation FriendLoc = FD->getFriendLoc();
+
+      // Use a separate iterator; 'D' is always one declaration 'behind' in
+      // this loop; the last friend printed here (or the first printed just
+      // now before this loop if there are no subsequent friends) will be
+      // skipped by the '++D' of the outer loop.
+      for (DeclContext::decl_iterator It; It = std::next(D), It != DEnd; ++D) {
+        auto NextFriend = dyn_cast<FriendDecl>(*It);
+        if (!NextFriend || NextFriend->getFriendLoc() != FriendLoc)
+          break;
+        VisitFriendDecl(NextFriend, false);
+      }
+    } else {
+      Visit(*D);
+    }
 
     // FIXME: Need to be able to tell the DeclPrinter when
     const char *Terminator = nullptr;
@@ -862,13 +881,21 @@ void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) {
   }
 }
 
-void DeclPrinter::VisitFriendDecl(FriendDecl *D) {
+void DeclPrinter::VisitFriendDecl(FriendDecl *D, bool FirstInGroup) {
   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);
+
+    // Hack to print friend declarations declared as a group, e.g.
+    // 'friend int, long;', instead of printing them as two separate
+    // FriendDecls, which they are in the AST.
+    if (FirstInGroup)
+      Out << "friend ";
+    else
+      Out << ", ";
+
+    Out << TSI->getType().getAsString(Policy);
   }
   else if (FunctionDecl *FD =
       dyn_cast<FunctionDecl>(D->getFriendDecl())) {
diff --git a/clang/test/Parser/cxx2c-variadic-friends.cpp b/clang/test/Parser/cxx2c-variadic-friends.cpp
index 85f696ebe28375..ab788085bbe198 100644
--- a/clang/test/Parser/cxx2c-variadic-friends.cpp
+++ b/clang/test/Parser/cxx2c-variadic-friends.cpp
@@ -1,33 +1,39 @@
 // RUN: %clang_cc1 -fsyntax-only -verify -std=c++2c %s
 // RUN: %clang_cc1 -fsyntax-only -ast-dump -std=c++2c %s | FileCheck %s
+// RUN: %clang_cc1 -ast-print -std=c++2c %s | FileCheck %s --check-prefix=PRINT
 // expected-no-diagnostics
 
 struct S;
 template <typename> struct TS; // #template
 
 // CHECK-LABEL: CXXRecordDecl {{.*}} struct Friends
+// PRINT-LABEL: struct Friends {
 struct Friends {
   // CHECK: FriendDecl {{.*}} 'int'
   // CHECK-NEXT: FriendDecl {{.*}} 'long'
+  // PRINT-NEXT: friend int, long;
   friend int, long;
 
   // CHECK-NEXT: FriendDecl {{.*}} 'int'
   // CHECK-NEXT: FriendDecl {{.*}} 'long'
   // CHECK-NEXT: FriendDecl {{.*}} 'char'
+  // PRINT-NEXT: friend int, long, char;
   friend int, long, char;
 
   // CHECK-NEXT: FriendDecl {{.*}} 'S'
+  // PRINT-NEXT: friend S;
   friend S;
 
   // CHECK-NEXT: FriendDecl {{.*}} 'S'
   // CHECK-NEXT: FriendDecl {{.*}} 'S'
   // CHECK-NEXT: FriendDecl {{.*}} 'S'
+  // PRINT-NEXT: friend S, S, S;
   friend S, S, S;
 
   // CHECK-NEXT: FriendDecl
   // CHECK-NEXT: ClassTemplateDecl {{.*}} friend TS
-  template <typename>
-  friend struct TS;
+  // PRINT-NEXT: friend template <typename> struct TS;
+  template <typename> friend struct TS;
 };
 
 namespace specialisations {
@@ -41,20 +47,28 @@ struct N {
 };
 
 // CHECK-LABEL: ClassTemplateDecl {{.*}} Variadic
-// CHECK: FriendDecl {{.*}} 'Pack' variadic
-// CHECK-NEXT: FriendDecl {{.*}} 'TS<Pack>' variadic
-template <typename ...Pack>
-struct Variadic {
-  friend Pack...;
+// PRINT-LABEL: template <typename ...Pack> struct Variadic {
+template <typename ...Pack> struct Variadic {
+  // CHECK: FriendDecl {{.*}} 'Pack' variadic
+  // CHECK-NEXT: FriendDecl {{.*}} 'long'
+  // CHECK-NEXT: FriendDecl {{.*}} 'Pack' variadic
+  // PRINT-NEXT: friend Pack..., long, Pack...;
+  friend Pack..., long, Pack...;
+
+  // CHECK-NEXT: FriendDecl {{.*}} 'TS<Pack>' variadic
+  // PRINT-NEXT: friend TS<Pack>...;
   friend TS<Pack>...;
 };
 
 // CHECK-LABEL: ClassTemplateDecl {{.*}} S2
-// CHECK: FriendDecl {{.*}} 'class C<Ts>':'C<Ts>' variadic
-// CHECK-NEXT: FriendDecl {{.*}} 'class N::C<Ts>':'C<Ts>' variadic
-template<class... Ts>
-struct S2 {
+// PRINT-LABEL: template <class ...Ts> struct S2 {
+template<class ...Ts> struct S2 {
+  // CHECK: FriendDecl {{.*}} 'class C<Ts>':'C<Ts>' variadic
+  // PRINT-NEXT: friend class C<Ts>...;
   friend class C<Ts>...;
+
+  // CHECK-NEXT: FriendDecl {{.*}} 'class N::C<Ts>':'C<Ts>' variadic
+  // PRINT-NEXT: friend class N::C<Ts>...
   friend class N::C<Ts>...;
 };
 }
diff --git a/clang/test/SemaCXX/cxx2c-variadic-friends.cpp b/clang/test/SemaCXX/cxx2c-variadic-friends.cpp
index f1213ce3e7e816..f6a249c4b99b29 100644
--- a/clang/test/SemaCXX/cxx2c-variadic-friends.cpp
+++ b/clang/test/SemaCXX/cxx2c-variadic-friends.cpp
@@ -1,4 +1,4 @@
-// %clang_cc1 -fsyntax-only -verify -std=c++2c %s
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++2c %s
 
 struct A;
 struct B;

>From c603a5d763b05cc10426f1c7b6d2bdf6aeaacd77 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Thu, 1 Aug 2024 04:58:06 +0200
Subject: [PATCH 04/20] [Tests] Move some tests

---
 clang/lib/Sema/SemaDeclCXX.cpp                |  1 +
 clang/test/AST/cxx2c-variadic-friends.cpp     | 74 ++++++++++++++
 .../Parser/cxx2c-variadic-friends-errors.cpp  | 66 -------------
 clang/test/Parser/cxx2c-variadic-friends.cpp  | 96 ++++++++-----------
 4 files changed, 113 insertions(+), 124 deletions(-)
 create mode 100644 clang/test/AST/cxx2c-variadic-friends.cpp
 delete mode 100644 clang/test/Parser/cxx2c-variadic-friends-errors.cpp

diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index f19a7f4c636a08..7e66c66d1cd827 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -17568,6 +17568,7 @@ Decl *Sema::ActOnFriendTypeDecl(Scope *S, const DeclSpec &DS,
 
   Decl *D;
   if (!TempParams.empty())
+    // TODO: Support variadic friend template decls?
     D = FriendTemplateDecl::Create(Context, CurContext, Loc, TempParams, TSI,
                                    FriendLoc);
   else
diff --git a/clang/test/AST/cxx2c-variadic-friends.cpp b/clang/test/AST/cxx2c-variadic-friends.cpp
new file mode 100644
index 00000000000000..2d5069196ab39b
--- /dev/null
+++ b/clang/test/AST/cxx2c-variadic-friends.cpp
@@ -0,0 +1,74 @@
+// RUN: %clang_cc1 -fsyntax-only -ast-dump -std=c++2c %s | FileCheck %s
+// RUN: %clang_cc1 -ast-print -std=c++2c %s | FileCheck %s --check-prefix=PRINT
+// RUN: %clang_cc1 -emit-pch -std=c++2c -o %t %s
+// RUN: %clang_cc1 -x c++ -std=c++2c -include-pch %t -ast-dump-all /dev/null
+
+struct S;
+template <typename> struct TS; // #template
+
+// CHECK-LABEL: CXXRecordDecl {{.*}} struct Friends
+// PRINT-LABEL: struct Friends {
+struct Friends {
+  // CHECK: FriendDecl {{.*}} 'int'
+  // CHECK-NEXT: FriendDecl {{.*}} 'long'
+  // PRINT-NEXT: friend int, long;
+  friend int, long;
+
+  // CHECK-NEXT: FriendDecl {{.*}} 'int'
+  // CHECK-NEXT: FriendDecl {{.*}} 'long'
+  // CHECK-NEXT: FriendDecl {{.*}} 'char'
+  // PRINT-NEXT: friend int, long, char;
+  friend int, long, char;
+
+  // CHECK-NEXT: FriendDecl {{.*}} 'S'
+  // PRINT-NEXT: friend S;
+  friend S;
+
+  // CHECK-NEXT: FriendDecl {{.*}} 'S'
+  // CHECK-NEXT: FriendDecl {{.*}} 'S'
+  // CHECK-NEXT: FriendDecl {{.*}} 'S'
+  // PRINT-NEXT: friend S, S, S;
+  friend S, S, S;
+
+  // CHECK-NEXT: FriendDecl
+  // CHECK-NEXT: ClassTemplateDecl {{.*}} friend TS
+  // PRINT-NEXT: friend template <typename> struct TS;
+  template <typename> friend struct TS;
+};
+
+namespace specialisations {
+template<class T>
+struct C {
+  template<class U> struct Nested;
+};
+
+struct N {
+  template<class U> class C;
+};
+
+// CHECK-LABEL: ClassTemplateDecl {{.*}} Variadic
+// PRINT-LABEL: template <typename ...Pack> struct Variadic {
+template <typename ...Pack> struct Variadic {
+  // CHECK: FriendDecl {{.*}} 'Pack' variadic
+  // CHECK-NEXT: FriendDecl {{.*}} 'long'
+  // CHECK-NEXT: FriendDecl {{.*}} 'Pack' variadic
+  // PRINT-NEXT: friend Pack..., long, Pack...;
+  friend Pack..., long, Pack...;
+
+  // CHECK-NEXT: FriendDecl {{.*}} 'TS<Pack>' variadic
+  // PRINT-NEXT: friend TS<Pack>...;
+  friend TS<Pack>...;
+};
+
+// CHECK-LABEL: ClassTemplateDecl {{.*}} S2
+// PRINT-LABEL: template <class ...Ts> struct S2 {
+template<class ...Ts> struct S2 {
+  // CHECK: FriendDecl {{.*}} 'class C<Ts>':'C<Ts>' variadic
+  // PRINT-NEXT: friend class C<Ts>...;
+  friend class C<Ts>...;
+
+  // CHECK-NEXT: FriendDecl {{.*}} 'class N::C<Ts>':'C<Ts>' variadic
+  // PRINT-NEXT: friend class N::C<Ts>...
+  friend class N::C<Ts>...;
+};
+}
diff --git a/clang/test/Parser/cxx2c-variadic-friends-errors.cpp b/clang/test/Parser/cxx2c-variadic-friends-errors.cpp
deleted file mode 100644
index 3373cf89e72fb5..00000000000000
--- a/clang/test/Parser/cxx2c-variadic-friends-errors.cpp
+++ /dev/null
@@ -1,66 +0,0 @@
-// RUN: %clang_cc1 -fsyntax-only -verify -std=c++2c %s
-
-template <typename> struct TS; // #template
-
-// CHECK-LABEL: CXXRecordDecl {{.*}} struct Errors
-struct Errors {
-  // We simply ignore the '...' here.
-  // CHECK: FriendDecl {{.*}} 'float'
-  friend float...; // expected-error {{pack expansion does not contain any unexpanded parameter packs}}
-
-  // CHECK-NEXT: FriendDecl {{.*}} 'short'
-  // CHECK-NEXT: FriendDecl {{.*}} 'unsigned int'
-  // CHECK-NEXT: FriendDecl {{.*}} 'unsigned short'
-  friend short..., unsigned, unsigned short...; // expected-error 2 {{pack expansion does not contain any unexpanded parameter packs}}
-
-  // FIXME: This is a pretty bad diagnostic.
-  template <typename>
-  friend struct TS, int; // expected-error {{cannot be referenced with the 'struct' specifier}}
-                         // expected-note@#template {{declared here}}
-
-  double friend; // expected-error {{'friend' must appear first in a non-function declaration}}
-  double friend, double; // expected-error {{expected member name or ';' after declaration specifiers}}
-};
-
-struct C { template<class T> class Nested; }; // expected-note 2 {{'C::Nested' declared here}}
-struct S {
-  template<class T>
-  friend class C::Nested;
-};
-
-template<class... Ts>
-struct VS {
-  template<class... Us>
-  friend Us...; // expected-error {{friend type templates must use an elaborated type}}
-
-  template<class... Us> // expected-note {{is declared here}}
-  friend class Us...; // expected-error {{declaration of 'Us' shadows template parameter}}
-                      // expected-error at -1 {{pack expansion does not contain any unexpanded parameter packs}}
-
-  template<class U>
-  friend class C<Ts>::Nested<U>...; // expected-error {{explicit specialization of non-template class 'C'}}
-                                    // expected-error at -1 {{no template named 'Nested' in the global namespace}}
-                                    // expected-error at -2 {{friends can only be classes or functions}}
-                                    // expected-error at -3 {{expected ';' at end of declaration list}}
-
-  template<class... Us>
-  friend class C<Ts...>::Nested<Us>...; // expected-error {{explicit specialization of non-template class 'C'}}
-                                        // expected-error at -1 {{no template named 'Nested' in the global namespace}}
-                                        // expected-error at -2 {{friends can only be classes or functions}}
-                                        // expected-error at -3 {{expected ';' at end of declaration list}}
-
-  template<class... Us>
-  friend class C<Us>::Nested...; // expected-error {{explicit specialization of non-template class 'C'}}
-                                 // expected-error at -1 {{friends can only be classes or functions}}
-                                 // expected-error at -2 {{expected ';' at end of declaration list}}
-};
-
-
-template<class... Ts> // expected-note {{template parameter is declared here}}
-struct S2 {
-  friend class Ts...; // expected-error {{declaration of 'Ts' shadows template parameter}}
-                      // expected-error at -1 {{pack expansion does not contain any unexpanded parameter packs}}
-
-  // TODO: Fix-it hint to insert '...'.
-  friend Ts; // expected-error {{friend declaration contains unexpanded parameter pack}}
-};
diff --git a/clang/test/Parser/cxx2c-variadic-friends.cpp b/clang/test/Parser/cxx2c-variadic-friends.cpp
index ab788085bbe198..9a9b82ab047e67 100644
--- a/clang/test/Parser/cxx2c-variadic-friends.cpp
+++ b/clang/test/Parser/cxx2c-variadic-friends.cpp
@@ -1,74 +1,54 @@
 // RUN: %clang_cc1 -fsyntax-only -verify -std=c++2c %s
-// RUN: %clang_cc1 -fsyntax-only -ast-dump -std=c++2c %s | FileCheck %s
-// RUN: %clang_cc1 -ast-print -std=c++2c %s | FileCheck %s --check-prefix=PRINT
-// expected-no-diagnostics
 
-struct S;
 template <typename> struct TS; // #template
 
-// CHECK-LABEL: CXXRecordDecl {{.*}} struct Friends
-// PRINT-LABEL: struct Friends {
-struct Friends {
-  // CHECK: FriendDecl {{.*}} 'int'
-  // CHECK-NEXT: FriendDecl {{.*}} 'long'
-  // PRINT-NEXT: friend int, long;
-  friend int, long;
-
-  // CHECK-NEXT: FriendDecl {{.*}} 'int'
-  // CHECK-NEXT: FriendDecl {{.*}} 'long'
-  // CHECK-NEXT: FriendDecl {{.*}} 'char'
-  // PRINT-NEXT: friend int, long, char;
+struct Errors {
+  friend int, int;
   friend int, long, char;
 
-  // CHECK-NEXT: FriendDecl {{.*}} 'S'
-  // PRINT-NEXT: friend S;
-  friend S;
+  // We simply ignore the '...' here.
+  friend float...; // expected-error {{pack expansion does not contain any unexpanded parameter packs}}
 
-  // CHECK-NEXT: FriendDecl {{.*}} 'S'
-  // CHECK-NEXT: FriendDecl {{.*}} 'S'
-  // CHECK-NEXT: FriendDecl {{.*}} 'S'
-  // PRINT-NEXT: friend S, S, S;
-  friend S, S, S;
+  friend short..., unsigned, unsigned short...; // expected-error 2 {{pack expansion does not contain any unexpanded parameter packs}}
 
-  // CHECK-NEXT: FriendDecl
-  // CHECK-NEXT: ClassTemplateDecl {{.*}} friend TS
-  // PRINT-NEXT: friend template <typename> struct TS;
-  template <typename> friend struct TS;
-};
+  // FIXME: This is a pretty bad diagnostic.
+  template <typename>
+  friend struct TS, int; // expected-error {{cannot be referenced with the 'struct' specifier}}
+                         // expected-note@#template {{declared here}}
 
-namespace specialisations {
-template<class T>
-struct C {
-  template<class U> struct Nested;
+  double friend; // expected-error {{'friend' must appear first in a non-function declaration}}
+  double friend, double; // expected-error {{expected member name or ';' after declaration specifiers}}
 };
 
-struct N {
-  template<class U> class C;
-};
+template <typename>
+struct C { template<class T> class Nested; };
 
-// CHECK-LABEL: ClassTemplateDecl {{.*}} Variadic
-// PRINT-LABEL: template <typename ...Pack> struct Variadic {
-template <typename ...Pack> struct Variadic {
-  // CHECK: FriendDecl {{.*}} 'Pack' variadic
-  // CHECK-NEXT: FriendDecl {{.*}} 'long'
-  // CHECK-NEXT: FriendDecl {{.*}} 'Pack' variadic
-  // PRINT-NEXT: friend Pack..., long, Pack...;
-  friend Pack..., long, Pack...;
+template<class... Ts> // expected-note {{template parameter is declared here}}
+struct VS {
+  friend Ts...;
 
-  // CHECK-NEXT: FriendDecl {{.*}} 'TS<Pack>' variadic
-  // PRINT-NEXT: friend TS<Pack>...;
-  friend TS<Pack>...;
-};
+  friend class Ts...; // expected-error {{declaration of 'Ts' shadows template parameter}}
+  // expected-error at -1 {{pack expansion does not contain any unexpanded parameter packs}}
+
+  // TODO: Fix-it hint to insert '...'.
+  friend Ts; // expected-error {{friend declaration contains unexpanded parameter pack}}
+
+  template<class... Us>
+  friend Us...; // expected-error {{friend type templates must use an elaborated type}}
+
+  template<class... Us> // expected-note {{is declared here}}
+  friend class Us...; // expected-error {{declaration of 'Us' shadows template parameter}}
+                      // expected-error at -1 {{pack expansion does not contain any unexpanded parameter packs}}
+
+  // FIXME: Ill-formed.
+  template<class U>
+  friend class C<Ts>::template Nested<U>...;
 
-// CHECK-LABEL: ClassTemplateDecl {{.*}} S2
-// PRINT-LABEL: template <class ...Ts> struct S2 {
-template<class ...Ts> struct S2 {
-  // CHECK: FriendDecl {{.*}} 'class C<Ts>':'C<Ts>' variadic
-  // PRINT-NEXT: friend class C<Ts>...;
-  friend class C<Ts>...;
+  // FIXME: Ill-formed.
+  template<class... Us>
+  friend class C<Ts...>::template Nested<Us>...;
 
-  // CHECK-NEXT: FriendDecl {{.*}} 'class N::C<Ts>':'C<Ts>' variadic
-  // PRINT-NEXT: friend class N::C<Ts>...
-  friend class N::C<Ts>...;
+  // FIXME: Ill-formed.
+  template<class... Us>
+  friend class C<Us>::Nested...;
 };
-}

>From da65e3b79893f64848caca47823ff2ed894ac97d Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Thu, 1 Aug 2024 05:03:31 +0200
Subject: [PATCH 05/20] Feature-test macro + release notes

---
 clang/docs/ReleaseNotes.rst                  | 2 ++
 clang/lib/Frontend/InitPreprocessor.cpp      | 5 +++++
 clang/test/Parser/cxx2c-variadic-friends.cpp | 6 ++++++
 clang/www/cxx_status.html                    | 2 +-
 4 files changed, 14 insertions(+), 1 deletion(-)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 3c2e0282d1c72d..aa8bb903d36466 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -89,6 +89,8 @@ C++2c Feature Support
 - Add ``__builtin_is_virtual_base_of`` intrinsic, which supports
   `P2985R0 A type trait for detecting virtual base classes <https://wg21.link/p2985r0>`_
 
+- Implemented `P2893R3 Variadic Friends <https://wg21.link/P2893>`_
+
 Resolutions to C++ Defect Reports
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
diff --git a/clang/lib/Frontend/InitPreprocessor.cpp b/clang/lib/Frontend/InitPreprocessor.cpp
index 17b9ca7cb9910b..d17e213ea4d3f3 100644
--- a/clang/lib/Frontend/InitPreprocessor.cpp
+++ b/clang/lib/Frontend/InitPreprocessor.cpp
@@ -754,6 +754,10 @@ static void InitializeCPlusPlusFeatureTestMacros(const LangOptions &LangOpts,
     Builder.defineMacro("__cpp_multidimensional_subscript", "202211L");
     Builder.defineMacro("__cpp_auto_cast", "202110L");
   }
+  // C++26 features.
+  if (LangOpts.CPlusPlus26) {
+    Builder.defineMacro("__cpp_variadic_friend", "202403L");
+  }
 
   // We provide those C++23 features as extensions in earlier language modes, so
   // we also define their feature test macros.
@@ -765,6 +769,7 @@ static void InitializeCPlusPlusFeatureTestMacros(const LangOptions &LangOpts,
   // C++26 features supported in earlier language modes.
   Builder.defineMacro("__cpp_deleted_function", "202403L");
 
+
   if (LangOpts.Char8)
     Builder.defineMacro("__cpp_char8_t", "202207L");
   Builder.defineMacro("__cpp_impl_destroying_delete", "201806L");
diff --git a/clang/test/Parser/cxx2c-variadic-friends.cpp b/clang/test/Parser/cxx2c-variadic-friends.cpp
index 9a9b82ab047e67..ac99e40fc6b8ce 100644
--- a/clang/test/Parser/cxx2c-variadic-friends.cpp
+++ b/clang/test/Parser/cxx2c-variadic-friends.cpp
@@ -1,5 +1,11 @@
 // RUN: %clang_cc1 -fsyntax-only -verify -std=c++2c %s
 
+#ifndef __cpp_variadic_friend
+#  error No variadic friends?
+#endif
+
+static_assert(__cpp_variadic_friend == 202403L);
+
 template <typename> struct TS; // #template
 
 struct Errors {
diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html
index a6ded8be3ae9e5..faee8b578b6242 100755
--- a/clang/www/cxx_status.html
+++ b/clang/www/cxx_status.html
@@ -202,7 +202,7 @@ <h2 id="cxx26">C++2c implementation status</h2>
  <tr>
   <td>Variadic friends</td>
   <td><a href="https://wg21.link/P2893R3">P2893R3</a></td>
-  <td class="none" align="center">No</td>
+  <td class="unreleased" align="center">Clang 20</td>
  </tr>
  <!-- Summer 2024 papers (St Louis) -->
  <tr>

>From 070e5bcfab0aa400be432305e87a51776cf92e89 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Thu, 1 Aug 2024 05:35:52 +0200
Subject: [PATCH 06/20] clang-format

---
 clang/lib/Frontend/InitPreprocessor.cpp | 1 -
 1 file changed, 1 deletion(-)

diff --git a/clang/lib/Frontend/InitPreprocessor.cpp b/clang/lib/Frontend/InitPreprocessor.cpp
index d17e213ea4d3f3..bb5822e36cbfa8 100644
--- a/clang/lib/Frontend/InitPreprocessor.cpp
+++ b/clang/lib/Frontend/InitPreprocessor.cpp
@@ -769,7 +769,6 @@ static void InitializeCPlusPlusFeatureTestMacros(const LangOptions &LangOpts,
   // C++26 features supported in earlier language modes.
   Builder.defineMacro("__cpp_deleted_function", "202403L");
 
-
   if (LangOpts.Char8)
     Builder.defineMacro("__cpp_char8_t", "202207L");
   Builder.defineMacro("__cpp_impl_destroying_delete", "201806L");

>From 70e76216a2cbb25f179dccf3b04b6d3076281a1d Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Mon, 5 Aug 2024 12:22:24 +0200
Subject: [PATCH 07/20] [NFC] auto -> auto*

---
 clang/lib/AST/DeclPrinter.cpp                  | 2 +-
 clang/lib/Sema/SemaTemplateInstantiateDecl.cpp | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/clang/lib/AST/DeclPrinter.cpp b/clang/lib/AST/DeclPrinter.cpp
index 0b4faaf0b39efa..d769a1f1458cc2 100644
--- a/clang/lib/AST/DeclPrinter.cpp
+++ b/clang/lib/AST/DeclPrinter.cpp
@@ -499,7 +499,7 @@ void DeclPrinter::VisitDeclContext(DeclContext *DC, bool Indent) {
       // now before this loop if there are no subsequent friends) will be
       // skipped by the '++D' of the outer loop.
       for (DeclContext::decl_iterator It; It = std::next(D), It != DEnd; ++D) {
-        auto NextFriend = dyn_cast<FriendDecl>(*It);
+        auto *NextFriend = dyn_cast<FriendDecl>(*It);
         if (!NextFriend || NextFriend->getFriendLoc() != FriendLoc)
           break;
         VisitFriendDecl(NextFriend, false);
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 66ecee046c3b4c..0b3b1e49a295be 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -1476,7 +1476,7 @@ Decl *TemplateDeclInstantiator::VisitFriendDecl(FriendDecl *D) {
             Decls.push_back(FD);
           }
 
-          auto FPD = FriendPackDecl::Create(SemaRef.Context, Owner, D, Decls);
+          auto *FPD = FriendPackDecl::Create(SemaRef.Context, Owner, D, Decls);
           FPD->setAccess(AS_public);
           Owner->addDecl(FPD);
           return FPD;

>From a520f3363c0d4624c187b342cd14d16dcf73d482 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Mon, 5 Aug 2024 13:39:31 +0200
Subject: [PATCH 08/20] [Clang] Add feature test macro and compat warnings

---
 clang/docs/LanguageExtensions.rst             |  1 +
 .../clang/Basic/DiagnosticParseKinds.td       |  6 +++++
 clang/lib/Frontend/InitPreprocessor.cpp       |  1 +
 clang/lib/Parse/ParseDeclCXX.cpp              | 23 ++++++++++++-------
 clang/test/Lexer/cxx-features.cpp             |  4 ++++
 .../cxx2c-variadic-friends-ext-diags.cpp      | 16 +++++++++++++
 6 files changed, 43 insertions(+), 8 deletions(-)
 create mode 100644 clang/test/Parser/cxx2c-variadic-friends-ext-diags.cpp

diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index a747464582e77d..df9cae817c7614 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -1504,6 +1504,7 @@ Conditional ``explicit``                     __cpp_conditional_explicit       C+
 Attributes on Lambda-Expressions                                              C++23         C++11
 Attributes on Structured Bindings            __cpp_structured_bindings        C++26         C++03
 ``= delete ("should have a reason");``       __cpp_deleted_function           C++26         C++03
+Variadic Friends                             __cpp_variadic_friend            C++26         C++03
 -------------------------------------------- -------------------------------- ------------- -------------
 Designated initializers (N494)                                                C99           C89
 Array & element qualification (N2607)                                         C23           C89
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index f8d50d12bb9351..87449db3053f02 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -965,6 +965,12 @@ def warn_cxx23_delete_with_message : Warning<
   "'= delete' with a message is incompatible with C++ standards before C++2c">,
   DefaultIgnore, InGroup<CXXPre26Compat>;
 
+def ext_variadic_friends : ExtWarn<
+  "variadic 'friend' declarations are a C++2c extension">, InGroup<CXX26>;
+def warn_cxx23_variadic_friends : Warning<
+  "variadic 'friend' declarations are incompatible with C++ standards before C++2c">,
+  DefaultIgnore, InGroup<CXXPre26Compat>;
+
 // C++11 default member initialization
 def ext_nonstatic_member_init : ExtWarn<
   "default member initializer for non-static data member is a C++11 "
diff --git a/clang/lib/Frontend/InitPreprocessor.cpp b/clang/lib/Frontend/InitPreprocessor.cpp
index bb5822e36cbfa8..6e29efe8fc6615 100644
--- a/clang/lib/Frontend/InitPreprocessor.cpp
+++ b/clang/lib/Frontend/InitPreprocessor.cpp
@@ -768,6 +768,7 @@ static void InitializeCPlusPlusFeatureTestMacros(const LangOptions &LangOpts,
 
   // C++26 features supported in earlier language modes.
   Builder.defineMacro("__cpp_deleted_function", "202403L");
+  Builder.defineMacro("__cpp_variadic_friend", "202403L");
 
   if (LangOpts.Char8)
     Builder.defineMacro("__cpp_char8_t", "202207L");
diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index d5c4390e221e53..f7381ae33658b8 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -3068,15 +3068,17 @@ Parser::DeclGroupPtrTy Parser::ParseCXXClassMemberDeclaration(
   // C++26 [class.mem.general]p10: If a name-declaration matches the
   // syntactic requirements of friend-type-declaration, it is a
   // friend-type-declaration.
-  //
-  // This means that e.g. 'friend int, long;' is valid, but
-  // 'int friend, long;' is not.
-  //
-  // TODO: Do we want to expose this in earlier language modes?
-  if (DS.isFriendSpecifiedFirst() && getLangOpts().CPlusPlus26 &&
-      Tok.isOneOf(tok::comma, tok::ellipsis)) {
+  if (DS.isFriendSpecifiedFirst() && Tok.isOneOf(tok::comma, tok::ellipsis)) {
     SourceLocation FriendLoc = DS.getFriendSpecLoc();
     SmallVector<Decl *> Decls;
+    auto DiagnoseCompat = [&, Diagnosed = false] () mutable {
+      if (Diagnosed)
+        return;
+      Diagnosed = true;
+      Diag(Tok.getLocation(), getLangOpts().CPlusPlus26
+                                  ? diag::warn_cxx23_variadic_friends
+                                  : diag::ext_variadic_friends);
+    };
 
     // Handles a single friend-type-specifier.
     auto ParsedFriendDecl = [&](ParsingDeclSpec &DeclSpec) {
@@ -3093,8 +3095,10 @@ Parser::DeclGroupPtrTy Parser::ParseCXXClassMemberDeclaration(
       }
 
       // Eat the '...'.
-      if (Variadic)
+      if (Variadic) {
+        DiagnoseCompat();
         ConsumeToken();
+      }
 
       Decls.push_back(D);
       return false;
@@ -3121,6 +3125,9 @@ Parser::DeclGroupPtrTy Parser::ParseCXXClassMemberDeclaration(
       return nullptr;
     }*/
 
+    if (Tok.is(tok::comma))
+      DiagnoseCompat();
+
     while (TryConsumeToken(tok::comma)) {
       ParsingDeclSpec DeclSpec(*this, TemplateDiags);
       const char *PrevSpec = nullptr;
diff --git a/clang/test/Lexer/cxx-features.cpp b/clang/test/Lexer/cxx-features.cpp
index 4c2aa3ae2c544b..cca92888268494 100644
--- a/clang/test/Lexer/cxx-features.cpp
+++ b/clang/test/Lexer/cxx-features.cpp
@@ -34,6 +34,10 @@
 
 // --- C++26 features ---
 
+#if check(variadic_friend, 202403, 202403, 202403, 202403, 202403, 202403, 202403)
+#error "wrong value for __cpp_variadic_friend"
+#endif
+
 #if check(deleted_function, 202403, 202403, 202403, 202403, 202403, 202403, 202403)
 #error "wrong value for __cpp_deleted_function"
 #endif
diff --git a/clang/test/Parser/cxx2c-variadic-friends-ext-diags.cpp b/clang/test/Parser/cxx2c-variadic-friends-ext-diags.cpp
new file mode 100644
index 00000000000000..ffcc97ffd63529
--- /dev/null
+++ b/clang/test/Parser/cxx2c-variadic-friends-ext-diags.cpp
@@ -0,0 +1,16 @@
+// RUN: %clang_cc1 -std=c++2c -verify=compat -fsyntax-only -Wpre-c++26-compat %s
+// RUN: %clang_cc1 -std=c++11 -verify=pre2c -fsyntax-only -Wc++26-extensions %s
+
+struct S {
+  friend int, long, char; // compat-warning {{variadic 'friend' declarations are incompatible with C++ standards before C++2c}} \
+                          // pre2c-warning {{variadic 'friend' declarations are a C++2c extension}}
+};
+
+template <typename ...Types>
+struct TS {
+  friend Types...; // compat-warning {{variadic 'friend' declarations are incompatible with C++ standards before C++2c}} \
+                   // pre2c-warning {{variadic 'friend' declarations are a C++2c extension}}
+
+  friend int, Types..., Types...; // compat-warning {{variadic 'friend' declarations are incompatible with C++ standards before C++2c}} \
+                                  // pre2c-warning {{variadic 'friend' declarations are a C++2c extension}}
+};

>From 99b85b40e1cc5a160d9c7daad3c4459371ff554c Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Mon, 5 Aug 2024 18:36:08 +0200
Subject: [PATCH 09/20] Handle more invalid cases

---
 clang/include/clang/Sema/Sema.h              |  1 +
 clang/lib/Parse/ParseDeclCXX.cpp             | 55 +++++++++++---------
 clang/lib/Sema/SemaDeclCXX.cpp               | 13 +++--
 clang/test/Parser/cxx2c-variadic-friends.cpp | 31 +++++++----
 4 files changed, 61 insertions(+), 39 deletions(-)

diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 07c8c2cef9cc30..05f573d208e00d 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -5854,6 +5854,7 @@ class Sema final : public SemaBase {
                                      unsigned TagSpec, SourceLocation TagLoc,
                                      CXXScopeSpec &SS, IdentifierInfo *Name,
                                      SourceLocation NameLoc,
+                                     SourceLocation EllipsisLoc,
                                      const ParsedAttributesView &Attr,
                                      MultiTemplateParamsArg TempParamLists);
 
diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index 085c7b52aa0979..247777e2b80f75 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -2004,7 +2004,14 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
 
   const PrintingPolicy &Policy = Actions.getASTContext().getPrintingPolicy();
   TagUseKind TUK;
-  if (isDefiningTypeSpecifierContext(DSC, getLangOpts().CPlusPlus) ==
+
+  // C++26 [class.mem.general]p10: If a name-declaration matches the
+  // syntactic requirements of friend-type-declaration, it is a
+  // friend-type-declaration.
+  if (getLangOpts().CPlusPlus && DS.isFriendSpecifiedFirst() &&
+      Tok.isOneOf(tok::comma, tok::ellipsis))
+    TUK = TagUseKind::Friend;
+  else if (isDefiningTypeSpecifierContext(DSC, getLangOpts().CPlusPlus) ==
           AllowDefiningTypeSpec::No ||
       (getLangOpts().OpenMP && OpenMPDirectiveParsing))
     TUK = TagUseKind::Reference;
@@ -2238,9 +2245,28 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
                             diag::err_keyword_not_allowed,
                             /*DiagnoseEmptyAttrs=*/true);
 
+    // Consume '...' first so we error on the ',' after it if there is one.
+    SourceLocation EllipsisLoc;
+    TryConsumeToken(tok::ellipsis, EllipsisLoc);
+
+    // CWG 2917: In a template-declaration whose declaration is a
+    // friend-type-declaration, the friend-type-specifier-list shall
+    // consist of exactly one friend-type-specifier.
+    //
+    // Essentially, the following is obviously nonsense, so disallow it:
+    //
+    //   template <typename>
+    //   friend class S, int;
+    //
+    if (Tok.is(tok::comma)) {
+      Diag(Tok.getLocation(),
+           diag::err_friend_template_decl_multiple_specifiers);
+      SkipUntil(tok::semi, StopBeforeMatch);
+    }
+
     TagOrTempResult = Actions.ActOnTemplatedFriendTag(
         getCurScope(), DS.getFriendSpecLoc(), TagType, StartLoc, SS, Name,
-        NameLoc, attrs,
+        NameLoc, EllipsisLoc, attrs,
         MultiTemplateParamsArg(TemplateParams ? &(*TemplateParams)[0] : nullptr,
                                TemplateParams ? TemplateParams->size() : 0));
   } else {
@@ -3063,10 +3089,6 @@ Parser::DeclGroupPtrTy Parser::ParseCXXClassMemberDeclaration(
 
   // Handle C++26's variadic friend declarations. These don't even have
   // declarators, so we get them out of the way early here.
-  //
-  // C++26 [class.mem.general]p10: If a name-declaration matches the
-  // syntactic requirements of friend-type-declaration, it is a
-  // friend-type-declaration.
   if (DS.isFriendSpecifiedFirst() && Tok.isOneOf(tok::comma, tok::ellipsis)) {
     SourceLocation FriendLoc = DS.getFriendSpecLoc();
     SmallVector<Decl *> Decls;
@@ -3083,13 +3105,12 @@ Parser::DeclGroupPtrTy Parser::ParseCXXClassMemberDeclaration(
     auto ParsedFriendDecl = [&](ParsingDeclSpec &DeclSpec) {
       bool Variadic = Tok.is(tok::ellipsis);
       RecordDecl *AnonRecord = nullptr;
-
       Decl *D = Actions.ParsedFreeStandingDeclSpec(
           getCurScope(), AS, DeclSpec, DeclAttrs, TemplateParams, false,
           AnonRecord, Variadic ? Tok.getLocation() : SourceLocation());
       DeclSpec.complete(D);
       if (!D) {
-        SkipUntil(tok::semi);
+        SkipUntil(tok::semi, tok::r_brace);
         return true;
       }
 
@@ -3106,24 +3127,6 @@ Parser::DeclGroupPtrTy Parser::ParseCXXClassMemberDeclaration(
     if (ParsedFriendDecl(DS))
       return nullptr;
 
-    // TODO: It seems like this case is already being caught somewhere else,
-    // so maybe we don't need this check here at all?
-    /*// CWG 2917: In a template-declaration whose declaration is a
-    // friend-type-declaration, the friend-type-specifier-list shall
-    // consist of exactly one friend-type-specifier.
-    //
-    // Essentially, the following is obviously nonsense, so disallow it:
-    //
-    //   template <typename>
-    //   friend class S, int;
-    if (!TemplateParams.empty() && Tok.is(tok::comma)) {
-      Diag(Decls.front()->getLocation(),
-           diag::err_friend_template_decl_multiple_specifiers)
-          << Decls.front()->getSourceRange();
-      SkipUntil(tok::semi);
-      return nullptr;
-    }*/
-
     if (Tok.is(tok::comma))
       DiagnoseCompat();
 
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 7e66c66d1cd827..ebc1ab30e053ad 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -17354,12 +17354,18 @@ Decl *Sema::BuildStaticAssertDeclaration(SourceLocation StaticAssertLoc,
 DeclResult Sema::ActOnTemplatedFriendTag(
     Scope *S, SourceLocation FriendLoc, unsigned TagSpec, SourceLocation TagLoc,
     CXXScopeSpec &SS, IdentifierInfo *Name, SourceLocation NameLoc,
-    const ParsedAttributesView &Attr, MultiTemplateParamsArg TempParamLists) {
+    SourceLocation EllipsisLoc, const ParsedAttributesView &Attr,
+    MultiTemplateParamsArg TempParamLists) {
   TagTypeKind Kind = TypeWithKeyword::getTagTypeKindForTypeSpec(TagSpec);
 
   bool IsMemberSpecialization = false;
   bool Invalid = false;
 
+  // FIXME: This works for now; revisit once we support packs in NNSs.
+  if (EllipsisLoc.isValid())
+      Diag(EllipsisLoc, diag::err_pack_expansion_without_parameter_packs)
+          << SourceRange(FriendLoc, NameLoc);
+
   if (TemplateParameterList *TemplateParams =
           MatchTemplateParametersToScopeSpecifier(
               TagLoc, NameLoc, SS, nullptr, TempParamLists, /*friend*/ true,
@@ -17369,6 +17375,7 @@ DeclResult Sema::ActOnTemplatedFriendTag(
       if (Invalid)
         return true;
 
+
       return CheckClassTemplate(S, TagSpec, TagUseKind::Friend, TagLoc, SS,
                                 Name, NameLoc, Attr, TemplateParams, AS_public,
                                 /*ModulePrivateLoc=*/SourceLocation(),
@@ -17437,7 +17444,7 @@ DeclResult Sema::ActOnTemplatedFriendTag(
 
     FriendDecl *Friend =
         FriendDecl::Create(Context, CurContext, NameLoc, TSI, FriendLoc,
-                           /*EllipsisLoc=*/SourceLocation(), TempParamLists);
+                           EllipsisLoc, TempParamLists);
     Friend->setAccess(AS_public);
     CurContext->addDecl(Friend);
     return Friend;
@@ -17462,7 +17469,7 @@ DeclResult Sema::ActOnTemplatedFriendTag(
 
   FriendDecl *Friend =
       FriendDecl::Create(Context, CurContext, NameLoc, TSI, FriendLoc,
-                         /*EllipsisLoc=*/SourceLocation(), TempParamLists);
+                         EllipsisLoc, TempParamLists);
   Friend->setAccess(AS_public);
   Friend->setUnsupportedFriend(true);
   CurContext->addDecl(Friend);
diff --git a/clang/test/Parser/cxx2c-variadic-friends.cpp b/clang/test/Parser/cxx2c-variadic-friends.cpp
index ac99e40fc6b8ce..17e894e7112240 100644
--- a/clang/test/Parser/cxx2c-variadic-friends.cpp
+++ b/clang/test/Parser/cxx2c-variadic-friends.cpp
@@ -12,15 +12,13 @@ struct Errors {
   friend int, int;
   friend int, long, char;
 
-  // We simply ignore the '...' here.
+  // We simply diagnose and ignore the '...' here.
   friend float...; // expected-error {{pack expansion does not contain any unexpanded parameter packs}}
 
   friend short..., unsigned, unsigned short...; // expected-error 2 {{pack expansion does not contain any unexpanded parameter packs}}
 
-  // FIXME: This is a pretty bad diagnostic.
   template <typename>
-  friend struct TS, int; // expected-error {{cannot be referenced with the 'struct' specifier}}
-                         // expected-note@#template {{declared here}}
+  friend struct TS, int; // expected-error {{a friend declaration that befriends a template must contain exactly one type-specifier}}
 
   double friend; // expected-error {{'friend' must appear first in a non-function declaration}}
   double friend, double; // expected-error {{expected member name or ';' after declaration specifiers}}
@@ -29,6 +27,9 @@ struct Errors {
 template <typename>
 struct C { template<class T> class Nested; };
 
+template <typename, typename>
+struct D { template<class T> class Nested; };
+
 template<class... Ts> // expected-note {{template parameter is declared here}}
 struct VS {
   friend Ts...;
@@ -46,15 +47,25 @@ struct VS {
   friend class Us...; // expected-error {{declaration of 'Us' shadows template parameter}}
                       // expected-error at -1 {{pack expansion does not contain any unexpanded parameter packs}}
 
-  // FIXME: Ill-formed.
   template<class U>
-  friend class C<Ts>::template Nested<U>...;
+  friend class C<Ts>::template Nested<U>...; // expected-error {{cannot specialize a dependent template}}
 
-  // FIXME: Ill-formed.
   template<class... Us>
-  friend class C<Ts...>::template Nested<Us>...;
+  friend class C<Ts...>::template Nested<Us>...; // expected-error {{cannot specialize a dependent template}}
+
+  // FIXME: Should be valid, but we currently can’t handle packs in NNSs.
+  template<class ...T>
+  friend class TS<Ts>::Nested...; // expected-error {{pack expansion does not contain any unexpanded parameter packs}}
+                                  // expected-warning at -1 {{dependent nested name specifier 'TS<Ts>::' for friend template declaration is not supported; ignoring this friend declaration}}
+
+  // FIXME: This I legitimately have no idea what to do with. I *think* it might
+  // be well-formed by the same logic as the previous one?
+  template<class T>
+  friend class D<T, Ts>::Nested...; // expected-error {{pack expansion does not contain any unexpanded parameter packs}}
+                                    // expected-warning at -1 {{dependent nested name specifier 'D<T, Ts>::' for friend class declaration is not supported; turning off access control for 'VS'}}
 
-  // FIXME: Ill-formed.
+  // FIXME: Ill-formed... probably?
   template<class... Us>
-  friend class C<Us>::Nested...;
+  friend class C<Us>::Nested...; // expected-error {{pack expansion does not contain any unexpanded parameter packs}}
+                                 // expected-warning at -1 {{dependent nested name specifier 'C<Us>::' for friend class declaration is not supported; turning off access control for 'VS'}}
 };

>From b6547be7766899238b50018fbc69358eb95a8122 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Mon, 5 Aug 2024 18:37:38 +0200
Subject: [PATCH 10/20] clang-format

---
 clang/lib/Parse/ParseDeclCXX.cpp | 4 ++--
 clang/lib/Sema/SemaDeclCXX.cpp   | 5 ++---
 2 files changed, 4 insertions(+), 5 deletions(-)

diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index 247777e2b80f75..7ee79d75157226 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -2012,8 +2012,8 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
       Tok.isOneOf(tok::comma, tok::ellipsis))
     TUK = TagUseKind::Friend;
   else if (isDefiningTypeSpecifierContext(DSC, getLangOpts().CPlusPlus) ==
-          AllowDefiningTypeSpec::No ||
-      (getLangOpts().OpenMP && OpenMPDirectiveParsing))
+               AllowDefiningTypeSpec::No ||
+           (getLangOpts().OpenMP && OpenMPDirectiveParsing))
     TUK = TagUseKind::Reference;
   else if (Tok.is(tok::l_brace) ||
            (DSC != DeclSpecContext::DSC_association &&
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index ebc1ab30e053ad..3e71774a04db9f 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -17363,8 +17363,8 @@ DeclResult Sema::ActOnTemplatedFriendTag(
 
   // FIXME: This works for now; revisit once we support packs in NNSs.
   if (EllipsisLoc.isValid())
-      Diag(EllipsisLoc, diag::err_pack_expansion_without_parameter_packs)
-          << SourceRange(FriendLoc, NameLoc);
+    Diag(EllipsisLoc, diag::err_pack_expansion_without_parameter_packs)
+        << SourceRange(FriendLoc, NameLoc);
 
   if (TemplateParameterList *TemplateParams =
           MatchTemplateParametersToScopeSpecifier(
@@ -17375,7 +17375,6 @@ DeclResult Sema::ActOnTemplatedFriendTag(
       if (Invalid)
         return true;
 
-
       return CheckClassTemplate(S, TagSpec, TagUseKind::Friend, TagLoc, SS,
                                 Name, NameLoc, Attr, TemplateParams, AS_public,
                                 /*ModulePrivateLoc=*/SourceLocation(),

>From 4214b46f967b11dcb261fdfdfd3a428864796e0e Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Mon, 5 Aug 2024 18:47:10 +0200
Subject: [PATCH 11/20] clang-format, again

---
 clang/lib/Parse/ParseDeclCXX.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index 7ee79d75157226..a09016e07426f8 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -3092,7 +3092,7 @@ Parser::DeclGroupPtrTy Parser::ParseCXXClassMemberDeclaration(
   if (DS.isFriendSpecifiedFirst() && Tok.isOneOf(tok::comma, tok::ellipsis)) {
     SourceLocation FriendLoc = DS.getFriendSpecLoc();
     SmallVector<Decl *> Decls;
-    auto DiagnoseCompat = [&, Diagnosed = false] () mutable {
+    auto DiagnoseCompat = [&, Diagnosed = false]() mutable {
       if (Diagnosed)
         return;
       Diagnosed = true;

>From bf437ab1cfaf5a690a91b7d06325eec2e04f0842 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Mon, 5 Aug 2024 19:06:46 +0200
Subject: [PATCH 12/20] Remove feature test macro etc in preparation of
 splitting the implementation of this into multiple prs

---
 clang/docs/LanguageExtensions.rst            |  1 -
 clang/docs/ReleaseNotes.rst                  |  2 --
 clang/lib/Frontend/InitPreprocessor.cpp      |  5 -----
 clang/test/Lexer/cxx-features.cpp            |  7 ++++---
 clang/test/Parser/cxx2c-variadic-friends.cpp |  6 ------
 clang/www/cxx_status.html                    | 10 +++++++++-
 6 files changed, 13 insertions(+), 18 deletions(-)

diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index 74a7c8343b124b..9dcb4ac9b75ca9 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -1504,7 +1504,6 @@ Conditional ``explicit``                     __cpp_conditional_explicit       C+
 Attributes on Lambda-Expressions                                              C++23         C++11
 Attributes on Structured Bindings            __cpp_structured_bindings        C++26         C++03
 ``= delete ("should have a reason");``       __cpp_deleted_function           C++26         C++03
-Variadic Friends                             __cpp_variadic_friend            C++26         C++03
 -------------------------------------------- -------------------------------- ------------- -------------
 Designated initializers (N494)                                                C99           C89
 Array & element qualification (N2607)                                         C23           C89
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 5918c007dce31b..6f50ab07f1fc0e 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -93,8 +93,6 @@ C++2c Feature Support
 - Add ``__builtin_is_virtual_base_of`` intrinsic, which supports
   `P2985R0 A type trait for detecting virtual base classes <https://wg21.link/p2985r0>`_
 
-- Implemented `P2893R3 Variadic Friends <https://wg21.link/P2893>`_
-
 Resolutions to C++ Defect Reports
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
diff --git a/clang/lib/Frontend/InitPreprocessor.cpp b/clang/lib/Frontend/InitPreprocessor.cpp
index 6e29efe8fc6615..17b9ca7cb9910b 100644
--- a/clang/lib/Frontend/InitPreprocessor.cpp
+++ b/clang/lib/Frontend/InitPreprocessor.cpp
@@ -754,10 +754,6 @@ static void InitializeCPlusPlusFeatureTestMacros(const LangOptions &LangOpts,
     Builder.defineMacro("__cpp_multidimensional_subscript", "202211L");
     Builder.defineMacro("__cpp_auto_cast", "202110L");
   }
-  // C++26 features.
-  if (LangOpts.CPlusPlus26) {
-    Builder.defineMacro("__cpp_variadic_friend", "202403L");
-  }
 
   // We provide those C++23 features as extensions in earlier language modes, so
   // we also define their feature test macros.
@@ -768,7 +764,6 @@ static void InitializeCPlusPlusFeatureTestMacros(const LangOptions &LangOpts,
 
   // C++26 features supported in earlier language modes.
   Builder.defineMacro("__cpp_deleted_function", "202403L");
-  Builder.defineMacro("__cpp_variadic_friend", "202403L");
 
   if (LangOpts.Char8)
     Builder.defineMacro("__cpp_char8_t", "202207L");
diff --git a/clang/test/Lexer/cxx-features.cpp b/clang/test/Lexer/cxx-features.cpp
index cca92888268494..9047e90ea8cc95 100644
--- a/clang/test/Lexer/cxx-features.cpp
+++ b/clang/test/Lexer/cxx-features.cpp
@@ -34,9 +34,10 @@
 
 // --- C++26 features ---
 
-#if check(variadic_friend, 202403, 202403, 202403, 202403, 202403, 202403, 202403)
-#error "wrong value for __cpp_variadic_friend"
-#endif
+// TODO: enable once it's fully implemented.
+//#if check(variadic_friend, 202403, 202403, 202403, 202403, 202403, 202403, 202403)
+//#error "wrong value for __cpp_variadic_friend"
+//#endif
 
 #if check(deleted_function, 202403, 202403, 202403, 202403, 202403, 202403, 202403)
 #error "wrong value for __cpp_deleted_function"
diff --git a/clang/test/Parser/cxx2c-variadic-friends.cpp b/clang/test/Parser/cxx2c-variadic-friends.cpp
index 17e894e7112240..c7e5aa528908c1 100644
--- a/clang/test/Parser/cxx2c-variadic-friends.cpp
+++ b/clang/test/Parser/cxx2c-variadic-friends.cpp
@@ -1,11 +1,5 @@
 // RUN: %clang_cc1 -fsyntax-only -verify -std=c++2c %s
 
-#ifndef __cpp_variadic_friend
-#  error No variadic friends?
-#endif
-
-static_assert(__cpp_variadic_friend == 202403L);
-
 template <typename> struct TS; // #template
 
 struct Errors {
diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html
index faee8b578b6242..ae413175f7ee8a 100755
--- a/clang/www/cxx_status.html
+++ b/clang/www/cxx_status.html
@@ -202,7 +202,15 @@ <h2 id="cxx26">C++2c implementation status</h2>
  <tr>
   <td>Variadic friends</td>
   <td><a href="https://wg21.link/P2893R3">P2893R3</a></td>
-  <td class="unreleased" align="center">Clang 20</td>
+  <td class="partial" align="center">
+    <details>
+      <summary>Clang 20 (Partial)</summary>
+      Friend declarations with nested name specifiers that
+      are dependent on a template parameter of the friend
+      declaration itself or which contain unexpanded packs
+      are currently not supported.
+    </details>
+  </td>
  </tr>
  <!-- Summer 2024 papers (St Louis) -->
  <tr>

>From e175ce6b80e7b6c63a18e5d20a201122243fc1d5 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Mon, 5 Aug 2024 19:13:43 +0200
Subject: [PATCH 13/20] Add test for cwg2917

---
 clang/test/CXX/drs/cwg29xx.cpp | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)
 create mode 100644 clang/test/CXX/drs/cwg29xx.cpp

diff --git a/clang/test/CXX/drs/cwg29xx.cpp b/clang/test/CXX/drs/cwg29xx.cpp
new file mode 100644
index 00000000000000..844cbea63debb7
--- /dev/null
+++ b/clang/test/CXX/drs/cwg29xx.cpp
@@ -0,0 +1,17 @@
+// RUN: %clang_cc1 -std=c++98 -pedantic-errors -verify=expected %s
+// RUN: %clang_cc1 -std=c++11 -pedantic-errors -verify=expected %s
+// RUN: %clang_cc1 -std=c++14 -pedantic-errors -verify=expected %s
+// RUN: %clang_cc1 -std=c++17 -pedantic-errors -verify=expected %s
+// RUN: %clang_cc1 -std=c++20 -pedantic-errors -verify=expected %s
+// RUN: %clang_cc1 -std=c++23 -pedantic-errors -verify=expected %s
+// RUN: %clang_cc1 -std=c++2c -pedantic-errors -verify=expected %s
+
+namespace cwg2917 { // cwg2917: 20 2024-07-30
+template <typename>
+class Foo;
+
+struct C {
+  template <typename>
+  friend class Foo, int; // expected-error {{a friend declaration that befriends a template must contain exactly one type-specifier}}
+};
+} // namespace cwg2917

>From 5f9d44ac8fa8af334e4817a487c2a9c9246e43a9 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Mon, 5 Aug 2024 19:16:31 +0200
Subject: [PATCH 14/20] Run make-cxx-dr-status

---
 clang/test/CXX/drs/cwg29xx.cpp |  2 +-
 clang/www/cxx_dr_status.html   | 10 ++++++++--
 2 files changed, 9 insertions(+), 3 deletions(-)

diff --git a/clang/test/CXX/drs/cwg29xx.cpp b/clang/test/CXX/drs/cwg29xx.cpp
index 844cbea63debb7..df48ce637a1b43 100644
--- a/clang/test/CXX/drs/cwg29xx.cpp
+++ b/clang/test/CXX/drs/cwg29xx.cpp
@@ -6,7 +6,7 @@
 // RUN: %clang_cc1 -std=c++23 -pedantic-errors -verify=expected %s
 // RUN: %clang_cc1 -std=c++2c -pedantic-errors -verify=expected %s
 
-namespace cwg2917 { // cwg2917: 20 2024-07-30
+namespace cwg2917 { // cwg2917: 20 open 2024-07-30
 template <typename>
 class Foo;
 
diff --git a/clang/www/cxx_dr_status.html b/clang/www/cxx_dr_status.html
index a18d5d7fec63d1..c600f34b5e636a 100755
--- a/clang/www/cxx_dr_status.html
+++ b/clang/www/cxx_dr_status.html
@@ -7302,7 +7302,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
   <tr class="open" id="1248">
     <td><a href="https://cplusplus.github.io/CWG/issues/1248.html">1248</a></td>
     <td>open</td>
-    <td>Updating Annex C to C99</td>
+    <td>Updating Annex C to C99 and C23</td>
     <td align="center">Not resolved</td>
   </tr>
   <tr id="1249">
@@ -17318,13 +17318,19 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
     <td><a href="https://cplusplus.github.io/CWG/issues/2917.html">2917</a></td>
     <td>open</td>
     <td>Disallow multiple <I>friend-type-specifier</I>s for a friend template</td>
-    <td align="center">Not resolved</td>
+    <td title="Clang 20 implements 2024-07-30 resolution" align="center">Not Resolved*</td>
   </tr>
   <tr class="open" id="2918">
     <td><a href="https://cplusplus.github.io/CWG/issues/2918.html">2918</a></td>
     <td>open</td>
     <td>Consideration of constraints for address of overloaded function</td>
     <td align="center">Not resolved</td>
+  </tr>
+  <tr class="open" id="2919">
+    <td><a href="https://cplusplus.github.io/CWG/issues/2919.html">2919</a></td>
+    <td>open</td>
+    <td>Conversion function candidates for initialization of const lvalue reference</td>
+    <td align="center">Not resolved</td>
   </tr></table>
 
 </div>

>From f75da38fdeb10712d17bff54f38a1efc04d084b1 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Mon, 5 Aug 2024 19:29:11 +0200
Subject: [PATCH 15/20] Update JSONNodeDumper

---
 clang/lib/AST/JSONNodeDumper.cpp       |   1 +
 clang/test/AST/ast-dump-funcs-json.cpp | 441 ++++++++++++++++++-------
 2 files changed, 317 insertions(+), 125 deletions(-)

diff --git a/clang/lib/AST/JSONNodeDumper.cpp b/clang/lib/AST/JSONNodeDumper.cpp
index eeb314b8d32b01..058d232647cf82 100644
--- a/clang/lib/AST/JSONNodeDumper.cpp
+++ b/clang/lib/AST/JSONNodeDumper.cpp
@@ -1087,6 +1087,7 @@ void JSONNodeDumper::VisitAccessSpecDecl(const AccessSpecDecl *ASD) {
 void JSONNodeDumper::VisitFriendDecl(const FriendDecl *FD) {
   if (const TypeSourceInfo *T = FD->getFriendType())
     JOS.attribute("type", createQualType(T->getType()));
+  attributeOnlyIfTrue("variadic", FD->isVariadic());
 }
 
 void JSONNodeDumper::VisitObjCIvarDecl(const ObjCIvarDecl *D) {
diff --git a/clang/test/AST/ast-dump-funcs-json.cpp b/clang/test/AST/ast-dump-funcs-json.cpp
index 041d98f2713d32..c5e5cd736534d4 100644
--- a/clang/test/AST/ast-dump-funcs-json.cpp
+++ b/clang/test/AST/ast-dump-funcs-json.cpp
@@ -41,13 +41,18 @@ int main() {
   Test1(); // Causes this to be marked 'used'
 }
 
+template <typename ...Ts>
+struct TestFriends {
+    friend Ts...;
+};
+
 // NOTE: CHECK lines have been autogenerated by gen_ast_dump_json_test.py
 
 
 // CHECK-NOT: {{^}}Dumping
 // CHECK:  "kind": "CXXMethodDecl",
 // CHECK-NEXT:  "loc": {
-// CHECK-NEXT:   "offset": {{[0-9]+}},
+// CHECK-NEXT:   "offset": 124,
 // CHECK-NEXT:   "file": "{{.*}}",
 // CHECK-NEXT:   "line": 4,
 // CHECK-NEXT:   "col": 8,
@@ -55,12 +60,12 @@ int main() {
 // CHECK-NEXT:  },
 // CHECK-NEXT:  "range": {
 // CHECK-NEXT:   "begin": {
-// CHECK-NEXT:    "offset": {{[0-9]+}},
+// CHECK-NEXT:    "offset": 119,
 // CHECK-NEXT:    "col": 3,
 // CHECK-NEXT:    "tokLen": 4
 // CHECK-NEXT:   },
 // CHECK-NEXT:   "end": {
-// CHECK-NEXT:    "offset": {{[0-9]+}},
+// CHECK-NEXT:    "offset": 130,
 // CHECK-NEXT:    "col": 14,
 // CHECK-NEXT:    "tokLen": 1
 // CHECK-NEXT:   }
@@ -76,7 +81,7 @@ int main() {
 // CHECK-NOT: {{^}}Dumping
 // CHECK:  "kind": "CXXMethodDecl",
 // CHECK-NEXT:  "loc": {
-// CHECK-NEXT:   "offset": {{[0-9]+}},
+// CHECK-NEXT:   "offset": 140,
 // CHECK-NEXT:   "file": "{{.*}}",
 // CHECK-NEXT:   "line": 5,
 // CHECK-NEXT:   "col": 8,
@@ -84,12 +89,12 @@ int main() {
 // CHECK-NEXT:  },
 // CHECK-NEXT:  "range": {
 // CHECK-NEXT:   "begin": {
-// CHECK-NEXT:    "offset": {{[0-9]+}},
+// CHECK-NEXT:    "offset": 135,
 // CHECK-NEXT:    "col": 3,
 // CHECK-NEXT:    "tokLen": 4
 // CHECK-NEXT:   },
 // CHECK-NEXT:   "end": {
-// CHECK-NEXT:    "offset": {{[0-9]+}},
+// CHECK-NEXT:    "offset": 148,
 // CHECK-NEXT:    "col": 16,
 // CHECK-NEXT:    "tokLen": 5
 // CHECK-NEXT:   }
@@ -105,7 +110,7 @@ int main() {
 // CHECK-NOT: {{^}}Dumping
 // CHECK:  "kind": "CXXMethodDecl",
 // CHECK-NEXT:  "loc": {
-// CHECK-NEXT:   "offset": {{[0-9]+}},
+// CHECK-NEXT:   "offset": 162,
 // CHECK-NEXT:   "file": "{{.*}}",
 // CHECK-NEXT:   "line": 6,
 // CHECK-NEXT:   "col": 8,
@@ -113,12 +118,12 @@ int main() {
 // CHECK-NEXT:  },
 // CHECK-NEXT:  "range": {
 // CHECK-NEXT:   "begin": {
-// CHECK-NEXT:    "offset": {{[0-9]+}},
+// CHECK-NEXT:    "offset": 157,
 // CHECK-NEXT:    "col": 3,
 // CHECK-NEXT:    "tokLen": 4
 // CHECK-NEXT:   },
 // CHECK-NEXT:   "end": {
-// CHECK-NEXT:    "offset": {{[0-9]+}},
+// CHECK-NEXT:    "offset": 170,
 // CHECK-NEXT:    "col": 16,
 // CHECK-NEXT:    "tokLen": 8
 // CHECK-NEXT:   }
@@ -134,7 +139,7 @@ int main() {
 // CHECK-NOT: {{^}}Dumping
 // CHECK:  "kind": "CXXMethodDecl",
 // CHECK-NEXT:  "loc": {
-// CHECK-NEXT:   "offset": {{[0-9]+}},
+// CHECK-NEXT:   "offset": 187,
 // CHECK-NEXT:   "file": "{{.*}}",
 // CHECK-NEXT:   "line": 7,
 // CHECK-NEXT:   "col": 8,
@@ -142,12 +147,12 @@ int main() {
 // CHECK-NEXT:  },
 // CHECK-NEXT:  "range": {
 // CHECK-NEXT:   "begin": {
-// CHECK-NEXT:    "offset": {{[0-9]+}},
+// CHECK-NEXT:    "offset": 182,
 // CHECK-NEXT:    "col": 3,
 // CHECK-NEXT:    "tokLen": 4
 // CHECK-NEXT:   },
 // CHECK-NEXT:   "end": {
-// CHECK-NEXT:    "offset": {{[0-9]+}},
+// CHECK-NEXT:    "offset": 195,
 // CHECK-NEXT:    "col": 16,
 // CHECK-NEXT:    "tokLen": 1
 // CHECK-NEXT:   }
@@ -163,7 +168,7 @@ int main() {
 // CHECK-NOT: {{^}}Dumping
 // CHECK:  "kind": "CXXMethodDecl",
 // CHECK-NEXT:  "loc": {
-// CHECK-NEXT:   "offset": {{[0-9]+}},
+// CHECK-NEXT:   "offset": 205,
 // CHECK-NEXT:   "file": "{{.*}}",
 // CHECK-NEXT:   "line": 8,
 // CHECK-NEXT:   "col": 8,
@@ -171,12 +176,12 @@ int main() {
 // CHECK-NEXT:  },
 // CHECK-NEXT:  "range": {
 // CHECK-NEXT:   "begin": {
-// CHECK-NEXT:    "offset": {{[0-9]+}},
+// CHECK-NEXT:    "offset": 200,
 // CHECK-NEXT:    "col": 3,
 // CHECK-NEXT:    "tokLen": 4
 // CHECK-NEXT:   },
 // CHECK-NEXT:   "end": {
-// CHECK-NEXT:    "offset": {{[0-9]+}},
+// CHECK-NEXT:    "offset": 213,
 // CHECK-NEXT:    "col": 16,
 // CHECK-NEXT:    "tokLen": 2
 // CHECK-NEXT:   }
@@ -192,7 +197,7 @@ int main() {
 // CHECK-NOT: {{^}}Dumping
 // CHECK:  "kind": "CXXMethodDecl",
 // CHECK-NEXT:  "loc": {
-// CHECK-NEXT:   "offset": {{[0-9]+}},
+// CHECK-NEXT:   "offset": 232,
 // CHECK-NEXT:   "file": "{{.*}}",
 // CHECK-NEXT:   "line": 9,
 // CHECK-NEXT:   "col": 16,
@@ -200,12 +205,12 @@ int main() {
 // CHECK-NEXT:  },
 // CHECK-NEXT:  "range": {
 // CHECK-NEXT:   "begin": {
-// CHECK-NEXT:    "offset": {{[0-9]+}},
+// CHECK-NEXT:    "offset": 219,
 // CHECK-NEXT:    "col": 3,
 // CHECK-NEXT:    "tokLen": 7
 // CHECK-NEXT:   },
 // CHECK-NEXT:   "end": {
-// CHECK-NEXT:    "offset": {{[0-9]+}},
+// CHECK-NEXT:    "offset": 253,
 // CHECK-NEXT:    "col": 37,
 // CHECK-NEXT:    "tokLen": 1
 // CHECK-NEXT:   }
@@ -221,18 +226,18 @@ int main() {
 // CHECK-NEXT:    "id": "0x{{.*}}",
 // CHECK-NEXT:    "kind": "ParmVarDecl",
 // CHECK-NEXT:    "loc": {
-// CHECK-NEXT:     "offset": {{[0-9]+}},
+// CHECK-NEXT:     "offset": 243,
 // CHECK-NEXT:     "col": 27,
 // CHECK-NEXT:     "tokLen": 1
 // CHECK-NEXT:    },
 // CHECK-NEXT:    "range": {
 // CHECK-NEXT:     "begin": {
-// CHECK-NEXT:      "offset": {{[0-9]+}},
+// CHECK-NEXT:      "offset": 238,
 // CHECK-NEXT:      "col": 22,
 // CHECK-NEXT:      "tokLen": 5
 // CHECK-NEXT:     },
 // CHECK-NEXT:     "end": {
-// CHECK-NEXT:      "offset": {{[0-9]+}},
+// CHECK-NEXT:      "offset": 238,
 // CHECK-NEXT:      "col": 22,
 // CHECK-NEXT:      "tokLen": 5
 // CHECK-NEXT:     }
@@ -245,18 +250,18 @@ int main() {
 // CHECK-NEXT:    "id": "0x{{.*}}",
 // CHECK-NEXT:    "kind": "ParmVarDecl",
 // CHECK-NEXT:    "loc": {
-// CHECK-NEXT:     "offset": {{[0-9]+}},
+// CHECK-NEXT:     "offset": 249,
 // CHECK-NEXT:     "col": 33,
 // CHECK-NEXT:     "tokLen": 1
 // CHECK-NEXT:    },
 // CHECK-NEXT:    "range": {
 // CHECK-NEXT:     "begin": {
-// CHECK-NEXT:      "offset": {{[0-9]+}},
+// CHECK-NEXT:      "offset": 245,
 // CHECK-NEXT:      "col": 29,
 // CHECK-NEXT:      "tokLen": 3
 // CHECK-NEXT:     },
 // CHECK-NEXT:     "end": {
-// CHECK-NEXT:      "offset": {{[0-9]+}},
+// CHECK-NEXT:      "offset": 251,
 // CHECK-NEXT:      "col": 35,
 // CHECK-NEXT:      "tokLen": 2
 // CHECK-NEXT:     }
@@ -271,12 +276,12 @@ int main() {
 // CHECK-NEXT:      "kind": "IntegerLiteral",
 // CHECK-NEXT:      "range": {
 // CHECK-NEXT:       "begin": {
-// CHECK-NEXT:        "offset": {{[0-9]+}},
+// CHECK-NEXT:        "offset": 251,
 // CHECK-NEXT:        "col": 35,
 // CHECK-NEXT:        "tokLen": 2
 // CHECK-NEXT:       },
 // CHECK-NEXT:       "end": {
-// CHECK-NEXT:        "offset": {{[0-9]+}},
+// CHECK-NEXT:        "offset": 251,
 // CHECK-NEXT:        "col": 35,
 // CHECK-NEXT:        "tokLen": 2
 // CHECK-NEXT:       }
@@ -296,7 +301,7 @@ int main() {
 // CHECK-NOT: {{^}}Dumping
 // CHECK:  "kind": "CXXMethodDecl",
 // CHECK-NEXT:  "loc": {
-// CHECK-NEXT:   "offset": {{[0-9]+}},
+// CHECK-NEXT:   "offset": 271,
 // CHECK-NEXT:   "file": "{{.*}}",
 // CHECK-NEXT:   "line": 10,
 // CHECK-NEXT:   "col": 16,
@@ -304,12 +309,12 @@ int main() {
 // CHECK-NEXT:  },
 // CHECK-NEXT:  "range": {
 // CHECK-NEXT:   "begin": {
-// CHECK-NEXT:    "offset": {{[0-9]+}},
+// CHECK-NEXT:    "offset": 258,
 // CHECK-NEXT:    "col": 3,
 // CHECK-NEXT:    "tokLen": 7
 // CHECK-NEXT:   },
 // CHECK-NEXT:   "end": {
-// CHECK-NEXT:    "offset": {{[0-9]+}},
+// CHECK-NEXT:    "offset": 281,
 // CHECK-NEXT:    "col": 26,
 // CHECK-NEXT:    "tokLen": 1
 // CHECK-NEXT:   }
@@ -327,7 +332,7 @@ int main() {
 // CHECK-NOT: {{^}}Dumping
 // CHECK:  "kind": "CXXMethodDecl",
 // CHECK-NEXT:  "loc": {
-// CHECK-NEXT:   "offset": {{[0-9]+}},
+// CHECK-NEXT:   "offset": 343,
 // CHECK-NEXT:   "file": "{{.*}}",
 // CHECK-NEXT:   "line": 14,
 // CHECK-NEXT:   "col": 8,
@@ -335,12 +340,12 @@ int main() {
 // CHECK-NEXT:  },
 // CHECK-NEXT:  "range": {
 // CHECK-NEXT:   "begin": {
-// CHECK-NEXT:    "offset": {{[0-9]+}},
+// CHECK-NEXT:    "offset": 338,
 // CHECK-NEXT:    "col": 3,
 // CHECK-NEXT:    "tokLen": 4
 // CHECK-NEXT:   },
 // CHECK-NEXT:   "end": {
-// CHECK-NEXT:    "offset": {{[0-9]+}},
+// CHECK-NEXT:    "offset": 367,
 // CHECK-NEXT:    "col": 32,
 // CHECK-NEXT:    "tokLen": 8
 // CHECK-NEXT:   }
@@ -355,18 +360,18 @@ int main() {
 // CHECK-NEXT:    "id": "0x{{.*}}",
 // CHECK-NEXT:    "kind": "ParmVarDecl",
 // CHECK-NEXT:    "loc": {
-// CHECK-NEXT:     "offset": {{[0-9]+}},
+// CHECK-NEXT:     "offset": 354,
 // CHECK-NEXT:     "col": 19,
 // CHECK-NEXT:     "tokLen": 1
 // CHECK-NEXT:    },
 // CHECK-NEXT:    "range": {
 // CHECK-NEXT:     "begin": {
-// CHECK-NEXT:      "offset": {{[0-9]+}},
+// CHECK-NEXT:      "offset": 349,
 // CHECK-NEXT:      "col": 14,
 // CHECK-NEXT:      "tokLen": 5
 // CHECK-NEXT:     },
 // CHECK-NEXT:     "end": {
-// CHECK-NEXT:      "offset": {{[0-9]+}},
+// CHECK-NEXT:      "offset": 349,
 // CHECK-NEXT:      "col": 14,
 // CHECK-NEXT:      "tokLen": 5
 // CHECK-NEXT:     }
@@ -379,18 +384,18 @@ int main() {
 // CHECK-NEXT:    "id": "0x{{.*}}",
 // CHECK-NEXT:    "kind": "ParmVarDecl",
 // CHECK-NEXT:    "loc": {
-// CHECK-NEXT:     "offset": {{[0-9]+}},
+// CHECK-NEXT:     "offset": 360,
 // CHECK-NEXT:     "col": 25,
 // CHECK-NEXT:     "tokLen": 1
 // CHECK-NEXT:    },
 // CHECK-NEXT:    "range": {
 // CHECK-NEXT:     "begin": {
-// CHECK-NEXT:      "offset": {{[0-9]+}},
+// CHECK-NEXT:      "offset": 356,
 // CHECK-NEXT:      "col": 21,
 // CHECK-NEXT:      "tokLen": 3
 // CHECK-NEXT:     },
 // CHECK-NEXT:     "end": {
-// CHECK-NEXT:      "offset": {{[0-9]+}},
+// CHECK-NEXT:      "offset": 362,
 // CHECK-NEXT:      "col": 27,
 // CHECK-NEXT:      "tokLen": 3
 // CHECK-NEXT:     }
@@ -405,12 +410,12 @@ int main() {
 // CHECK-NEXT:      "kind": "IntegerLiteral",
 // CHECK-NEXT:      "range": {
 // CHECK-NEXT:       "begin": {
-// CHECK-NEXT:        "offset": {{[0-9]+}},
+// CHECK-NEXT:        "offset": 362,
 // CHECK-NEXT:        "col": 27,
 // CHECK-NEXT:        "tokLen": 3
 // CHECK-NEXT:       },
 // CHECK-NEXT:       "end": {
-// CHECK-NEXT:        "offset": {{[0-9]+}},
+// CHECK-NEXT:        "offset": 362,
 // CHECK-NEXT:        "col": 27,
 // CHECK-NEXT:        "tokLen": 3
 // CHECK-NEXT:       }
@@ -428,12 +433,12 @@ int main() {
 // CHECK-NEXT:    "kind": "OverrideAttr",
 // CHECK-NEXT:    "range": {
 // CHECK-NEXT:     "begin": {
-// CHECK-NEXT:      "offset": {{[0-9]+}},
+// CHECK-NEXT:      "offset": 367,
 // CHECK-NEXT:      "col": 32,
 // CHECK-NEXT:      "tokLen": 8
 // CHECK-NEXT:     },
 // CHECK-NEXT:     "end": {
-// CHECK-NEXT:      "offset": {{[0-9]+}},
+// CHECK-NEXT:      "offset": 367,
 // CHECK-NEXT:      "col": 32,
 // CHECK-NEXT:      "tokLen": 8
 // CHECK-NEXT:     }
@@ -446,7 +451,7 @@ int main() {
 // CHECK-NOT: {{^}}Dumping
 // CHECK:  "kind": "CXXMethodDecl",
 // CHECK-NEXT:  "loc": {
-// CHECK-NEXT:   "offset": {{[0-9]+}},
+// CHECK-NEXT:   "offset": 399,
 // CHECK-NEXT:   "file": "{{.*}}",
 // CHECK-NEXT:   "line": 18,
 // CHECK-NEXT:   "col": 8,
@@ -454,12 +459,12 @@ int main() {
 // CHECK-NEXT:  },
 // CHECK-NEXT:  "range": {
 // CHECK-NEXT:   "begin": {
-// CHECK-NEXT:    "offset": {{[0-9]+}},
+// CHECK-NEXT:    "offset": 394,
 // CHECK-NEXT:    "col": 3,
 // CHECK-NEXT:    "tokLen": 4
 // CHECK-NEXT:   },
 // CHECK-NEXT:   "end": {
-// CHECK-NEXT:    "offset": {{[0-9]+}},
+// CHECK-NEXT:    "offset": 405,
 // CHECK-NEXT:    "col": 14,
 // CHECK-NEXT:    "tokLen": 1
 // CHECK-NEXT:   }
@@ -475,7 +480,7 @@ int main() {
 // CHECK-NOT: {{^}}Dumping
 // CHECK:  "kind": "CXXMethodDecl",
 // CHECK-NEXT:  "loc": {
-// CHECK-NEXT:   "offset": {{[0-9]+}},
+// CHECK-NEXT:   "offset": 419,
 // CHECK-NEXT:   "file": "{{.*}}",
 // CHECK-NEXT:   "line": 20,
 // CHECK-NEXT:   "col": 9,
@@ -483,12 +488,12 @@ int main() {
 // CHECK-NEXT:  },
 // CHECK-NEXT:  "range": {
 // CHECK-NEXT:   "begin": {
-// CHECK-NEXT:    "offset": {{[0-9]+}},
+// CHECK-NEXT:    "offset": 411,
 // CHECK-NEXT:    "col": 1,
 // CHECK-NEXT:    "tokLen": 4
 // CHECK-NEXT:   },
 // CHECK-NEXT:   "end": {
-// CHECK-NEXT:    "offset": {{[0-9]+}},
+// CHECK-NEXT:    "offset": 428,
 // CHECK-NEXT:    "col": 18,
 // CHECK-NEXT:    "tokLen": 1
 // CHECK-NEXT:   }
@@ -506,12 +511,12 @@ int main() {
 // CHECK-NEXT:    "kind": "CompoundStmt",
 // CHECK-NEXT:    "range": {
 // CHECK-NEXT:     "begin": {
-// CHECK-NEXT:      "offset": {{[0-9]+}},
+// CHECK-NEXT:      "offset": 427,
 // CHECK-NEXT:      "col": 17,
 // CHECK-NEXT:      "tokLen": 1
 // CHECK-NEXT:     },
 // CHECK-NEXT:     "end": {
-// CHECK-NEXT:      "offset": {{[0-9]+}},
+// CHECK-NEXT:      "offset": 428,
 // CHECK-NEXT:      "col": 18,
 // CHECK-NEXT:      "tokLen": 1
 // CHECK-NEXT:     }
@@ -524,7 +529,7 @@ int main() {
 // CHECK-NOT: {{^}}Dumping
 // CHECK:  "kind": "FunctionDecl",
 // CHECK-NEXT:  "loc": {
-// CHECK-NEXT:   "offset": {{[0-9]+}},
+// CHECK-NEXT:   "offset": 446,
 // CHECK-NEXT:   "file": "{{.*}}",
 // CHECK-NEXT:   "line": 22,
 // CHECK-NEXT:   "col": 6,
@@ -532,12 +537,12 @@ int main() {
 // CHECK-NEXT:  },
 // CHECK-NEXT:  "range": {
 // CHECK-NEXT:   "begin": {
-// CHECK-NEXT:    "offset": {{[0-9]+}},
+// CHECK-NEXT:    "offset": 441,
 // CHECK-NEXT:    "col": 1,
 // CHECK-NEXT:    "tokLen": 4
 // CHECK-NEXT:   },
 // CHECK-NEXT:   "end": {
-// CHECK-NEXT:    "offset": {{[0-9]+}},
+// CHECK-NEXT:    "offset": 452,
 // CHECK-NEXT:    "col": 12,
 // CHECK-NEXT:    "tokLen": 1
 // CHECK-NEXT:   }
@@ -554,7 +559,7 @@ int main() {
 // CHECK-NOT: {{^}}Dumping
 // CHECK:  "kind": "FunctionDecl",
 // CHECK-NEXT:  "loc": {
-// CHECK-NEXT:   "offset": {{[0-9]+}},
+// CHECK-NEXT:   "offset": 460,
 // CHECK-NEXT:   "file": "{{.*}}",
 // CHECK-NEXT:   "line": 23,
 // CHECK-NEXT:   "col": 6,
@@ -562,12 +567,12 @@ int main() {
 // CHECK-NEXT:  },
 // CHECK-NEXT:  "range": {
 // CHECK-NEXT:   "begin": {
-// CHECK-NEXT:    "offset": {{[0-9]+}},
+// CHECK-NEXT:    "offset": 455,
 // CHECK-NEXT:    "col": 1,
 // CHECK-NEXT:    "tokLen": 4
 // CHECK-NEXT:   },
 // CHECK-NEXT:   "end": {
-// CHECK-NEXT:    "offset": {{[0-9]+}},
+// CHECK-NEXT:    "offset": 470,
 // CHECK-NEXT:    "col": 16,
 // CHECK-NEXT:    "tokLen": 1
 // CHECK-NEXT:   }
@@ -583,7 +588,7 @@ int main() {
 // CHECK-NOT: {{^}}Dumping
 // CHECK:  "kind": "FunctionDecl",
 // CHECK-NEXT:  "loc": {
-// CHECK-NEXT:   "offset": {{[0-9]+}},
+// CHECK-NEXT:   "offset": 478,
 // CHECK-NEXT:   "file": "{{.*}}",
 // CHECK-NEXT:   "line": 24,
 // CHECK-NEXT:   "col": 6,
@@ -591,12 +596,12 @@ int main() {
 // CHECK-NEXT:  },
 // CHECK-NEXT:  "range": {
 // CHECK-NEXT:   "begin": {
-// CHECK-NEXT:    "offset": {{[0-9]+}},
+// CHECK-NEXT:    "offset": 473,
 // CHECK-NEXT:    "col": 1,
 // CHECK-NEXT:    "tokLen": 4
 // CHECK-NEXT:   },
 // CHECK-NEXT:   "end": {
-// CHECK-NEXT:    "offset": {{[0-9]+}},
+// CHECK-NEXT:    "offset": 496,
 // CHECK-NEXT:    "col": 24,
 // CHECK-NEXT:    "tokLen": 1
 // CHECK-NEXT:   }
@@ -611,18 +616,18 @@ int main() {
 // CHECK-NEXT:    "id": "0x{{.*}}",
 // CHECK-NEXT:    "kind": "ParmVarDecl",
 // CHECK-NEXT:    "loc": {
-// CHECK-NEXT:     "offset": {{[0-9]+}},
+// CHECK-NEXT:     "offset": 488,
 // CHECK-NEXT:     "col": 16,
 // CHECK-NEXT:     "tokLen": 1
 // CHECK-NEXT:    },
 // CHECK-NEXT:    "range": {
 // CHECK-NEXT:     "begin": {
-// CHECK-NEXT:      "offset": {{[0-9]+}},
+// CHECK-NEXT:      "offset": 484,
 // CHECK-NEXT:      "col": 12,
 // CHECK-NEXT:      "tokLen": 3
 // CHECK-NEXT:     },
 // CHECK-NEXT:     "end": {
-// CHECK-NEXT:      "offset": {{[0-9]+}},
+// CHECK-NEXT:      "offset": 488,
 // CHECK-NEXT:      "col": 16,
 // CHECK-NEXT:      "tokLen": 1
 // CHECK-NEXT:     }
@@ -636,18 +641,18 @@ int main() {
 // CHECK-NEXT:    "id": "0x{{.*}}",
 // CHECK-NEXT:    "kind": "ParmVarDecl",
 // CHECK-NEXT:    "loc": {
-// CHECK-NEXT:     "offset": {{[0-9]+}},
+// CHECK-NEXT:     "offset": 495,
 // CHECK-NEXT:     "col": 23,
 // CHECK-NEXT:     "tokLen": 1
 // CHECK-NEXT:    },
 // CHECK-NEXT:    "range": {
 // CHECK-NEXT:     "begin": {
-// CHECK-NEXT:      "offset": {{[0-9]+}},
+// CHECK-NEXT:      "offset": 491,
 // CHECK-NEXT:      "col": 19,
 // CHECK-NEXT:      "tokLen": 3
 // CHECK-NEXT:     },
 // CHECK-NEXT:     "end": {
-// CHECK-NEXT:      "offset": {{[0-9]+}},
+// CHECK-NEXT:      "offset": 495,
 // CHECK-NEXT:      "col": 23,
 // CHECK-NEXT:      "tokLen": 1
 // CHECK-NEXT:     }
@@ -664,7 +669,7 @@ int main() {
 // CHECK-NOT: {{^}}Dumping
 // CHECK:  "kind": "FunctionDecl",
 // CHECK-NEXT:  "loc": {
-// CHECK-NEXT:   "offset": {{[0-9]+}},
+// CHECK-NEXT:   "offset": 504,
 // CHECK-NEXT:   "file": "{{.*}}",
 // CHECK-NEXT:   "line": 25,
 // CHECK-NEXT:   "col": 6,
@@ -672,12 +677,12 @@ int main() {
 // CHECK-NEXT:  },
 // CHECK-NEXT:  "range": {
 // CHECK-NEXT:   "begin": {
-// CHECK-NEXT:    "offset": {{[0-9]+}},
+// CHECK-NEXT:    "offset": 499,
 // CHECK-NEXT:    "col": 1,
 // CHECK-NEXT:    "tokLen": 4
 // CHECK-NEXT:   },
 // CHECK-NEXT:   "end": {
-// CHECK-NEXT:    "offset": {{[0-9]+}},
+// CHECK-NEXT:    "offset": 527,
 // CHECK-NEXT:    "col": 29,
 // CHECK-NEXT:    "tokLen": 1
 // CHECK-NEXT:   }
@@ -692,18 +697,18 @@ int main() {
 // CHECK-NEXT:    "id": "0x{{.*}}",
 // CHECK-NEXT:    "kind": "ParmVarDecl",
 // CHECK-NEXT:    "loc": {
-// CHECK-NEXT:     "offset": {{[0-9]+}},
+// CHECK-NEXT:     "offset": 514,
 // CHECK-NEXT:     "col": 16,
 // CHECK-NEXT:     "tokLen": 1
 // CHECK-NEXT:    },
 // CHECK-NEXT:    "range": {
 // CHECK-NEXT:     "begin": {
-// CHECK-NEXT:      "offset": {{[0-9]+}},
+// CHECK-NEXT:      "offset": 510,
 // CHECK-NEXT:      "col": 12,
 // CHECK-NEXT:      "tokLen": 3
 // CHECK-NEXT:     },
 // CHECK-NEXT:     "end": {
-// CHECK-NEXT:      "offset": {{[0-9]+}},
+// CHECK-NEXT:      "offset": 514,
 // CHECK-NEXT:      "col": 16,
 // CHECK-NEXT:      "tokLen": 1
 // CHECK-NEXT:     }
@@ -717,18 +722,18 @@ int main() {
 // CHECK-NEXT:    "id": "0x{{.*}}",
 // CHECK-NEXT:    "kind": "ParmVarDecl",
 // CHECK-NEXT:    "loc": {
-// CHECK-NEXT:     "offset": {{[0-9]+}},
+// CHECK-NEXT:     "offset": 521,
 // CHECK-NEXT:     "col": 23,
 // CHECK-NEXT:     "tokLen": 1
 // CHECK-NEXT:    },
 // CHECK-NEXT:    "range": {
 // CHECK-NEXT:     "begin": {
-// CHECK-NEXT:      "offset": {{[0-9]+}},
+// CHECK-NEXT:      "offset": 517,
 // CHECK-NEXT:      "col": 19,
 // CHECK-NEXT:      "tokLen": 3
 // CHECK-NEXT:     },
 // CHECK-NEXT:     "end": {
-// CHECK-NEXT:      "offset": {{[0-9]+}},
+// CHECK-NEXT:      "offset": 525,
 // CHECK-NEXT:      "col": 27,
 // CHECK-NEXT:      "tokLen": 2
 // CHECK-NEXT:     }
@@ -744,12 +749,12 @@ int main() {
 // CHECK-NEXT:      "kind": "IntegerLiteral",
 // CHECK-NEXT:      "range": {
 // CHECK-NEXT:       "begin": {
-// CHECK-NEXT:        "offset": {{[0-9]+}},
+// CHECK-NEXT:        "offset": 525,
 // CHECK-NEXT:        "col": 27,
 // CHECK-NEXT:        "tokLen": 2
 // CHECK-NEXT:       },
 // CHECK-NEXT:       "end": {
-// CHECK-NEXT:        "offset": {{[0-9]+}},
+// CHECK-NEXT:        "offset": 525,
 // CHECK-NEXT:        "col": 27,
 // CHECK-NEXT:        "tokLen": 2
 // CHECK-NEXT:       }
@@ -769,7 +774,7 @@ int main() {
 // CHECK-NOT: {{^}}Dumping
 // CHECK:  "kind": "FunctionDecl",
 // CHECK-NEXT:  "loc": {
-// CHECK-NEXT:   "offset": {{[0-9]+}},
+// CHECK-NEXT:   "offset": 545,
 // CHECK-NEXT:   "file": "{{.*}}",
 // CHECK-NEXT:   "line": 26,
 // CHECK-NEXT:   "col": 16,
@@ -777,12 +782,12 @@ int main() {
 // CHECK-NEXT:  },
 // CHECK-NEXT:  "range": {
 // CHECK-NEXT:   "begin": {
-// CHECK-NEXT:    "offset": {{[0-9]+}},
+// CHECK-NEXT:    "offset": 530,
 // CHECK-NEXT:    "col": 1,
 // CHECK-NEXT:    "tokLen": 9
 // CHECK-NEXT:   },
 // CHECK-NEXT:   "end": {
-// CHECK-NEXT:    "offset": {{[0-9]+}},
+// CHECK-NEXT:    "offset": 555,
 // CHECK-NEXT:    "col": 26,
 // CHECK-NEXT:    "tokLen": 1
 // CHECK-NEXT:   }
@@ -799,7 +804,7 @@ int main() {
 // CHECK-NOT: {{^}}Dumping
 // CHECK:  "kind": "FunctionDecl",
 // CHECK-NEXT:  "loc": {
-// CHECK-NEXT:   "offset": {{[0-9]+}},
+// CHECK-NEXT:   "offset": 570,
 // CHECK-NEXT:   "file": "{{.*}}",
 // CHECK-NEXT:   "line": 27,
 // CHECK-NEXT:   "col": 13,
@@ -807,12 +812,12 @@ int main() {
 // CHECK-NEXT:  },
 // CHECK-NEXT:  "range": {
 // CHECK-NEXT:   "begin": {
-// CHECK-NEXT:    "offset": {{[0-9]+}},
+// CHECK-NEXT:    "offset": 558,
 // CHECK-NEXT:    "col": 1,
 // CHECK-NEXT:    "tokLen": 6
 // CHECK-NEXT:   },
 // CHECK-NEXT:   "end": {
-// CHECK-NEXT:    "offset": {{[0-9]+}},
+// CHECK-NEXT:    "offset": 580,
 // CHECK-NEXT:    "col": 23,
 // CHECK-NEXT:    "tokLen": 1
 // CHECK-NEXT:   }
@@ -829,7 +834,7 @@ int main() {
 // CHECK-NOT: {{^}}Dumping
 // CHECK:  "kind": "FunctionDecl",
 // CHECK-NEXT:  "loc": {
-// CHECK-NEXT:   "offset": {{[0-9]+}},
+// CHECK-NEXT:   "offset": 595,
 // CHECK-NEXT:   "file": "{{.*}}",
 // CHECK-NEXT:   "line": 28,
 // CHECK-NEXT:   "col": 13,
@@ -837,12 +842,12 @@ int main() {
 // CHECK-NEXT:  },
 // CHECK-NEXT:  "range": {
 // CHECK-NEXT:   "begin": {
-// CHECK-NEXT:    "offset": {{[0-9]+}},
+// CHECK-NEXT:    "offset": 583,
 // CHECK-NEXT:    "col": 1,
 // CHECK-NEXT:    "tokLen": 6
 // CHECK-NEXT:   },
 // CHECK-NEXT:   "end": {
-// CHECK-NEXT:    "offset": {{[0-9]+}},
+// CHECK-NEXT:    "offset": 605,
 // CHECK-NEXT:    "col": 23,
 // CHECK-NEXT:    "tokLen": 1
 // CHECK-NEXT:   }
@@ -859,7 +864,7 @@ int main() {
 // CHECK-NOT: {{^}}Dumping
 // CHECK:  "kind": "FunctionDecl",
 // CHECK-NEXT:  "loc": {
-// CHECK-NEXT:   "offset": {{[0-9]+}},
+// CHECK-NEXT:   "offset": 620,
 // CHECK-NEXT:   "file": "{{.*}}",
 // CHECK-NEXT:   "line": 29,
 // CHECK-NEXT:   "col": 13,
@@ -867,12 +872,12 @@ int main() {
 // CHECK-NEXT:  },
 // CHECK-NEXT:  "range": {
 // CHECK-NEXT:   "begin": {
-// CHECK-NEXT:    "offset": {{[0-9]+}},
+// CHECK-NEXT:    "offset": 608,
 // CHECK-NEXT:    "col": 1,
 // CHECK-NEXT:    "tokLen": 6
 // CHECK-NEXT:   },
 // CHECK-NEXT:   "end": {
-// CHECK-NEXT:    "offset": {{[0-9]+}},
+// CHECK-NEXT:    "offset": 630,
 // CHECK-NEXT:    "col": 23,
 // CHECK-NEXT:    "tokLen": 1
 // CHECK-NEXT:   }
@@ -889,7 +894,7 @@ int main() {
 // CHECK-NOT: {{^}}Dumping
 // CHECK:  "kind": "FunctionDecl",
 // CHECK-NEXT:  "loc": {
-// CHECK-NEXT:   "offset": {{[0-9]+}},
+// CHECK-NEXT:   "offset": 638,
 // CHECK-NEXT:   "file": "{{.*}}",
 // CHECK-NEXT:   "line": 30,
 // CHECK-NEXT:   "col": 6,
@@ -897,12 +902,12 @@ int main() {
 // CHECK-NEXT:  },
 // CHECK-NEXT:  "range": {
 // CHECK-NEXT:   "begin": {
-// CHECK-NEXT:    "offset": {{[0-9]+}},
+// CHECK-NEXT:    "offset": 633,
 // CHECK-NEXT:    "col": 1,
 // CHECK-NEXT:    "tokLen": 4
 // CHECK-NEXT:   },
 // CHECK-NEXT:   "end": {
-// CHECK-NEXT:    "offset": {{[0-9]+}},
+// CHECK-NEXT:    "offset": 650,
 // CHECK-NEXT:    "col": 18,
 // CHECK-NEXT:    "tokLen": 8
 // CHECK-NEXT:   }
@@ -918,7 +923,7 @@ int main() {
 // CHECK-NOT: {{^}}Dumping
 // CHECK:  "kind": "FunctionDecl",
 // CHECK-NEXT:  "loc": {
-// CHECK-NEXT:   "offset": {{[0-9]+}},
+// CHECK-NEXT:   "offset": 665,
 // CHECK-NEXT:   "file": "{{.*}}",
 // CHECK-NEXT:   "line": 31,
 // CHECK-NEXT:   "col": 6,
@@ -926,12 +931,12 @@ int main() {
 // CHECK-NEXT:  },
 // CHECK-NEXT:  "range": {
 // CHECK-NEXT:   "begin": {
-// CHECK-NEXT:    "offset": {{[0-9]+}},
+// CHECK-NEXT:    "offset": 660,
 // CHECK-NEXT:    "col": 1,
 // CHECK-NEXT:    "tokLen": 4
 // CHECK-NEXT:   },
 // CHECK-NEXT:   "end": {
-// CHECK-NEXT:    "offset": {{[0-9]+}},
+// CHECK-NEXT:    "offset": 692,
 // CHECK-NEXT:    "col": 33,
 // CHECK-NEXT:    "tokLen": 1
 // CHECK-NEXT:   }
@@ -947,7 +952,7 @@ int main() {
 // CHECK-NOT: {{^}}Dumping
 // CHECK:  "kind": "FunctionDecl",
 // CHECK-NEXT:  "loc": {
-// CHECK-NEXT:   "offset": {{[0-9]+}},
+// CHECK-NEXT:   "offset": 700,
 // CHECK-NEXT:   "file": "{{.*}}",
 // CHECK-NEXT:   "line": 32,
 // CHECK-NEXT:   "col": 6,
@@ -955,12 +960,12 @@ int main() {
 // CHECK-NEXT:  },
 // CHECK-NEXT:  "range": {
 // CHECK-NEXT:   "begin": {
-// CHECK-NEXT:    "offset": {{[0-9]+}},
+// CHECK-NEXT:    "offset": 695,
 // CHECK-NEXT:    "col": 1,
 // CHECK-NEXT:    "tokLen": 4
 // CHECK-NEXT:   },
 // CHECK-NEXT:   "end": {
-// CHECK-NEXT:    "offset": {{[0-9]+}},
+// CHECK-NEXT:    "offset": 723,
 // CHECK-NEXT:    "col": 29,
 // CHECK-NEXT:    "tokLen": 1
 // CHECK-NEXT:   }
@@ -976,7 +981,7 @@ int main() {
 // CHECK-NOT: {{^}}Dumping
 // CHECK:  "kind": "FunctionTemplateDecl",
 // CHECK-NEXT:  "loc": {
-// CHECK-NEXT:   "offset": {{[0-9]+}},
+// CHECK-NEXT:   "offset": 751,
 // CHECK-NEXT:   "file": "{{.*}}",
 // CHECK-NEXT:   "line": 35,
 // CHECK-NEXT:   "col": 3,
@@ -984,13 +989,13 @@ int main() {
 // CHECK-NEXT:  },
 // CHECK-NEXT:  "range": {
 // CHECK-NEXT:   "begin": {
-// CHECK-NEXT:    "offset": {{[0-9]+}},
+// CHECK-NEXT:    "offset": 727,
 // CHECK-NEXT:    "line": 34,
 // CHECK-NEXT:    "col": 1,
 // CHECK-NEXT:    "tokLen": 8
 // CHECK-NEXT:   },
 // CHECK-NEXT:   "end": {
-// CHECK-NEXT:    "offset": {{[0-9]+}},
+// CHECK-NEXT:    "offset": 760,
 // CHECK-NEXT:    "line": 35,
 // CHECK-NEXT:    "col": 12,
 // CHECK-NEXT:    "tokLen": 1
@@ -1002,19 +1007,19 @@ int main() {
 // CHECK-NEXT:    "id": "0x{{.*}}",
 // CHECK-NEXT:    "kind": "TemplateTypeParmDecl",
 // CHECK-NEXT:    "loc": {
-// CHECK-NEXT:     "offset": {{[0-9]+}},
+// CHECK-NEXT:     "offset": 746,
 // CHECK-NEXT:     "line": 34,
 // CHECK-NEXT:     "col": 20,
 // CHECK-NEXT:     "tokLen": 1
 // CHECK-NEXT:    },
 // CHECK-NEXT:    "range": {
 // CHECK-NEXT:     "begin": {
-// CHECK-NEXT:      "offset": {{[0-9]+}},
+// CHECK-NEXT:      "offset": 737,
 // CHECK-NEXT:      "col": 11,
 // CHECK-NEXT:      "tokLen": 8
 // CHECK-NEXT:     },
 // CHECK-NEXT:     "end": {
-// CHECK-NEXT:      "offset": {{[0-9]+}},
+// CHECK-NEXT:      "offset": 746,
 // CHECK-NEXT:      "col": 20,
 // CHECK-NEXT:      "tokLen": 1
 // CHECK-NEXT:     }
@@ -1029,19 +1034,19 @@ int main() {
 // CHECK-NEXT:    "id": "0x{{.*}}",
 // CHECK-NEXT:    "kind": "FunctionDecl",
 // CHECK-NEXT:    "loc": {
-// CHECK-NEXT:     "offset": {{[0-9]+}},
+// CHECK-NEXT:     "offset": 751,
 // CHECK-NEXT:     "line": 35,
 // CHECK-NEXT:     "col": 3,
 // CHECK-NEXT:     "tokLen": 6
 // CHECK-NEXT:    },
 // CHECK-NEXT:    "range": {
 // CHECK-NEXT:     "begin": {
-// CHECK-NEXT:      "offset": {{[0-9]+}},
+// CHECK-NEXT:      "offset": 749,
 // CHECK-NEXT:      "col": 1,
 // CHECK-NEXT:      "tokLen": 1
 // CHECK-NEXT:     },
 // CHECK-NEXT:     "end": {
-// CHECK-NEXT:      "offset": {{[0-9]+}},
+// CHECK-NEXT:      "offset": 760,
 // CHECK-NEXT:      "col": 12,
 // CHECK-NEXT:      "tokLen": 1
 // CHECK-NEXT:     }
@@ -1055,18 +1060,18 @@ int main() {
 // CHECK-NEXT:      "id": "0x{{.*}}",
 // CHECK-NEXT:      "kind": "ParmVarDecl",
 // CHECK-NEXT:      "loc": {
-// CHECK-NEXT:       "offset": {{[0-9]+}},
+// CHECK-NEXT:       "offset": 760,
 // CHECK-NEXT:       "col": 12,
 // CHECK-NEXT:       "tokLen": 1
 // CHECK-NEXT:      },
 // CHECK-NEXT:      "range": {
 // CHECK-NEXT:       "begin": {
-// CHECK-NEXT:        "offset": {{[0-9]+}},
+// CHECK-NEXT:        "offset": 758,
 // CHECK-NEXT:        "col": 10,
 // CHECK-NEXT:        "tokLen": 1
 // CHECK-NEXT:       },
 // CHECK-NEXT:       "end": {
-// CHECK-NEXT:        "offset": {{[0-9]+}},
+// CHECK-NEXT:        "offset": 759,
 // CHECK-NEXT:        "col": 11,
 // CHECK-NEXT:        "tokLen": 1
 // CHECK-NEXT:       }
@@ -1084,7 +1089,7 @@ int main() {
 // CHECK-NOT: {{^}}Dumping
 // CHECK:  "kind": "FunctionDecl",
 // CHECK-NEXT:  "loc": {
-// CHECK-NEXT:   "offset": {{[0-9]+}},
+// CHECK-NEXT:   "offset": 769,
 // CHECK-NEXT:   "file": "{{.*}}",
 // CHECK-NEXT:   "line": 37,
 // CHECK-NEXT:   "col": 6,
@@ -1092,12 +1097,12 @@ int main() {
 // CHECK-NEXT:  },
 // CHECK-NEXT:  "range": {
 // CHECK-NEXT:   "begin": {
-// CHECK-NEXT:    "offset": {{[0-9]+}},
+// CHECK-NEXT:    "offset": 764,
 // CHECK-NEXT:    "col": 1,
 // CHECK-NEXT:    "tokLen": 4
 // CHECK-NEXT:   },
 // CHECK-NEXT:   "end": {
-// CHECK-NEXT:    "offset": {{[0-9]+}},
+// CHECK-NEXT:    "offset": 782,
 // CHECK-NEXT:    "col": 19,
 // CHECK-NEXT:    "tokLen": 1
 // CHECK-NEXT:   }
@@ -1112,18 +1117,18 @@ int main() {
 // CHECK-NEXT:    "id": "0x{{.*}}",
 // CHECK-NEXT:    "kind": "ParmVarDecl",
 // CHECK-NEXT:    "loc": {
-// CHECK-NEXT:     "offset": {{[0-9]+}},
+// CHECK-NEXT:     "offset": 779,
 // CHECK-NEXT:     "col": 16,
 // CHECK-NEXT:     "tokLen": 1
 // CHECK-NEXT:    },
 // CHECK-NEXT:    "range": {
 // CHECK-NEXT:     "begin": {
-// CHECK-NEXT:      "offset": {{[0-9]+}},
+// CHECK-NEXT:      "offset": 776,
 // CHECK-NEXT:      "col": 13,
 // CHECK-NEXT:      "tokLen": 3
 // CHECK-NEXT:     },
 // CHECK-NEXT:     "end": {
-// CHECK-NEXT:      "offset": {{[0-9]+}},
+// CHECK-NEXT:      "offset": 776,
 // CHECK-NEXT:      "col": 13,
 // CHECK-NEXT:      "tokLen": 3
 // CHECK-NEXT:     }
@@ -1137,12 +1142,12 @@ int main() {
 // CHECK-NEXT:    "kind": "CompoundStmt",
 // CHECK-NEXT:    "range": {
 // CHECK-NEXT:     "begin": {
-// CHECK-NEXT:      "offset": {{[0-9]+}},
+// CHECK-NEXT:      "offset": 781,
 // CHECK-NEXT:      "col": 18,
 // CHECK-NEXT:      "tokLen": 1
 // CHECK-NEXT:     },
 // CHECK-NEXT:     "end": {
-// CHECK-NEXT:      "offset": {{[0-9]+}},
+// CHECK-NEXT:      "offset": 782,
 // CHECK-NEXT:      "col": 19,
 // CHECK-NEXT:      "tokLen": 1
 // CHECK-NEXT:     }
@@ -1155,7 +1160,7 @@ int main() {
 // CHECK-NOT: {{^}}Dumping
 // CHECK:  "kind": "FunctionDecl",
 // CHECK-NEXT:  "loc": {
-// CHECK-NEXT:   "offset": {{[0-9]+}},
+// CHECK-NEXT:   "offset": 789,
 // CHECK-NEXT:   "file": "{{.*}}",
 // CHECK-NEXT:   "line": 38,
 // CHECK-NEXT:   "col": 6,
@@ -1163,12 +1168,12 @@ int main() {
 // CHECK-NEXT:  },
 // CHECK-NEXT:  "range": {
 // CHECK-NEXT:   "begin": {
-// CHECK-NEXT:    "offset": {{[0-9]+}},
+// CHECK-NEXT:    "offset": 784,
 // CHECK-NEXT:    "col": 1,
 // CHECK-NEXT:    "tokLen": 4
 // CHECK-NEXT:   },
 // CHECK-NEXT:   "end": {
-// CHECK-NEXT:    "offset": {{[0-9]+}},
+// CHECK-NEXT:    "offset": 807,
 // CHECK-NEXT:    "col": 24,
 // CHECK-NEXT:    "tokLen": 1
 // CHECK-NEXT:   }
@@ -1184,18 +1189,18 @@ int main() {
 // CHECK-NEXT:    "id": "0x{{.*}}",
 // CHECK-NEXT:    "kind": "ParmVarDecl",
 // CHECK-NEXT:    "loc": {
-// CHECK-NEXT:     "offset": {{[0-9]+}},
+// CHECK-NEXT:     "offset": 799,
 // CHECK-NEXT:     "col": 16,
 // CHECK-NEXT:     "tokLen": 1
 // CHECK-NEXT:    },
 // CHECK-NEXT:    "range": {
 // CHECK-NEXT:     "begin": {
-// CHECK-NEXT:      "offset": {{[0-9]+}},
+// CHECK-NEXT:      "offset": 796,
 // CHECK-NEXT:      "col": 13,
 // CHECK-NEXT:      "tokLen": 3
 // CHECK-NEXT:     },
 // CHECK-NEXT:     "end": {
-// CHECK-NEXT:      "offset": {{[0-9]+}},
+// CHECK-NEXT:      "offset": 796,
 // CHECK-NEXT:      "col": 13,
 // CHECK-NEXT:      "tokLen": 3
 // CHECK-NEXT:     }
@@ -1209,12 +1214,12 @@ int main() {
 // CHECK-NEXT:    "kind": "CompoundStmt",
 // CHECK-NEXT:    "range": {
 // CHECK-NEXT:     "begin": {
-// CHECK-NEXT:      "offset": {{[0-9]+}},
+// CHECK-NEXT:      "offset": 806,
 // CHECK-NEXT:      "col": 23,
 // CHECK-NEXT:      "tokLen": 1
 // CHECK-NEXT:     },
 // CHECK-NEXT:     "end": {
-// CHECK-NEXT:      "offset": {{[0-9]+}},
+// CHECK-NEXT:      "offset": 807,
 // CHECK-NEXT:      "col": 24,
 // CHECK-NEXT:      "tokLen": 1
 // CHECK-NEXT:     }
@@ -1222,3 +1227,189 @@ int main() {
 // CHECK-NEXT:   }
 // CHECK-NEXT:  ]
 // CHECK-NEXT: }
+
+
+// CHECK-NOT: {{^}}Dumping
+// CHECK:  "kind": "ClassTemplateDecl",
+// CHECK-NEXT:  "loc": {
+// CHECK-NEXT:   "offset": 905,
+// CHECK-NEXT:   "file": "{{.*}}",
+// CHECK-NEXT:   "line": 45,
+// CHECK-NEXT:   "col": 8,
+// CHECK-NEXT:   "tokLen": 11
+// CHECK-NEXT:  },
+// CHECK-NEXT:  "range": {
+// CHECK-NEXT:   "begin": {
+// CHECK-NEXT:    "offset": 872,
+// CHECK-NEXT:    "line": 44,
+// CHECK-NEXT:    "col": 1,
+// CHECK-NEXT:    "tokLen": 8
+// CHECK-NEXT:   },
+// CHECK-NEXT:   "end": {
+// CHECK-NEXT:    "offset": 937,
+// CHECK-NEXT:    "line": 47,
+// CHECK-NEXT:    "col": 1,
+// CHECK-NEXT:    "tokLen": 1
+// CHECK-NEXT:   }
+// CHECK-NEXT:  },
+// CHECK-NEXT:  "name": "TestFriends",
+// CHECK-NEXT:  "inner": [
+// CHECK-NEXT:   {
+// CHECK-NEXT:    "id": "0x{{.*}}",
+// CHECK-NEXT:    "kind": "TemplateTypeParmDecl",
+// CHECK-NEXT:    "loc": {
+// CHECK-NEXT:     "offset": 894,
+// CHECK-NEXT:     "line": 44,
+// CHECK-NEXT:     "col": 23,
+// CHECK-NEXT:     "tokLen": 2
+// CHECK-NEXT:    },
+// CHECK-NEXT:    "range": {
+// CHECK-NEXT:     "begin": {
+// CHECK-NEXT:      "offset": 882,
+// CHECK-NEXT:      "col": 11,
+// CHECK-NEXT:      "tokLen": 8
+// CHECK-NEXT:     },
+// CHECK-NEXT:     "end": {
+// CHECK-NEXT:      "offset": 894,
+// CHECK-NEXT:      "col": 23,
+// CHECK-NEXT:      "tokLen": 2
+// CHECK-NEXT:     }
+// CHECK-NEXT:    },
+// CHECK-NEXT:    "isReferenced": true,
+// CHECK-NEXT:    "name": "Ts",
+// CHECK-NEXT:    "tagUsed": "typename",
+// CHECK-NEXT:    "depth": 0,
+// CHECK-NEXT:    "index": 0,
+// CHECK-NEXT:    "isParameterPack": true
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:    "id": "0x{{.*}}",
+// CHECK-NEXT:    "kind": "CXXRecordDecl",
+// CHECK-NEXT:    "loc": {
+// CHECK-NEXT:     "offset": 905,
+// CHECK-NEXT:     "line": 45,
+// CHECK-NEXT:     "col": 8,
+// CHECK-NEXT:     "tokLen": 11
+// CHECK-NEXT:    },
+// CHECK-NEXT:    "range": {
+// CHECK-NEXT:     "begin": {
+// CHECK-NEXT:      "offset": 898,
+// CHECK-NEXT:      "col": 1,
+// CHECK-NEXT:      "tokLen": 6
+// CHECK-NEXT:     },
+// CHECK-NEXT:     "end": {
+// CHECK-NEXT:      "offset": 937,
+// CHECK-NEXT:      "line": 47,
+// CHECK-NEXT:      "col": 1,
+// CHECK-NEXT:      "tokLen": 1
+// CHECK-NEXT:     }
+// CHECK-NEXT:    },
+// CHECK-NEXT:    "name": "TestFriends",
+// CHECK-NEXT:    "tagUsed": "struct",
+// CHECK-NEXT:    "completeDefinition": true,
+// CHECK-NEXT:    "definitionData": {
+// CHECK-NEXT:     "canConstDefaultInit": true,
+// CHECK-NEXT:     "copyAssign": {
+// CHECK-NEXT:      "hasConstParam": true,
+// CHECK-NEXT:      "implicitHasConstParam": true,
+// CHECK-NEXT:      "needsImplicit": true,
+// CHECK-NEXT:      "simple": true,
+// CHECK-NEXT:      "trivial": true
+// CHECK-NEXT:     },
+// CHECK-NEXT:     "copyCtor": {
+// CHECK-NEXT:      "hasConstParam": true,
+// CHECK-NEXT:      "implicitHasConstParam": true,
+// CHECK-NEXT:      "needsImplicit": true,
+// CHECK-NEXT:      "simple": true,
+// CHECK-NEXT:      "trivial": true
+// CHECK-NEXT:     },
+// CHECK-NEXT:     "defaultCtor": {
+// CHECK-NEXT:      "defaultedIsConstexpr": true,
+// CHECK-NEXT:      "exists": true,
+// CHECK-NEXT:      "isConstexpr": true,
+// CHECK-NEXT:      "needsImplicit": true,
+// CHECK-NEXT:      "trivial": true
+// CHECK-NEXT:     },
+// CHECK-NEXT:     "dtor": {
+// CHECK-NEXT:      "irrelevant": true,
+// CHECK-NEXT:      "needsImplicit": true,
+// CHECK-NEXT:      "simple": true,
+// CHECK-NEXT:      "trivial": true
+// CHECK-NEXT:     },
+// CHECK-NEXT:     "hasConstexprNonCopyMoveConstructor": true,
+// CHECK-NEXT:     "isAggregate": true,
+// CHECK-NEXT:     "isEmpty": true,
+// CHECK-NEXT:     "isLiteral": true,
+// CHECK-NEXT:     "isPOD": true,
+// CHECK-NEXT:     "isStandardLayout": true,
+// CHECK-NEXT:     "isTrivial": true,
+// CHECK-NEXT:     "isTriviallyCopyable": true,
+// CHECK-NEXT:     "moveAssign": {
+// CHECK-NEXT:      "exists": true,
+// CHECK-NEXT:      "needsImplicit": true,
+// CHECK-NEXT:      "simple": true,
+// CHECK-NEXT:      "trivial": true
+// CHECK-NEXT:     },
+// CHECK-NEXT:     "moveCtor": {
+// CHECK-NEXT:      "exists": true,
+// CHECK-NEXT:      "needsImplicit": true,
+// CHECK-NEXT:      "simple": true,
+// CHECK-NEXT:      "trivial": true
+// CHECK-NEXT:     }
+// CHECK-NEXT:    },
+// CHECK-NEXT:    "inner": [
+// CHECK-NEXT:     {
+// CHECK-NEXT:      "id": "0x{{.*}}",
+// CHECK-NEXT:      "kind": "CXXRecordDecl",
+// CHECK-NEXT:      "loc": {
+// CHECK-NEXT:       "offset": 905,
+// CHECK-NEXT:       "line": 45,
+// CHECK-NEXT:       "col": 8,
+// CHECK-NEXT:       "tokLen": 11
+// CHECK-NEXT:      },
+// CHECK-NEXT:      "range": {
+// CHECK-NEXT:       "begin": {
+// CHECK-NEXT:        "offset": 898,
+// CHECK-NEXT:        "col": 1,
+// CHECK-NEXT:        "tokLen": 6
+// CHECK-NEXT:       },
+// CHECK-NEXT:       "end": {
+// CHECK-NEXT:        "offset": 905,
+// CHECK-NEXT:        "col": 8,
+// CHECK-NEXT:        "tokLen": 11
+// CHECK-NEXT:       }
+// CHECK-NEXT:      },
+// CHECK-NEXT:      "isImplicit": true,
+// CHECK-NEXT:      "name": "TestFriends",
+// CHECK-NEXT:      "tagUsed": "struct"
+// CHECK-NEXT:     },
+// CHECK-NEXT:     {
+// CHECK-NEXT:      "id": "0x{{.*}}",
+// CHECK-NEXT:      "kind": "FriendDecl",
+// CHECK-NEXT:      "loc": {
+// CHECK-NEXT:       "offset": 930,
+// CHECK-NEXT:       "line": 46,
+// CHECK-NEXT:       "col": 12,
+// CHECK-NEXT:       "tokLen": 2
+// CHECK-NEXT:      },
+// CHECK-NEXT:      "range": {
+// CHECK-NEXT:       "begin": {
+// CHECK-NEXT:        "offset": 923,
+// CHECK-NEXT:        "col": 5,
+// CHECK-NEXT:        "tokLen": 6
+// CHECK-NEXT:       },
+// CHECK-NEXT:       "end": {
+// CHECK-NEXT:        "offset": 932,
+// CHECK-NEXT:        "col": 14,
+// CHECK-NEXT:        "tokLen": 3
+// CHECK-NEXT:       }
+// CHECK-NEXT:      },
+// CHECK-NEXT:      "type": {
+// CHECK-NEXT:       "qualType": "Ts"
+// CHECK-NEXT:      },
+// CHECK-NEXT:      "variadic": true
+// CHECK-NEXT:     }
+// CHECK-NEXT:    ]
+// CHECK-NEXT:   }
+// CHECK-NEXT:  ]
+// CHECK-NEXT: }

>From 36d46ffadf6fb09bccce8e8ac1c43a897cfd39ba Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Tue, 6 Aug 2024 16:09:56 +0200
Subject: [PATCH 16/20] Add more tests

---
 clang/test/Parser/cxx2c-variadic-friends.cpp | 25 ++++++++++++++++++++
 1 file changed, 25 insertions(+)

diff --git a/clang/test/Parser/cxx2c-variadic-friends.cpp b/clang/test/Parser/cxx2c-variadic-friends.cpp
index c7e5aa528908c1..027fe6d3abca49 100644
--- a/clang/test/Parser/cxx2c-variadic-friends.cpp
+++ b/clang/test/Parser/cxx2c-variadic-friends.cpp
@@ -63,3 +63,28 @@ struct VS {
   friend class C<Us>::Nested...; // expected-error {{pack expansion does not contain any unexpanded parameter packs}}
                                  // expected-warning at -1 {{dependent nested name specifier 'C<Us>::' for friend class declaration is not supported; turning off access control for 'VS'}}
 };
+
+namespace length_mismatch {
+struct A {
+  template <typename...>
+  struct Nested {
+    struct Foo{};
+  };
+};
+template <typename ...Ts>
+struct S {
+  template <typename ...Us>
+  struct T {
+    // expected-error at +2 {{pack expansion contains parameter packs 'Ts' and 'Us' that have different lengths (1 vs. 2)}}
+    // expected-error at +1 {{pack expansion contains parameter packs 'Ts' and 'Us' that have different lengths (2 vs. 1)}}
+    friend class Ts::template Nested<Us>::Foo...;
+  };
+};
+
+void f() {
+  S<A>::T<int> s;
+  S<A, A>::T<int, long> s2;
+  S<A>::T<int, long> s3; // expected-note {{in instantiation of}}
+  S<A, A>::T<int> s4; // expected-note {{in instantiation of}}
+}
+}

>From b3e3c2c952f6ebd404dd143ba0f0e629f4b58fbb Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Tue, 6 Aug 2024 17:40:51 +0200
Subject: [PATCH 17/20] So long, FriendPackDecl

---
 clang/include/clang/AST/DeclFriend.h          | 44 -------------------
 clang/include/clang/AST/RecursiveASTVisitor.h |  2 -
 clang/include/clang/Basic/DeclNodes.td        |  1 -
 .../include/clang/Serialization/ASTBitCodes.h |  3 --
 clang/lib/AST/ASTImporter.cpp                 | 26 -----------
 clang/lib/AST/DeclBase.cpp                    |  1 -
 clang/lib/AST/DeclFriend.cpp                  | 20 ---------
 clang/lib/CodeGen/CGDecl.cpp                  |  1 -
 .../lib/Sema/SemaTemplateInstantiateDecl.cpp  | 14 ++----
 clang/lib/Serialization/ASTCommon.cpp         |  1 -
 clang/lib/Serialization/ASTReaderDecl.cpp     | 11 -----
 clang/lib/Serialization/ASTWriterDecl.cpp     |  9 ----
 clang/test/SemaCXX/cxx2c-variadic-friends.cpp | 20 +++++++++
 clang/tools/libclang/CIndex.cpp               |  1 -
 14 files changed, 23 insertions(+), 131 deletions(-)

diff --git a/clang/include/clang/AST/DeclFriend.h b/clang/include/clang/AST/DeclFriend.h
index 19dd531e89ccf9..77368da9c968bc 100644
--- a/clang/include/clang/AST/DeclFriend.h
+++ b/clang/include/clang/AST/DeclFriend.h
@@ -197,50 +197,6 @@ class FriendDecl final
   static bool classofKind(Kind K) { return K == Decl::Friend; }
 };
 
-class FriendPackDecl final
-    : public Decl,
-      private llvm::TrailingObjects<FriendPackDecl, FriendDecl *> {
-  FriendDecl *InstantiatedFrom;
-
-  /// The number of friend-declarations created by this pack expansion.
-  unsigned NumExpansions;
-
-  FriendPackDecl(DeclContext *DC, FriendDecl *InstantiatedFrom,
-                 ArrayRef<FriendDecl *> FriendDecls)
-      : Decl(FriendPack, DC,
-             InstantiatedFrom ? InstantiatedFrom->getLocation()
-                              : SourceLocation()),
-        InstantiatedFrom(InstantiatedFrom), NumExpansions(FriendDecls.size()) {
-    std::uninitialized_copy(FriendDecls.begin(), FriendDecls.end(),
-                            getTrailingObjects<FriendDecl *>());
-  }
-
-public:
-  friend class ASTDeclReader;
-  friend class ASTDeclWriter;
-  friend TrailingObjects;
-
-  FriendDecl *getInstantiatedFromFriendDecl() const { return InstantiatedFrom; }
-
-  ArrayRef<FriendDecl *> expansions() const {
-    return llvm::ArrayRef(getTrailingObjects<FriendDecl *>(), NumExpansions);
-  }
-
-  static FriendPackDecl *Create(ASTContext &C, DeclContext *DC,
-                                FriendDecl *InstantiatedFrom,
-                                ArrayRef<FriendDecl *> FriendDecls);
-
-  static FriendPackDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID,
-                                            unsigned NumExpansions);
-
-  SourceRange getSourceRange() const override LLVM_READONLY {
-    return InstantiatedFrom->getSourceRange();
-  }
-
-  static bool classof(const Decl *D) { return classofKind(D->getKind()); }
-  static bool classofKind(Kind K) { return K == FriendPack; }
-};
-
 /// An iterator over the friend declarations of a class.
 class CXXRecordDecl::friend_iterator {
   friend class CXXRecordDecl;
diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h
index e49fd3f58db572..dcf5dbf449f8bf 100644
--- a/clang/include/clang/AST/RecursiveASTVisitor.h
+++ b/clang/include/clang/AST/RecursiveASTVisitor.h
@@ -1606,8 +1606,6 @@ DEF_TRAVERSE_DECL(FriendDecl, {
   }
 })
 
-DEF_TRAVERSE_DECL(FriendPackDecl, {})
-
 DEF_TRAVERSE_DECL(FriendTemplateDecl, {
   if (D->getFriendType())
     TRY_TO(TraverseTypeLoc(D->getFriendType()->getTypeLoc()));
diff --git a/clang/include/clang/Basic/DeclNodes.td b/clang/include/clang/Basic/DeclNodes.td
index 8464cce4a9ddfc..48396e85c5adac 100644
--- a/clang/include/clang/Basic/DeclNodes.td
+++ b/clang/include/clang/Basic/DeclNodes.td
@@ -98,7 +98,6 @@ def FileScopeAsm : DeclNode<Decl>;
 def TopLevelStmt : DeclNode<Decl>, DeclContext;
 def AccessSpec : DeclNode<Decl>;
 def Friend : DeclNode<Decl>;
-def FriendPack : DeclNode<Decl>;
 def FriendTemplate : DeclNode<Decl>;
 def StaticAssert : DeclNode<Decl>;
 def Block : DeclNode<Decl, "blocks">, DeclContext;
diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h
index 92e3fce78b6826..5dd0ba33f8a9c2 100644
--- a/clang/include/clang/Serialization/ASTBitCodes.h
+++ b/clang/include/clang/Serialization/ASTBitCodes.h
@@ -1377,9 +1377,6 @@ enum DeclCode {
   /// A FriendDecl record.
   DECL_FRIEND,
 
-  /// A FriendPackDecl record.
-  DECL_FRIEND_PACK,
-
   /// A FriendTemplateDecl record.
   DECL_FRIEND_TEMPLATE,
 
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 46d07069af6ba4..b0043eb8761833 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -536,7 +536,6 @@ namespace clang {
     ExpectedDecl VisitFieldDecl(FieldDecl *D);
     ExpectedDecl VisitIndirectFieldDecl(IndirectFieldDecl *D);
     ExpectedDecl VisitFriendDecl(FriendDecl *D);
-    ExpectedDecl VisitFriendPackDecl(FriendPackDecl *D);
     ExpectedDecl VisitObjCIvarDecl(ObjCIvarDecl *D);
     ExpectedDecl VisitVarDecl(VarDecl *D);
     ExpectedDecl VisitImplicitParamDecl(ImplicitParamDecl *D);
@@ -4441,31 +4440,6 @@ ExpectedDecl ASTNodeImporter::VisitFriendDecl(FriendDecl *D) {
   return FrD;
 }
 
-ExpectedDecl ASTNodeImporter::VisitFriendPackDecl(FriendPackDecl *D) {
-  // Import the major distinguishing characteristics of a declaration.
-  DeclContext *DC, *LexicalDC;
-  if (Error Err = ImportDeclContext(D, DC, LexicalDC))
-    return std::move(Err);
-
-  auto ToInstantiatedFromFriendOrErr =
-      Importer.Import(D->getInstantiatedFromFriendDecl());
-  if (!ToInstantiatedFromFriendOrErr)
-    return ToInstantiatedFromFriendOrErr.takeError();
-  SmallVector<FriendDecl *, 4> Expansions(D->expansions().size());
-  if (Error Err = ImportArrayChecked(D->expansions(), Expansions.begin()))
-    return std::move(Err);
-
-  FriendPackDecl *ToFriendPack;
-  if (GetImportedOrCreateDecl(ToFriendPack, D, Importer.getToContext(), DC,
-                              cast<FriendDecl>(*ToInstantiatedFromFriendOrErr),
-                              Expansions))
-    return ToFriendPack;
-
-  addDeclToContexts(D, ToFriendPack);
-
-  return ToFriendPack;
-}
-
 ExpectedDecl ASTNodeImporter::VisitObjCIvarDecl(ObjCIvarDecl *D) {
   // Import the major distinguishing characteristics of an ivar.
   DeclContext *DC, *LexicalDC;
diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp
index 36fcdb477f585f..98a7746b7d9970 100644
--- a/clang/lib/AST/DeclBase.cpp
+++ b/clang/lib/AST/DeclBase.cpp
@@ -949,7 +949,6 @@ unsigned Decl::getIdentifierNamespaceForKind(Kind DeclKind) {
 
     // Never have names.
     case Friend:
-    case FriendPack:
     case FriendTemplate:
     case AccessSpec:
     case LinkageSpec:
diff --git a/clang/lib/AST/DeclFriend.cpp b/clang/lib/AST/DeclFriend.cpp
index 2662f3f634da6c..8b285bfce8d522 100644
--- a/clang/lib/AST/DeclFriend.cpp
+++ b/clang/lib/AST/DeclFriend.cpp
@@ -69,26 +69,6 @@ FriendDecl *FriendDecl::CreateDeserialized(ASTContext &C, GlobalDeclID ID,
   return new (C, ID, Extra) FriendDecl(EmptyShell(), FriendTypeNumTPLists);
 }
 
-FriendPackDecl *FriendPackDecl::Create(ASTContext &C, DeclContext *DC,
-                                       FriendDecl *InstantiatedFrom,
-                                       ArrayRef<FriendDecl *> FriendDecls) {
-  size_t Extra = additionalSizeToAlloc<FriendDecl *>(FriendDecls.size());
-  return new (C, DC, Extra) FriendPackDecl(DC, InstantiatedFrom, FriendDecls);
-}
-
-FriendPackDecl *FriendPackDecl::CreateDeserialized(ASTContext &C,
-                                                   GlobalDeclID ID,
-                                                   unsigned NumExpansions) {
-  size_t Extra = additionalSizeToAlloc<FriendDecl *>(NumExpansions);
-  auto *Result =
-      new (C, ID, Extra) FriendPackDecl(nullptr, nullptr, std::nullopt);
-  Result->NumExpansions = NumExpansions;
-  auto *Trail = Result->getTrailingObjects<FriendDecl *>();
-  for (unsigned I = 0; I != NumExpansions; ++I)
-    new (Trail + I) FriendDecl *(nullptr);
-  return Result;
-}
-
 FriendDecl *CXXRecordDecl::getFirstFriend() const {
   ExternalASTSource *Source = getParentASTContext().getExternalSource();
   Decl *First = data().FirstFriend.get(Source);
diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp
index 4aa8e8f321aade..882dbad456379a 100644
--- a/clang/lib/CodeGen/CGDecl.cpp
+++ b/clang/lib/CodeGen/CGDecl.cpp
@@ -96,7 +96,6 @@ void CodeGenFunction::EmitDecl(const Decl &D) {
   case Decl::FileScopeAsm:
   case Decl::TopLevelStmt:
   case Decl::Friend:
-  case Decl::FriendPack:
   case Decl::FriendTemplate:
   case Decl::Block:
   case Decl::Captured:
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 0b3b1e49a295be..5cdf91458fd852 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -1456,7 +1456,7 @@ Decl *TemplateDeclInstantiator::VisitFriendDecl(FriendDecl *D) {
           return nullptr;
 
         assert(!RetainExpansion &&
-               "should never retain an expansion for a FriendPackDecl");
+               "should never retain an expansion for a variadic friend decl");
 
         if (ShouldExpand) {
           SmallVector<FriendDecl *> Decls;
@@ -1476,10 +1476,8 @@ Decl *TemplateDeclInstantiator::VisitFriendDecl(FriendDecl *D) {
             Decls.push_back(FD);
           }
 
-          auto *FPD = FriendPackDecl::Create(SemaRef.Context, Owner, D, Decls);
-          FPD->setAccess(AS_public);
-          Owner->addDecl(FPD);
-          return FPD;
+          // Just drop this node; we have no use for it anymore.
+          return nullptr;
         }
       }
 
@@ -1516,12 +1514,6 @@ Decl *TemplateDeclInstantiator::VisitFriendDecl(FriendDecl *D) {
   return FD;
 }
 
-Decl *TemplateDeclInstantiator::VisitFriendPackDecl(FriendPackDecl *D) {
-  // These only get created for fully unexpanded packs, at which point
-  // we should never perform any subsequent instantiation on them.
-  llvm_unreachable("instantiating FriendPackDecl?");
-}
-
 Decl *TemplateDeclInstantiator::VisitStaticAssertDecl(StaticAssertDecl *D) {
   Expr *AssertExpr = D->getAssertExpr();
 
diff --git a/clang/lib/Serialization/ASTCommon.cpp b/clang/lib/Serialization/ASTCommon.cpp
index 47ceb03b4460b4..444a8a3d3a5143 100644
--- a/clang/lib/Serialization/ASTCommon.cpp
+++ b/clang/lib/Serialization/ASTCommon.cpp
@@ -428,7 +428,6 @@ bool serialization::isRedeclarableDeclKind(unsigned Kind) {
   case Decl::TopLevelStmt:
   case Decl::AccessSpec:
   case Decl::Friend:
-  case Decl::FriendPack:
   case Decl::FriendTemplate:
   case Decl::StaticAssert:
   case Decl::Block:
diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp
index 8b767ece3053ca..baa8c8a0a215bf 100644
--- a/clang/lib/Serialization/ASTReaderDecl.cpp
+++ b/clang/lib/Serialization/ASTReaderDecl.cpp
@@ -412,7 +412,6 @@ namespace clang {
     void VisitImportDecl(ImportDecl *D);
     void VisitAccessSpecDecl(AccessSpecDecl *D);
     void VisitFriendDecl(FriendDecl *D);
-    void VisitFriendPackDecl(FriendPackDecl *D);
     void VisitFriendTemplateDecl(FriendTemplateDecl *D);
     void VisitStaticAssertDecl(StaticAssertDecl *D);
     void VisitBlockDecl(BlockDecl *BD);
@@ -2358,13 +2357,6 @@ void ASTDeclReader::VisitFriendDecl(FriendDecl *D) {
   D->EllipsisLoc = readSourceLocation();
 }
 
-void ASTDeclReader::VisitFriendPackDecl(FriendPackDecl *D) {
-  D->InstantiatedFrom = readDeclAs<FriendDecl>();
-  auto **Expansions = D->getTrailingObjects<FriendDecl *>();
-  for (unsigned I = 0; I != D->NumExpansions; ++I)
-    Expansions[I] = readDeclAs<FriendDecl>();
-}
-
 void ASTDeclReader::VisitFriendTemplateDecl(FriendTemplateDecl *D) {
   VisitDecl(D);
   unsigned NumParams = Record.readInt();
@@ -3911,9 +3903,6 @@ Decl *ASTReader::ReadDeclRecord(GlobalDeclID ID) {
   case DECL_FRIEND:
     D = FriendDecl::CreateDeserialized(Context, ID, Record.readInt());
     break;
-  case DECL_FRIEND_PACK:
-    D = FriendPackDecl::CreateDeserialized(Context, ID, Record.readInt());
-    break;
   case DECL_FRIEND_TEMPLATE:
     D = FriendTemplateDecl::CreateDeserialized(Context, ID);
     break;
diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp
index 56cb2c63b1c651..8862ced487d13e 100644
--- a/clang/lib/Serialization/ASTWriterDecl.cpp
+++ b/clang/lib/Serialization/ASTWriterDecl.cpp
@@ -130,7 +130,6 @@ namespace clang {
     void VisitImportDecl(ImportDecl *D);
     void VisitAccessSpecDecl(AccessSpecDecl *D);
     void VisitFriendDecl(FriendDecl *D);
-    void VisitFriendPackDecl(FriendPackDecl *D);
     void VisitFriendTemplateDecl(FriendTemplateDecl *D);
     void VisitStaticAssertDecl(StaticAssertDecl *D);
     void VisitBlockDecl(BlockDecl *D);
@@ -1659,14 +1658,6 @@ void ASTDeclWriter::VisitFriendDecl(FriendDecl *D) {
   Code = serialization::DECL_FRIEND;
 }
 
-void ASTDeclWriter::VisitFriendPackDecl(FriendPackDecl *D) {
-  Record.push_back(D->NumExpansions);
-  Record.AddDeclRef(D->getInstantiatedFromFriendDecl());
-  for (auto *E : D->expansions())
-    Record.AddDeclRef(E);
-  Code = serialization::DECL_FRIEND_PACK;
-}
-
 void ASTDeclWriter::VisitFriendTemplateDecl(FriendTemplateDecl *D) {
   VisitDecl(D);
   Record.push_back(D->getNumTemplateParameters());
diff --git a/clang/test/SemaCXX/cxx2c-variadic-friends.cpp b/clang/test/SemaCXX/cxx2c-variadic-friends.cpp
index f6a249c4b99b29..a4d7c8078338d2 100644
--- a/clang/test/SemaCXX/cxx2c-variadic-friends.cpp
+++ b/clang/test/SemaCXX/cxx2c-variadic-friends.cpp
@@ -134,3 +134,23 @@ class R<R<Ts...>, R<Us...>> {
 struct E { struct Nested; };
 R<R<E>, R<C, int>> rr;
 } // namespace p2893r3_note
+
+namespace template_template {
+template <typename U, template <typename> typename... Friend>
+class S {
+  friend class Friend<U>...;
+  static constexpr int a = 42;
+};
+
+template <typename U>
+struct T {
+  static_assert(S<U, T>::a == 42);
+  static_assert(S<U, T>::a == 43); // expected-error {{static assertion failed due to requirement 'S<int, template_template::T>::a == 43'}} \
+                                   // expected-note {{expression evaluates to '42 == 43'}}
+};
+
+void f() {
+  T<int> t; // expected-note {{in instantiation of}}
+}
+}
+
diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp
index cda7f6e90a1516..937d7ff09e4eef 100644
--- a/clang/tools/libclang/CIndex.cpp
+++ b/clang/tools/libclang/CIndex.cpp
@@ -7032,7 +7032,6 @@ CXCursor clang_getCursorDefinition(CXCursor C) {
   case Decl::PragmaComment:
   case Decl::PragmaDetectMismatch:
   case Decl::UsingPack:
-  case Decl::FriendPack:
   case Decl::Concept:
   case Decl::ImplicitConceptSpecialization:
   case Decl::LifetimeExtendedTemporary:

>From bce98e6e309cc64737176b97032ca87eb3d23c1b Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Tue, 13 Aug 2024 12:22:47 +0200
Subject: [PATCH 18/20] =?UTF-8?q?Address=20most=20of=20Corentin=E2=80=99s?=
 =?UTF-8?q?=20feedback?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 clang/include/clang/AST/DeclFriend.h          |  6 +--
 clang/include/clang/Sema/Sema.h               |  4 +-
 clang/lib/AST/DeclPrinter.cpp                 | 37 +++----------------
 clang/lib/AST/JSONNodeDumper.cpp              |  2 +-
 clang/lib/AST/ODRHash.cpp                     |  2 +-
 clang/lib/AST/TextNodeDumper.cpp              |  2 +-
 clang/lib/Parse/ParseDeclCXX.cpp              | 29 +++++----------
 clang/lib/Sema/SemaDecl.cpp                   |  6 +--
 clang/lib/Sema/SemaDeclCXX.cpp                | 12 +++---
 .../lib/Sema/SemaTemplateInstantiateDecl.cpp  |  2 +-
 clang/test/AST/cxx2c-variadic-friends.cpp     | 15 ++++++--
 11 files changed, 43 insertions(+), 74 deletions(-)

diff --git a/clang/include/clang/AST/DeclFriend.h b/clang/include/clang/AST/DeclFriend.h
index 77368da9c968bc..50dadeede02d53 100644
--- a/clang/include/clang/AST/DeclFriend.h
+++ b/clang/include/clang/AST/DeclFriend.h
@@ -158,11 +158,11 @@ class FriendDecl final
                             : getTrailingObjects<TemplateParameterList *>()[0]
                                   ->getTemplateLoc();
       SourceLocation EndL =
-          isVariadic() ? getEllipsisLoc() : TInfo->getTypeLoc().getEndLoc();
+          isPackExpansion() ? getEllipsisLoc() : TInfo->getTypeLoc().getEndLoc();
       return SourceRange(StartL, EndL);
     }
 
-    if (isVariadic())
+    if (isPackExpansion())
       return SourceRange(getFriendLoc(), getEllipsisLoc());
 
     if (NamedDecl *ND = getFriendDecl()) {
@@ -190,7 +190,7 @@ class FriendDecl final
     UnsupportedFriend = Unsupported;
   }
 
-  bool isVariadic() const { return EllipsisLoc.isValid(); }
+  bool isPackExpansion() const { return EllipsisLoc.isValid(); }
 
   // Implement isa/cast/dyncast/etc.
   static bool classof(const Decl *D) { return classofKind(D->getKind()); }
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 05f573d208e00d..bd48bebec25c70 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -3801,7 +3801,7 @@ class Sema final : public SemaBase {
                                    MultiTemplateParamsArg TemplateParams,
                                    bool IsExplicitInstantiation,
                                    RecordDecl *&AnonRecord,
-                                   SourceLocation FriendEllipsisLoc = {});
+                                   SourceLocation EllipsisLoc = {});
 
   /// BuildAnonymousStructOrUnion - Handle the declaration of an
   /// anonymous structure or union. Anonymous unions are a C++ feature
@@ -5540,7 +5540,7 @@ class Sema final : public SemaBase {
   ///   template <> template \<class T> friend class A<int>::B;
   Decl *ActOnFriendTypeDecl(Scope *S, const DeclSpec &DS,
                             MultiTemplateParamsArg TemplateParams,
-                            SourceLocation FriendEllipsisLoc);
+                            SourceLocation EllipsisLoc);
   NamedDecl *ActOnFriendFunctionDecl(Scope *S, Declarator &D,
                                      MultiTemplateParamsArg TemplateParams);
 
diff --git a/clang/lib/AST/DeclPrinter.cpp b/clang/lib/AST/DeclPrinter.cpp
index d769a1f1458cc2..07be813abd8adc 100644
--- a/clang/lib/AST/DeclPrinter.cpp
+++ b/clang/lib/AST/DeclPrinter.cpp
@@ -67,7 +67,7 @@ namespace {
     void VisitEnumConstantDecl(EnumConstantDecl *D);
     void VisitEmptyDecl(EmptyDecl *D);
     void VisitFunctionDecl(FunctionDecl *D);
-    void VisitFriendDecl(FriendDecl *D, bool FirstInGroup = true);
+    void VisitFriendDecl(FriendDecl *D);
     void VisitFieldDecl(FieldDecl *D);
     void VisitVarDecl(VarDecl *D);
     void VisitLabelDecl(LabelDecl *D);
@@ -487,26 +487,7 @@ void DeclPrinter::VisitDeclContext(DeclContext *DC, bool Indent) {
     }
 
     this->Indent();
-
-    // Group friend declarations if need be.
-    if (isa<FriendDecl>(*D)) {
-      auto *FD = cast<FriendDecl>(*D);
-      VisitFriendDecl(FD);
-      SourceLocation FriendLoc = FD->getFriendLoc();
-
-      // Use a separate iterator; 'D' is always one declaration 'behind' in
-      // this loop; the last friend printed here (or the first printed just
-      // now before this loop if there are no subsequent friends) will be
-      // skipped by the '++D' of the outer loop.
-      for (DeclContext::decl_iterator It; It = std::next(D), It != DEnd; ++D) {
-        auto *NextFriend = dyn_cast<FriendDecl>(*It);
-        if (!NextFriend || NextFriend->getFriendLoc() != FriendLoc)
-          break;
-        VisitFriendDecl(NextFriend, false);
-      }
-    } else {
-      Visit(*D);
-    }
+    Visit(*D);
 
     // FIXME: Need to be able to tell the DeclPrinter when
     const char *Terminator = nullptr;
@@ -881,20 +862,12 @@ void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) {
   }
 }
 
-void DeclPrinter::VisitFriendDecl(FriendDecl *D, bool FirstInGroup) {
+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));
-
-    // Hack to print friend declarations declared as a group, e.g.
-    // 'friend int, long;', instead of printing them as two separate
-    // FriendDecls, which they are in the AST.
-    if (FirstInGroup)
-      Out << "friend ";
-    else
-      Out << ", ";
-
+    Out << "friend ";
     Out << TSI->getType().getAsString(Policy);
   }
   else if (FunctionDecl *FD =
@@ -913,7 +886,7 @@ void DeclPrinter::VisitFriendDecl(FriendDecl *D, bool FirstInGroup) {
     VisitRedeclarableTemplateDecl(CTD);
   }
 
-  if (D->isVariadic())
+  if (D->isPackExpansion())
     Out << "...";
 }
 
diff --git a/clang/lib/AST/JSONNodeDumper.cpp b/clang/lib/AST/JSONNodeDumper.cpp
index 058d232647cf82..b7e905a73ca486 100644
--- a/clang/lib/AST/JSONNodeDumper.cpp
+++ b/clang/lib/AST/JSONNodeDumper.cpp
@@ -1087,7 +1087,7 @@ void JSONNodeDumper::VisitAccessSpecDecl(const AccessSpecDecl *ASD) {
 void JSONNodeDumper::VisitFriendDecl(const FriendDecl *FD) {
   if (const TypeSourceInfo *T = FD->getFriendType())
     JOS.attribute("type", createQualType(T->getType()));
-  attributeOnlyIfTrue("variadic", FD->isVariadic());
+  attributeOnlyIfTrue("variadic", FD->isPackExpansion());
 }
 
 void JSONNodeDumper::VisitObjCIvarDecl(const ObjCIvarDecl *D) {
diff --git a/clang/lib/AST/ODRHash.cpp b/clang/lib/AST/ODRHash.cpp
index 65a02a6b661495..b748093831e3f5 100644
--- a/clang/lib/AST/ODRHash.cpp
+++ b/clang/lib/AST/ODRHash.cpp
@@ -461,7 +461,7 @@ class ODRDeclVisitor : public ConstDeclVisitor<ODRDeclVisitor> {
     } else {
       AddDecl(D->getFriendDecl());
     }
-    Hash.AddBoolean(D->isVariadic());
+    Hash.AddBoolean(D->isPackExpansion());
   }
 
   void VisitTemplateTypeParmDecl(const TemplateTypeParmDecl *D) {
diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp
index 585d88e2e031ee..0b27aec491be9a 100644
--- a/clang/lib/AST/TextNodeDumper.cpp
+++ b/clang/lib/AST/TextNodeDumper.cpp
@@ -2694,7 +2694,7 @@ void TextNodeDumper::VisitAccessSpecDecl(const AccessSpecDecl *D) {
 void TextNodeDumper::VisitFriendDecl(const FriendDecl *D) {
   if (TypeSourceInfo *T = D->getFriendType())
     dumpType(T->getType());
-  if (D->isVariadic())
+  if (D->isPackExpansion())
     OS << " variadic";
 }
 
diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index a09016e07426f8..b9525b32d0bab5 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -449,7 +449,7 @@ Decl *Parser::ParseLinkage(ParsingDeclSpec &DS, DeclaratorContext Context) {
 ///
 ///      export-function-declaration:
 ///         'export' function-declaration
-/// 
+///
 ///      export-declaration-group:
 ///         'export' '{' function-declaration-seq[opt] '}'
 ///
@@ -3090,36 +3090,28 @@ Parser::DeclGroupPtrTy Parser::ParseCXXClassMemberDeclaration(
   // Handle C++26's variadic friend declarations. These don't even have
   // declarators, so we get them out of the way early here.
   if (DS.isFriendSpecifiedFirst() && Tok.isOneOf(tok::comma, tok::ellipsis)) {
+    Diag(Tok.getLocation(), getLangOpts().CPlusPlus26
+                                ? diag::warn_cxx23_variadic_friends
+                                : diag::ext_variadic_friends);
+
     SourceLocation FriendLoc = DS.getFriendSpecLoc();
     SmallVector<Decl *> Decls;
-    auto DiagnoseCompat = [&, Diagnosed = false]() mutable {
-      if (Diagnosed)
-        return;
-      Diagnosed = true;
-      Diag(Tok.getLocation(), getLangOpts().CPlusPlus26
-                                  ? diag::warn_cxx23_variadic_friends
-                                  : diag::ext_variadic_friends);
-    };
 
     // Handles a single friend-type-specifier.
     auto ParsedFriendDecl = [&](ParsingDeclSpec &DeclSpec) {
-      bool Variadic = Tok.is(tok::ellipsis);
+      SourceLocation VariadicLoc;
+      TryConsumeToken(tok::ellipsis, VariadicLoc);
+
       RecordDecl *AnonRecord = nullptr;
       Decl *D = Actions.ParsedFreeStandingDeclSpec(
           getCurScope(), AS, DeclSpec, DeclAttrs, TemplateParams, false,
-          AnonRecord, Variadic ? Tok.getLocation() : SourceLocation());
+          AnonRecord, VariadicLoc);
       DeclSpec.complete(D);
       if (!D) {
         SkipUntil(tok::semi, tok::r_brace);
         return true;
       }
 
-      // Eat the '...'.
-      if (Variadic) {
-        DiagnoseCompat();
-        ConsumeToken();
-      }
-
       Decls.push_back(D);
       return false;
     };
@@ -3127,9 +3119,6 @@ Parser::DeclGroupPtrTy Parser::ParseCXXClassMemberDeclaration(
     if (ParsedFriendDecl(DS))
       return nullptr;
 
-    if (Tok.is(tok::comma))
-      DiagnoseCompat();
-
     while (TryConsumeToken(tok::comma)) {
       ParsingDeclSpec DeclSpec(*this, TemplateDiags);
       const char *PrevSpec = nullptr;
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index d0f22b5c065bcb..eabc691c8225ce 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -5001,7 +5001,7 @@ Decl *Sema::ParsedFreeStandingDeclSpec(Scope *S, AccessSpecifier AS,
                                        MultiTemplateParamsArg TemplateParams,
                                        bool IsExplicitInstantiation,
                                        RecordDecl *&AnonRecord,
-                                       SourceLocation FriendEllipsisLoc) {
+                                       SourceLocation EllipsisLoc) {
   Decl *TagD = nullptr;
   TagDecl *Tag = nullptr;
   if (DS.getTypeSpecType() == DeclSpec::TST_class ||
@@ -5068,10 +5068,10 @@ Decl *Sema::ParsedFreeStandingDeclSpec(Scope *S, AccessSpecifier AS,
     // whatever routines created it handled the friendship aspect.
     if (TagD && !Tag)
       return nullptr;
-    return ActOnFriendTypeDecl(S, DS, TemplateParams, FriendEllipsisLoc);
+    return ActOnFriendTypeDecl(S, DS, TemplateParams, EllipsisLoc);
   }
 
-  assert(FriendEllipsisLoc.isInvalid() &&
+  assert(EllipsisLoc.isInvalid() &&
          "Friend ellipsis but not friend-specified?");
 
   // Track whether this decl-specifier declares anything.
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 3e71774a04db9f..516c2419c8ecf7 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -17477,7 +17477,7 @@ DeclResult Sema::ActOnTemplatedFriendTag(
 
 Decl *Sema::ActOnFriendTypeDecl(Scope *S, const DeclSpec &DS,
                                 MultiTemplateParamsArg TempParams,
-                                SourceLocation FriendEllipsisLoc) {
+                                SourceLocation EllipsisLoc) {
   SourceLocation Loc = DS.getBeginLoc();
   SourceLocation FriendLoc = DS.getFriendSpecLoc();
 
@@ -17520,14 +17520,14 @@ Decl *Sema::ActOnFriendTypeDecl(Scope *S, const DeclSpec &DS,
 
   // If '...' is present, the type must contain an unexpanded parameter
   // pack, and vice versa.
-  if (FriendEllipsisLoc.isInvalid() &&
+  if (EllipsisLoc.isInvalid() &&
       DiagnoseUnexpandedParameterPack(Loc, TSI, UPPC_FriendDeclaration))
     return nullptr;
-  if (FriendEllipsisLoc.isValid() &&
+  if (EllipsisLoc.isValid() &&
       !TSI->getType()->containsUnexpandedParameterPack()) {
-    Diag(FriendEllipsisLoc, diag::err_pack_expansion_without_parameter_packs)
+    Diag(EllipsisLoc, diag::err_pack_expansion_without_parameter_packs)
         << TSI->getTypeLoc().getSourceRange();
-    FriendEllipsisLoc = SourceLocation();
+    EllipsisLoc = SourceLocation();
   }
 
   if (!T->isElaboratedTypeSpecifier()) {
@@ -17579,7 +17579,7 @@ Decl *Sema::ActOnFriendTypeDecl(Scope *S, const DeclSpec &DS,
                                    FriendLoc);
   else
     D = FriendDecl::Create(Context, CurContext, TSI->getTypeLoc().getBeginLoc(),
-                           TSI, FriendLoc, FriendEllipsisLoc);
+                           TSI, FriendLoc, EllipsisLoc);
 
   if (!D)
     return nullptr;
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 5cdf91458fd852..14ca29f1bc3f1a 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -1442,7 +1442,7 @@ Decl *TemplateDeclInstantiator::VisitFriendDecl(FriendDecl *D) {
     if (D->isUnsupportedFriend()) {
       InstTy = Ty;
     } else {
-      if (D->isVariadic()) {
+      if (D->isPackExpansion()) {
         SmallVector<UnexpandedParameterPack, 2> Unexpanded;
         SemaRef.collectUnexpandedParameterPacks(Ty->getTypeLoc(), Unexpanded);
         assert(!Unexpanded.empty() && "Pack expansion without packs");
diff --git a/clang/test/AST/cxx2c-variadic-friends.cpp b/clang/test/AST/cxx2c-variadic-friends.cpp
index 2d5069196ab39b..9c526291df5de2 100644
--- a/clang/test/AST/cxx2c-variadic-friends.cpp
+++ b/clang/test/AST/cxx2c-variadic-friends.cpp
@@ -11,13 +11,16 @@ template <typename> struct TS; // #template
 struct Friends {
   // CHECK: FriendDecl {{.*}} 'int'
   // CHECK-NEXT: FriendDecl {{.*}} 'long'
-  // PRINT-NEXT: friend int, long;
+  // PRINT-NEXT: friend int;
+  // PRINT-NEXT: friend long;
   friend int, long;
 
   // CHECK-NEXT: FriendDecl {{.*}} 'int'
   // CHECK-NEXT: FriendDecl {{.*}} 'long'
   // CHECK-NEXT: FriendDecl {{.*}} 'char'
-  // PRINT-NEXT: friend int, long, char;
+  // PRINT-NEXT: friend int;
+  // PRINT-NEXT: friend long;
+  // PRINT-NEXT: friend char;
   friend int, long, char;
 
   // CHECK-NEXT: FriendDecl {{.*}} 'S'
@@ -27,7 +30,9 @@ struct Friends {
   // CHECK-NEXT: FriendDecl {{.*}} 'S'
   // CHECK-NEXT: FriendDecl {{.*}} 'S'
   // CHECK-NEXT: FriendDecl {{.*}} 'S'
-  // PRINT-NEXT: friend S, S, S;
+  // PRINT-NEXT: friend S;
+  // PRINT-NEXT: friend S;
+  // PRINT-NEXT: friend S;
   friend S, S, S;
 
   // CHECK-NEXT: FriendDecl
@@ -52,7 +57,9 @@ template <typename ...Pack> struct Variadic {
   // CHECK: FriendDecl {{.*}} 'Pack' variadic
   // CHECK-NEXT: FriendDecl {{.*}} 'long'
   // CHECK-NEXT: FriendDecl {{.*}} 'Pack' variadic
-  // PRINT-NEXT: friend Pack..., long, Pack...;
+  // PRINT-NEXT: friend Pack...;
+  // PRINT-NEXT: friend long;
+  // PRINT-NEXT: friend Pack...;
   friend Pack..., long, Pack...;
 
   // CHECK-NEXT: FriendDecl {{.*}} 'TS<Pack>' variadic

>From 410d32bf05ceb866ed812a60bc7da71882e7840d Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Tue, 13 Aug 2024 13:14:15 +0200
Subject: [PATCH 19/20] Handle invalid pack expansions properly

---
 .../clang/Basic/DiagnosticSemaKinds.td        |  2 ++
 clang/lib/Sema/SemaDeclCXX.cpp                | 23 +++++++++++-----
 clang/test/CXX/drs/cwg29xx.cpp                | 10 ++++++-
 clang/test/Parser/cxx2c-variadic-friends.cpp  | 27 ++++++++++---------
 4 files changed, 42 insertions(+), 20 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 8daa2386ef64f5..fee120588791bd 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -1740,6 +1740,8 @@ def ext_friend_tag_redecl_outside_namespace : ExtWarn<
 def err_pure_friend : Error<"friend declaration cannot have a pure-specifier">;
 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_invalid_base_in_interface : Error<
   "interface type cannot inherit from "
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 516c2419c8ecf7..fd0914d5a396ba 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -17361,11 +17361,6 @@ DeclResult Sema::ActOnTemplatedFriendTag(
   bool IsMemberSpecialization = false;
   bool Invalid = false;
 
-  // FIXME: This works for now; revisit once we support packs in NNSs.
-  if (EllipsisLoc.isValid())
-    Diag(EllipsisLoc, diag::err_pack_expansion_without_parameter_packs)
-        << SourceRange(FriendLoc, NameLoc);
-
   if (TemplateParameterList *TemplateParams =
           MatchTemplateParametersToScopeSpecifier(
               TagLoc, NameLoc, SS, nullptr, TempParamLists, /*friend*/ true,
@@ -17404,6 +17399,7 @@ DeclResult Sema::ActOnTemplatedFriendTag(
   // If it's explicit specializations all the way down, just forget
   // about the template header and build an appropriate non-templated
   // friend.  TODO: for source fidelity, remember the headers.
+  NestedNameSpecifierLoc QualifierLoc = SS.getWithLocInContext(Context);
   if (isAllExplicitSpecializations) {
     if (SS.isEmpty()) {
       bool Owned = false;
@@ -17419,7 +17415,6 @@ DeclResult Sema::ActOnTemplatedFriendTag(
                       /*IsTemplateParamOrArg=*/false, /*OOK=*/OOK_Outside);
     }
 
-    NestedNameSpecifierLoc QualifierLoc = SS.getWithLocInContext(Context);
     ElaboratedTypeKeyword Keyword
       = TypeWithKeyword::getKeywordForTagTypeKind(Kind);
     QualType T = CheckTypenameType(Keyword, TagLoc, QualifierLoc,
@@ -17452,6 +17447,22 @@ DeclResult Sema::ActOnTemplatedFriendTag(
   assert(SS.isNotEmpty() && "valid templated tag with no SS and no direct?");
 
 
+  // CWG 2917: if it (= the friend-type-specifier) is a pack expansion
+  // (13.7.4 [temp.variadic]), any packs expanded by that pack expansion
+  // shall not have been introduced by the template-declaration.
+  SmallVector<UnexpandedParameterPack, 1> Unexpanded;
+  collectUnexpandedParameterPacks(QualifierLoc, Unexpanded);
+  unsigned FriendDeclDepth = TempParamLists.front()->getDepth();
+  for (UnexpandedParameterPack& U : Unexpanded) {
+    if (getDepthAndIndex(U).first >= FriendDeclDepth) {
+      auto *ND = U.first.dyn_cast<NamedDecl *>();
+      if (!ND)
+        ND = U.first.get<const TemplateTypeParmType *>()->getDecl();
+      Diag(U.second, diag::friend_template_decl_malformed_pack_expansion)
+          << ND->getDeclName() << SourceRange(SS.getBeginLoc(), EllipsisLoc);
+      return true;
+    }
+  }
 
   // Handle the case of a templated-scope friend class.  e.g.
   //   template <class T> class A<T>::B;
diff --git a/clang/test/CXX/drs/cwg29xx.cpp b/clang/test/CXX/drs/cwg29xx.cpp
index df48ce637a1b43..8cac9f283980b6 100644
--- a/clang/test/CXX/drs/cwg29xx.cpp
+++ b/clang/test/CXX/drs/cwg29xx.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -std=c++98 -pedantic-errors -verify=expected %s
+// RUN: %clang_cc1 -std=c++98 -pedantic-errors -verify=expected,cxx98 %s
 // RUN: %clang_cc1 -std=c++11 -pedantic-errors -verify=expected %s
 // RUN: %clang_cc1 -std=c++14 -pedantic-errors -verify=expected %s
 // RUN: %clang_cc1 -std=c++17 -pedantic-errors -verify=expected %s
@@ -10,8 +10,16 @@ namespace cwg2917 { // cwg2917: 20 open 2024-07-30
 template <typename>
 class Foo;
 
+template<class ...> // cxx98-error {{variadic templates are a C++11 extension}}
 struct C {
+  struct Nested { };
+};
+
+struct S {
   template <typename>
   friend class Foo, int; // expected-error {{a friend declaration that befriends a template must contain exactly one type-specifier}}
+
+  template <typename ...Ts> // cxx98-error {{variadic templates are a C++11 extension}}
+  friend class C<Ts>::Nested...; // expected-error {{friend declaration expands pack 'Ts' that is declared it its own template parameter list}}
 };
 } // namespace cwg2917
diff --git a/clang/test/Parser/cxx2c-variadic-friends.cpp b/clang/test/Parser/cxx2c-variadic-friends.cpp
index 027fe6d3abca49..b7da3e61104812 100644
--- a/clang/test/Parser/cxx2c-variadic-friends.cpp
+++ b/clang/test/Parser/cxx2c-variadic-friends.cpp
@@ -24,6 +24,9 @@ struct C { template<class T> class Nested; };
 template <typename, typename>
 struct D { template<class T> class Nested; };
 
+template <bool>
+struct E { template<class T> class Nested; };
+
 template<class... Ts> // expected-note {{template parameter is declared here}}
 struct VS {
   friend Ts...;
@@ -39,7 +42,6 @@ struct VS {
 
   template<class... Us> // expected-note {{is declared here}}
   friend class Us...; // expected-error {{declaration of 'Us' shadows template parameter}}
-                      // expected-error at -1 {{pack expansion does not contain any unexpanded parameter packs}}
 
   template<class U>
   friend class C<Ts>::template Nested<U>...; // expected-error {{cannot specialize a dependent template}}
@@ -47,21 +49,20 @@ struct VS {
   template<class... Us>
   friend class C<Ts...>::template Nested<Us>...; // expected-error {{cannot specialize a dependent template}}
 
-  // FIXME: Should be valid, but we currently can’t handle packs in NNSs.
+  // Nonsense (see CWG 2917).
+  template<class... Us>
+  friend class C<Us>::Nested...; // expected-error {{friend declaration expands pack 'Us' that is declared it its own template parameter list}}
+
+  template<bool... Bs>
+  friend class E<Bs>::Nested...; // expected-error {{friend declaration expands pack 'Bs' that is declared it its own template parameter list}}
+
+  // FIXME: Both of these should be valid, but we can't handle these at
+  // the moment because the NNS is dependent.
   template<class ...T>
-  friend class TS<Ts>::Nested...; // expected-error {{pack expansion does not contain any unexpanded parameter packs}}
-                                  // expected-warning at -1 {{dependent nested name specifier 'TS<Ts>::' for friend template declaration is not supported; ignoring this friend declaration}}
+  friend class TS<Ts>::Nested...; // expected-warning {{dependent nested name specifier 'TS<Ts>::' for friend template declaration is not supported; ignoring this friend declaration}}
 
-  // FIXME: This I legitimately have no idea what to do with. I *think* it might
-  // be well-formed by the same logic as the previous one?
   template<class T>
-  friend class D<T, Ts>::Nested...; // expected-error {{pack expansion does not contain any unexpanded parameter packs}}
-                                    // expected-warning at -1 {{dependent nested name specifier 'D<T, Ts>::' for friend class declaration is not supported; turning off access control for 'VS'}}
-
-  // FIXME: Ill-formed... probably?
-  template<class... Us>
-  friend class C<Us>::Nested...; // expected-error {{pack expansion does not contain any unexpanded parameter packs}}
-                                 // expected-warning at -1 {{dependent nested name specifier 'C<Us>::' for friend class declaration is not supported; turning off access control for 'VS'}}
+  friend class D<T, Ts>::Nested...; // expected-warning {{dependent nested name specifier 'D<T, Ts>::' for friend class declaration is not supported; turning off access control for 'VS'}}
 };
 
 namespace length_mismatch {

>From fdcac04ada6a8f3e100dc9556dc2ee82f1e98364 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Tue, 13 Aug 2024 13:14:31 +0200
Subject: [PATCH 20/20] clang-format

---
 clang/include/clang/AST/DeclFriend.h | 4 ++--
 clang/lib/Sema/SemaDeclCXX.cpp       | 3 +--
 2 files changed, 3 insertions(+), 4 deletions(-)

diff --git a/clang/include/clang/AST/DeclFriend.h b/clang/include/clang/AST/DeclFriend.h
index 50dadeede02d53..095f14a81fd574 100644
--- a/clang/include/clang/AST/DeclFriend.h
+++ b/clang/include/clang/AST/DeclFriend.h
@@ -157,8 +157,8 @@ class FriendDecl final
           (NumTPLists == 0) ? getFriendLoc()
                             : getTrailingObjects<TemplateParameterList *>()[0]
                                   ->getTemplateLoc();
-      SourceLocation EndL =
-          isPackExpansion() ? getEllipsisLoc() : TInfo->getTypeLoc().getEndLoc();
+      SourceLocation EndL = isPackExpansion() ? getEllipsisLoc()
+                                              : TInfo->getTypeLoc().getEndLoc();
       return SourceRange(StartL, EndL);
     }
 
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index fd0914d5a396ba..5288cf26e200a5 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -17446,14 +17446,13 @@ DeclResult Sema::ActOnTemplatedFriendTag(
 
   assert(SS.isNotEmpty() && "valid templated tag with no SS and no direct?");
 
-
   // CWG 2917: if it (= the friend-type-specifier) is a pack expansion
   // (13.7.4 [temp.variadic]), any packs expanded by that pack expansion
   // shall not have been introduced by the template-declaration.
   SmallVector<UnexpandedParameterPack, 1> Unexpanded;
   collectUnexpandedParameterPacks(QualifierLoc, Unexpanded);
   unsigned FriendDeclDepth = TempParamLists.front()->getDepth();
-  for (UnexpandedParameterPack& U : Unexpanded) {
+  for (UnexpandedParameterPack &U : Unexpanded) {
     if (getDepthAndIndex(U).first >= FriendDeclDepth) {
       auto *ND = U.first.dyn_cast<NamedDecl *>();
       if (!ND)



More information about the cfe-commits mailing list