[clang] [Clang] Implement C++26’s P2893R3 ‘Variadic friends’ (PR #101448)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Jul 31 20:30:34 PDT 2024
https://github.com/Sirraide created https://github.com/llvm/llvm-project/pull/101448
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.
>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 1/5] [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 9789282f351a5..1c2c86d3ef384 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 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/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 103235547f482..b0043eb876183 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 04b9b93699f36..8b285bfce8d52 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 26773a69ab9ac..1a8ac103841ec 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 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/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index ce827c689beb7..d5c4390e221e5 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 694a754646f27..3b8c07a893ece 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 5782daa041f32..f19a7f4c636a0 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 f93cd113988ae..879b32a6f4f5e 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 31ab6c651d59f..5791cef8f0888 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 17c774038571e..8862ced487d13 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 0000000000000..dfbe9ad1f6f68
--- /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 0000000000000..85f696ebe2837
--- /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 2/5] [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 1c2c86d3ef384..19dd531e89ccf 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 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/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 b0043eb876183..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);
@@ -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 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 8b285bfce8d52..2662f3f634da6 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 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/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 879b32a6f4f5e..66ecee046c3b4 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 444a8a3d3a514..47ceb03b4460b 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 5791cef8f0888..c23cb2a6b48ef 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 8862ced487d13..56cb2c63b1c65 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 dfbe9ad1f6f68..3373cf89e72fb 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 0000000000000..f1213ce3e7e81
--- /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 937d7ff09e4ee..cda7f6e90a151 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 3/5] [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 1a8ac103841ec..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())) {
diff --git a/clang/test/Parser/cxx2c-variadic-friends.cpp b/clang/test/Parser/cxx2c-variadic-friends.cpp
index 85f696ebe2837..ab788085bbe19 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 f1213ce3e7e81..f6a249c4b99b2 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 4/5] [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 f19a7f4c636a0..7e66c66d1cd82 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 0000000000000..2d5069196ab39
--- /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 3373cf89e72fb..0000000000000
--- 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 ab788085bbe19..9a9b82ab047e6 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 5/5] 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 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/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 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 9a9b82ab047e6..ac99e40fc6b8c 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 a6ded8be3ae9e..faee8b578b624 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>
More information about the cfe-commits
mailing list