Skip to content

Commit 2b0a8fc

Browse files
Sirraidezyn0217
andauthored
[Clang] Implement C++26’s P2893R3 ‘Variadic friends’ (#101448)
Implement P2893R3 ‘Variadic friends’ for C++26. This closes #98587. Co-authored-by: Younan Zhang <[email protected]>
1 parent b5e63cc commit 2b0a8fc

28 files changed

+942
-170
lines changed

clang/docs/LanguageExtensions.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1505,6 +1505,7 @@ Attributes on Lambda-Expressions C+
15051505
Attributes on Structured Bindings __cpp_structured_bindings C++26 C++03
15061506
Pack Indexing __cpp_pack_indexing C++26 C++03
15071507
``= delete ("should have a reason");`` __cpp_deleted_function C++26 C++03
1508+
Variadic Friends __cpp_variadic_friend C++26 C++03
15081509
-------------------------------------------- -------------------------------- ------------- -------------
15091510
Designated initializers (N494) C99 C89
15101511
Array & element qualification (N2607) C23 C89

clang/docs/ReleaseNotes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,8 @@ C++2c Feature Support
126126
- Add ``__builtin_is_virtual_base_of`` intrinsic, which supports
127127
`P2985R0 A type trait for detecting virtual base classes <https://wg21.link/p2985r0>`_
128128

129+
- Implemented `P2893R3 Variadic Friends <https://wg21.link/P2893>`_
130+
129131
Resolutions to C++ Defect Reports
130132
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
131133

clang/include/clang/AST/DeclFriend.h

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ class FriendDecl final
7070
// Location of the 'friend' specifier.
7171
SourceLocation FriendLoc;
7272

73+
// Location of the '...', if present.
74+
SourceLocation EllipsisLoc;
75+
7376
/// True if this 'friend' declaration is unsupported. Eventually we
7477
/// will support every possible friend declaration, but for now we
7578
/// silently ignore some and set this flag to authorize all access.
@@ -82,10 +85,11 @@ class FriendDecl final
8285
unsigned NumTPLists : 31;
8386

8487
FriendDecl(DeclContext *DC, SourceLocation L, FriendUnion Friend,
85-
SourceLocation FriendL,
88+
SourceLocation FriendL, SourceLocation EllipsisLoc,
8689
ArrayRef<TemplateParameterList *> FriendTypeTPLists)
8790
: Decl(Decl::Friend, DC, L), Friend(Friend), FriendLoc(FriendL),
88-
UnsupportedFriend(false), NumTPLists(FriendTypeTPLists.size()) {
91+
EllipsisLoc(EllipsisLoc), UnsupportedFriend(false),
92+
NumTPLists(FriendTypeTPLists.size()) {
8993
for (unsigned i = 0; i < NumTPLists; ++i)
9094
getTrailingObjects<TemplateParameterList *>()[i] = FriendTypeTPLists[i];
9195
}
@@ -110,7 +114,7 @@ class FriendDecl final
110114

111115
static FriendDecl *
112116
Create(ASTContext &C, DeclContext *DC, SourceLocation L, FriendUnion Friend_,
113-
SourceLocation FriendL,
117+
SourceLocation FriendL, SourceLocation EllipsisLoc = {},
114118
ArrayRef<TemplateParameterList *> FriendTypeTPLists = std::nullopt);
115119
static FriendDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID,
116120
unsigned FriendTypeNumTPLists);
@@ -143,8 +147,24 @@ class FriendDecl final
143147
return FriendLoc;
144148
}
145149

150+
/// Retrieves the location of the '...', if present.
151+
SourceLocation getEllipsisLoc() const { return EllipsisLoc; }
152+
146153
/// Retrieves the source range for the friend declaration.
147154
SourceRange getSourceRange() const override LLVM_READONLY {
155+
if (TypeSourceInfo *TInfo = getFriendType()) {
156+
SourceLocation StartL =
157+
(NumTPLists == 0) ? getFriendLoc()
158+
: getTrailingObjects<TemplateParameterList *>()[0]
159+
->getTemplateLoc();
160+
SourceLocation EndL = isPackExpansion() ? getEllipsisLoc()
161+
: TInfo->getTypeLoc().getEndLoc();
162+
return SourceRange(StartL, EndL);
163+
}
164+
165+
if (isPackExpansion())
166+
return SourceRange(getFriendLoc(), getEllipsisLoc());
167+
148168
if (NamedDecl *ND = getFriendDecl()) {
149169
if (const auto *FD = dyn_cast<FunctionDecl>(ND))
150170
return FD->getSourceRange();
@@ -158,15 +178,8 @@ class FriendDecl final
158178
}
159179
return SourceRange(getFriendLoc(), ND->getEndLoc());
160180
}
161-
else if (TypeSourceInfo *TInfo = getFriendType()) {
162-
SourceLocation StartL =
163-
(NumTPLists == 0) ? getFriendLoc()
164-
: getTrailingObjects<TemplateParameterList *>()[0]
165-
->getTemplateLoc();
166-
return SourceRange(StartL, TInfo->getTypeLoc().getEndLoc());
167-
}
168-
else
169-
return SourceRange(getFriendLoc(), getLocation());
181+
182+
return SourceRange(getFriendLoc(), getLocation());
170183
}
171184

172185
/// Determines if this friend kind is unsupported.
@@ -177,6 +190,8 @@ class FriendDecl final
177190
UnsupportedFriend = Unsupported;
178191
}
179192

193+
bool isPackExpansion() const { return EllipsisLoc.isValid(); }
194+
180195
// Implement isa/cast/dyncast/etc.
181196
static bool classof(const Decl *D) { return classofKind(D->getKind()); }
182197
static bool classofKind(Kind K) { return K == Decl::Friend; }

clang/include/clang/Basic/DiagnosticParseKinds.td

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -965,6 +965,12 @@ def warn_cxx23_delete_with_message : Warning<
965965
"'= delete' with a message is incompatible with C++ standards before C++2c">,
966966
DefaultIgnore, InGroup<CXXPre26Compat>;
967967

968+
def ext_variadic_friends : ExtWarn<
969+
"variadic 'friend' declarations are a C++2c extension">, InGroup<CXX26>;
970+
def warn_cxx23_variadic_friends : Warning<
971+
"variadic 'friend' declarations are incompatible with C++ standards before C++2c">,
972+
DefaultIgnore, InGroup<CXXPre26Compat>;
973+
968974
// C++11 default member initialization
969975
def ext_nonstatic_member_init : ExtWarn<
970976
"default member initializer for non-static data member is a C++11 "

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1741,6 +1741,10 @@ def ext_friend_tag_redecl_outside_namespace : ExtWarn<
17411741
"enclosing namespace is a Microsoft extension; add a nested name specifier">,
17421742
InGroup<MicrosoftUnqualifiedFriend>;
17431743
def err_pure_friend : Error<"friend declaration cannot have a pure-specifier">;
1744+
def err_friend_template_decl_multiple_specifiers: Error<
1745+
"a friend declaration that befriends a template must contain exactly one type-specifier">;
1746+
def friend_template_decl_malformed_pack_expansion : Error<
1747+
"friend declaration expands pack %0 that is declared it its own template parameter list">;
17441748

17451749
def err_invalid_base_in_interface : Error<
17461750
"interface type cannot inherit from "

clang/include/clang/Sema/Sema.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3800,7 +3800,8 @@ class Sema final : public SemaBase {
38003800
const ParsedAttributesView &DeclAttrs,
38013801
MultiTemplateParamsArg TemplateParams,
38023802
bool IsExplicitInstantiation,
3803-
RecordDecl *&AnonRecord);
3803+
RecordDecl *&AnonRecord,
3804+
SourceLocation EllipsisLoc = {});
38043805

38053806
/// BuildAnonymousStructOrUnion - Handle the declaration of an
38063807
/// anonymous structure or union. Anonymous unions are a C++ feature
@@ -5538,7 +5539,8 @@ class Sema final : public SemaBase {
55385539
/// parameters present at all, require proper matching, i.e.
55395540
/// template <> template \<class T> friend class A<int>::B;
55405541
Decl *ActOnFriendTypeDecl(Scope *S, const DeclSpec &DS,
5541-
MultiTemplateParamsArg TemplateParams);
5542+
MultiTemplateParamsArg TemplateParams,
5543+
SourceLocation EllipsisLoc);
55425544
NamedDecl *ActOnFriendFunctionDecl(Scope *S, Declarator &D,
55435545
MultiTemplateParamsArg TemplateParams);
55445546

@@ -5852,6 +5854,7 @@ class Sema final : public SemaBase {
58525854
unsigned TagSpec, SourceLocation TagLoc,
58535855
CXXScopeSpec &SS, IdentifierInfo *Name,
58545856
SourceLocation NameLoc,
5857+
SourceLocation EllipsisLoc,
58555858
const ParsedAttributesView &Attr,
58565859
MultiTemplateParamsArg TempParamLists);
58575860

clang/lib/AST/ASTImporter.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4429,11 +4429,14 @@ ExpectedDecl ASTNodeImporter::VisitFriendDecl(FriendDecl *D) {
44294429
auto FriendLocOrErr = import(D->getFriendLoc());
44304430
if (!FriendLocOrErr)
44314431
return FriendLocOrErr.takeError();
4432+
auto EllipsisLocOrErr = import(D->getEllipsisLoc());
4433+
if (!EllipsisLocOrErr)
4434+
return EllipsisLocOrErr.takeError();
44324435

44334436
FriendDecl *FrD;
44344437
if (GetImportedOrCreateDecl(FrD, D, Importer.getToContext(), DC,
4435-
*LocationOrErr, ToFU,
4436-
*FriendLocOrErr, ToTPLists))
4438+
*LocationOrErr, ToFU, *FriendLocOrErr,
4439+
*EllipsisLocOrErr, ToTPLists))
44374440
return FrD;
44384441

44394442
FrD->setAccess(D->getAccess());

clang/lib/AST/DeclFriend.cpp

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,11 @@ FriendDecl *FriendDecl::getNextFriendSlowCase() {
3131
NextFriend.get(getASTContext().getExternalSource()));
3232
}
3333

34-
FriendDecl *FriendDecl::Create(ASTContext &C, DeclContext *DC,
35-
SourceLocation L,
36-
FriendUnion Friend,
37-
SourceLocation FriendL,
38-
ArrayRef<TemplateParameterList *> FriendTypeTPLists) {
34+
FriendDecl *
35+
FriendDecl::Create(ASTContext &C, DeclContext *DC, SourceLocation L,
36+
FriendUnion Friend, SourceLocation FriendL,
37+
SourceLocation EllipsisLoc,
38+
ArrayRef<TemplateParameterList *> FriendTypeTPLists) {
3939
#ifndef NDEBUG
4040
if (Friend.is<NamedDecl *>()) {
4141
const auto *D = Friend.get<NamedDecl*>();
@@ -56,8 +56,8 @@ FriendDecl *FriendDecl::Create(ASTContext &C, DeclContext *DC,
5656
std::size_t Extra =
5757
FriendDecl::additionalSizeToAlloc<TemplateParameterList *>(
5858
FriendTypeTPLists.size());
59-
auto *FD = new (C, DC, Extra) FriendDecl(DC, L, Friend, FriendL,
60-
FriendTypeTPLists);
59+
auto *FD = new (C, DC, Extra)
60+
FriendDecl(DC, L, Friend, FriendL, EllipsisLoc, FriendTypeTPLists);
6161
cast<CXXRecordDecl>(DC)->pushFriendDecl(FD);
6262
return FD;
6363
}

clang/lib/AST/DeclPrinter.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -868,7 +868,7 @@ void DeclPrinter::VisitFriendDecl(FriendDecl *D) {
868868
for (unsigned i = 0; i < NumTPLists; ++i)
869869
printTemplateParameters(D->getFriendTypeTemplateParameterList(i));
870870
Out << "friend ";
871-
Out << " " << TSI->getType().getAsString(Policy);
871+
Out << TSI->getType().getAsString(Policy);
872872
}
873873
else if (FunctionDecl *FD =
874874
dyn_cast<FunctionDecl>(D->getFriendDecl())) {
@@ -885,6 +885,9 @@ void DeclPrinter::VisitFriendDecl(FriendDecl *D) {
885885
Out << "friend ";
886886
VisitRedeclarableTemplateDecl(CTD);
887887
}
888+
889+
if (D->isPackExpansion())
890+
Out << "...";
888891
}
889892

890893
void DeclPrinter::VisitFieldDecl(FieldDecl *D) {

clang/lib/AST/JSONNodeDumper.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1090,6 +1090,7 @@ void JSONNodeDumper::VisitAccessSpecDecl(const AccessSpecDecl *ASD) {
10901090
void JSONNodeDumper::VisitFriendDecl(const FriendDecl *FD) {
10911091
if (const TypeSourceInfo *T = FD->getFriendType())
10921092
JOS.attribute("type", createQualType(T->getType()));
1093+
attributeOnlyIfTrue("isPackExpansion", FD->isPackExpansion());
10931094
}
10941095

10951096
void JSONNodeDumper::VisitObjCIvarDecl(const ObjCIvarDecl *D) {

0 commit comments

Comments
 (0)