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

via cfe-commits cfe-commits at lists.llvm.org
Wed Jul 31 20:31:03 PDT 2024


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang-modules

Author: None (Sirraide)

<details>
<summary>Changes</summary>

Implement P2893R3 ‘Variadic friends’ for C++26.

The implementation proper is mostly done, from what I can tell, but I have a few questions, so I thought I’d be best to list them all here (some are for me to investigate, some I’d appreciate it if anyone would happen to know the answer to them):
- [ ] I don’t know much about the `ASTImporter`, so I’m not entirely sure if I did everything right in there.
- [ ] Still need to add tests for [CWG 2917](https://cplusplus.github.io/CWG/issues/2917.html), which I filed while working on this, to the DR test suite proper. (@<!-- -->Endilll Sorry for asking this *again*, but do I need to run the script that updates the DR tests as part of this, or should that be done in a separate pr before this?)
- [ ] Do we want to expose this in earlier language modes? The syntax wasn’t valid before, so I *believe* there shouldn’t be an issue with that, but I’m not sure.
- [ ] There are some edge cases that mostly involve what we treat as unsupported friend declarations that I still need to either reject or also convert to unsupported friend declarations.
- [ ] Is there ever a situation that would cause us to have to instantiate a `FriendPackDecl` in the `TemplateDeclInstantiator`? We do that for `UsingPackDecl`s, but those are sufficiently different enough from the former that I’m not sure about it. I at least haven’t been able to come up w/ any cases that would cause us to try and instantiate one.
- [ ] The diagnostics for some invalid declarations (e.g. CWG 2917) could still be better imo.
- [ ] I had to add a hack to the decl printer to get e.g. `friend int, long;` to be printed like that instead of as separate declarations (which they currently are in the AST). Is that fine or is that too much of a hack?
- [ ] Whether an ellipsis is present after a friend-type-specifier is currently handled via a separate parameter in some of the Sema functions. It would be possible to instead store that information in the `DeclSpec`, but I haven’t written too much parser code in Clang before, and wasn’t sure how strict we are wrt putting things in that class because I don’t think the `...` is part of the specifier according to the grammar.

Thanks also to @<!-- -->zyn0217, who was working on this before me; I’ve taken over working on this since they didn’t have time for it anymore. I’ve incorporated parts of their WIP patch, so I’ll credit them as a co-author whenever this gets merged.

This closes #<!-- -->98587.


---

Patch is 43.60 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/101448.diff


27 Files Affected:

- (modified) clang/docs/ReleaseNotes.rst (+2) 
- (modified) clang/include/clang/AST/DeclFriend.h (+71-12) 
- (modified) clang/include/clang/AST/RecursiveASTVisitor.h (+2) 
- (modified) clang/include/clang/Basic/DeclNodes.td (+1) 
- (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+2) 
- (modified) clang/include/clang/Sema/Sema.h (+4-2) 
- (modified) clang/include/clang/Serialization/ASTBitCodes.h (+3) 
- (modified) clang/lib/AST/ASTImporter.cpp (+31-2) 
- (modified) clang/lib/AST/DeclBase.cpp (+1) 
- (modified) clang/lib/AST/DeclFriend.cpp (+27-7) 
- (modified) clang/lib/AST/DeclPrinter.cpp (+35-5) 
- (modified) clang/lib/AST/ODRHash.cpp (+1) 
- (modified) clang/lib/AST/TextNodeDumper.cpp (+2) 
- (modified) clang/lib/CodeGen/CGDecl.cpp (+1) 
- (modified) clang/lib/Frontend/InitPreprocessor.cpp (+5) 
- (modified) clang/lib/Parse/ParseDeclCXX.cpp (+89) 
- (modified) clang/lib/Sema/SemaDecl.cpp (+6-2) 
- (modified) clang/lib/Sema/SemaDeclCXX.cpp (+20-7) 
- (modified) clang/lib/Sema/SemaTemplateInstantiateDecl.cpp (+49-2) 
- (modified) clang/lib/Serialization/ASTCommon.cpp (+1) 
- (modified) clang/lib/Serialization/ASTReaderDecl.cpp (+12) 
- (modified) clang/lib/Serialization/ASTWriterDecl.cpp (+10) 
- (added) clang/test/AST/cxx2c-variadic-friends.cpp (+74) 
- (added) clang/test/Parser/cxx2c-variadic-friends.cpp (+60) 
- (added) clang/test/SemaCXX/cxx2c-variadic-friends.cpp (+136) 
- (modified) clang/tools/libclang/CIndex.cpp (+1) 
- (modified) clang/www/cxx_status.html (+1-1) 


``````````diff
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 3c2e0282d1c72..aa8bb903d3646 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/include/clang/AST/DeclFriend.h b/clang/include/clang/AST/DeclFriend.h
index 9789282f351a5..19dd531e89ccf 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,8 +147,24 @@ 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 (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();
@@ -158,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.
@@ -177,11 +190,57 @@ 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; }
 };
 
+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 dcf5dbf449f8b..e49fd3f58db57 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 48396e85c5ada..8464cce4a9ddf 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/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 581434d33c5c9..8daa2386ef64f 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 2ec6367eccea0..07c8c2cef9cc3 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/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h
index 5dd0ba33f8a9c..92e3fce78b682 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 103235547f482..46d07069af6ba 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);
@@ -4424,11 +4425,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());
@@ -4437,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 a1f70546bde42..ee856e73c0d62 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 04b9b93699f36..2662f3f634da6 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;
 }
@@ -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/AST/DeclPrinter.cpp b/clang/lib/AST/DeclPrinter.cpp
index 26773a69ab9ac..0b4faaf0b39ef 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())) {
@@ -885,6 +912,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 fbfe92318dc5e..65a02a6b66149 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 5ba9523504258..585d88e2e031e 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/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp
index 882dbad456379..4aa8e8f321aad 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/Frontend/InitPreprocessor.cpp b/clang/lib/Frontend/InitPreprocessor.cpp
index 17b9ca7cb9910..d17e213ea4d3f 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 tes...
[truncated]

``````````

</details>


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


More information about the cfe-commits mailing list