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

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

This closes #98587.

Co-authored-by: Younan Zhang <zyn7109@gmail.com>
This commit is contained in:
Sirraide 2024-08-15 21:16:30 +02:00 committed by GitHub
parent b5e63cc533
commit 2b0a8fcf70
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
28 changed files with 942 additions and 170 deletions

View File

@ -1505,6 +1505,7 @@ Attributes on Lambda-Expressions C+
Attributes on Structured Bindings __cpp_structured_bindings C++26 C++03
Pack Indexing __cpp_pack_indexing C++26 C++03
``= delete ("should have a reason");`` __cpp_deleted_function C++26 C++03
Variadic Friends __cpp_variadic_friend C++26 C++03
-------------------------------------------- -------------------------------- ------------- -------------
Designated initializers (N494) C99 C89
Array & element qualification (N2607) C23 C89

View File

@ -126,6 +126,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
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

View File

@ -70,6 +70,9 @@ private:
// 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 @@ private:
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 @@ public:
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 @@ public:
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 = isPackExpansion() ? getEllipsisLoc()
: TInfo->getTypeLoc().getEndLoc();
return SourceRange(StartL, EndL);
}
if (isPackExpansion())
return SourceRange(getFriendLoc(), getEllipsisLoc());
if (NamedDecl *ND = getFriendDecl()) {
if (const auto *FD = dyn_cast<FunctionDecl>(ND))
return FD->getSourceRange();
@ -158,15 +178,8 @@ public:
}
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,6 +190,8 @@ public:
UnsupportedFriend = Unsupported;
}
bool isPackExpansion() 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; }

View File

@ -965,6 +965,12 @@ def warn_cxx23_delete_with_message : Warning<
"'= delete' with a message is incompatible with C++ standards before C++2c">,
DefaultIgnore, InGroup<CXXPre26Compat>;
def ext_variadic_friends : ExtWarn<
"variadic 'friend' declarations are a C++2c extension">, InGroup<CXX26>;
def warn_cxx23_variadic_friends : Warning<
"variadic 'friend' declarations are incompatible with C++ standards before C++2c">,
DefaultIgnore, InGroup<CXXPre26Compat>;
// C++11 default member initialization
def ext_nonstatic_member_init : ExtWarn<
"default member initializer for non-static data member is a C++11 "

View File

@ -1741,6 +1741,10 @@ 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 friend_template_decl_malformed_pack_expansion : Error<
"friend declaration expands pack %0 that is declared it its own template parameter list">;
def err_invalid_base_in_interface : Error<
"interface type cannot inherit from "

View File

@ -3800,7 +3800,8 @@ public:
const ParsedAttributesView &DeclAttrs,
MultiTemplateParamsArg TemplateParams,
bool IsExplicitInstantiation,
RecordDecl *&AnonRecord);
RecordDecl *&AnonRecord,
SourceLocation EllipsisLoc = {});
/// BuildAnonymousStructOrUnion - Handle the declaration of an
/// anonymous structure or union. Anonymous unions are a C++ feature
@ -5538,7 +5539,8 @@ public:
/// 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 EllipsisLoc);
NamedDecl *ActOnFriendFunctionDecl(Scope *S, Declarator &D,
MultiTemplateParamsArg TemplateParams);
@ -5852,6 +5854,7 @@ public:
unsigned TagSpec, SourceLocation TagLoc,
CXXScopeSpec &SS, IdentifierInfo *Name,
SourceLocation NameLoc,
SourceLocation EllipsisLoc,
const ParsedAttributesView &Attr,
MultiTemplateParamsArg TempParamLists);

View File

@ -4429,11 +4429,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());

View File

@ -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;
}

View File

@ -868,7 +868,7 @@ void DeclPrinter::VisitFriendDecl(FriendDecl *D) {
for (unsigned i = 0; i < NumTPLists; ++i)
printTemplateParameters(D->getFriendTypeTemplateParameterList(i));
Out << "friend ";
Out << " " << TSI->getType().getAsString(Policy);
Out << TSI->getType().getAsString(Policy);
}
else if (FunctionDecl *FD =
dyn_cast<FunctionDecl>(D->getFriendDecl())) {
@ -885,6 +885,9 @@ void DeclPrinter::VisitFriendDecl(FriendDecl *D) {
Out << "friend ";
VisitRedeclarableTemplateDecl(CTD);
}
if (D->isPackExpansion())
Out << "...";
}
void DeclPrinter::VisitFieldDecl(FieldDecl *D) {

View File

@ -1090,6 +1090,7 @@ void JSONNodeDumper::VisitAccessSpecDecl(const AccessSpecDecl *ASD) {
void JSONNodeDumper::VisitFriendDecl(const FriendDecl *FD) {
if (const TypeSourceInfo *T = FD->getFriendType())
JOS.attribute("type", createQualType(T->getType()));
attributeOnlyIfTrue("isPackExpansion", FD->isPackExpansion());
}
void JSONNodeDumper::VisitObjCIvarDecl(const ObjCIvarDecl *D) {

View File

@ -461,6 +461,7 @@ public:
} else {
AddDecl(D->getFriendDecl());
}
Hash.AddBoolean(D->isPackExpansion());
}
void VisitTemplateTypeParmDecl(const TemplateTypeParmDecl *D) {

View File

@ -2697,6 +2697,8 @@ void TextNodeDumper::VisitAccessSpecDecl(const AccessSpecDecl *D) {
void TextNodeDumper::VisitFriendDecl(const FriendDecl *D) {
if (TypeSourceInfo *T = D->getFriendType())
dumpType(T->getType());
if (D->isPackExpansion())
OS << "...";
}
void TextNodeDumper::VisitObjCIvarDecl(const ObjCIvarDecl *D) {

View File

@ -765,6 +765,7 @@ static void InitializeCPlusPlusFeatureTestMacros(const LangOptions &LangOpts,
// C++26 features supported in earlier language modes.
Builder.defineMacro("__cpp_pack_indexing", "202311L");
Builder.defineMacro("__cpp_deleted_function", "202403L");
Builder.defineMacro("__cpp_variadic_friend", "202403L");
if (LangOpts.Char8)
Builder.defineMacro("__cpp_char8_t", "202207L");

View File

@ -452,7 +452,7 @@ Decl *Parser::ParseLinkage(ParsingDeclSpec &DS, DeclaratorContext Context) {
///
/// export-function-declaration:
/// 'export' function-declaration
///
///
/// export-declaration-group:
/// 'export' '{' function-declaration-seq[opt] '}'
///
@ -2007,9 +2007,16 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
const PrintingPolicy &Policy = Actions.getASTContext().getPrintingPolicy();
TagUseKind TUK;
if (isDefiningTypeSpecifierContext(DSC, getLangOpts().CPlusPlus) ==
AllowDefiningTypeSpec::No ||
(getLangOpts().OpenMP && OpenMPDirectiveParsing))
// C++26 [class.mem.general]p10: If a name-declaration matches the
// syntactic requirements of friend-type-declaration, it is a
// friend-type-declaration.
if (getLangOpts().CPlusPlus && DS.isFriendSpecifiedFirst() &&
Tok.isOneOf(tok::comma, tok::ellipsis))
TUK = TagUseKind::Friend;
else if (isDefiningTypeSpecifierContext(DSC, getLangOpts().CPlusPlus) ==
AllowDefiningTypeSpec::No ||
(getLangOpts().OpenMP && OpenMPDirectiveParsing))
TUK = TagUseKind::Reference;
else if (Tok.is(tok::l_brace) ||
(DSC != DeclSpecContext::DSC_association &&
@ -2241,9 +2248,28 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
diag::err_keyword_not_allowed,
/*DiagnoseEmptyAttrs=*/true);
// Consume '...' first so we error on the ',' after it if there is one.
SourceLocation EllipsisLoc;
TryConsumeToken(tok::ellipsis, EllipsisLoc);
// CWG 2917: In a template-declaration whose declaration is a
// friend-type-declaration, the friend-type-specifier-list shall
// consist of exactly one friend-type-specifier.
//
// Essentially, the following is obviously nonsense, so disallow it:
//
// template <typename>
// friend class S, int;
//
if (Tok.is(tok::comma)) {
Diag(Tok.getLocation(),
diag::err_friend_template_decl_multiple_specifiers);
SkipUntil(tok::semi, StopBeforeMatch);
}
TagOrTempResult = Actions.ActOnTemplatedFriendTag(
getCurScope(), DS.getFriendSpecLoc(), TagType, StartLoc, SS, Name,
NameLoc, attrs,
NameLoc, EllipsisLoc, attrs,
MultiTemplateParamsArg(TemplateParams ? &(*TemplateParams)[0] : nullptr,
TemplateParams ? TemplateParams->size() : 0));
} else {
@ -2818,6 +2844,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
@ -2850,6 +2877,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) {
@ -3051,6 +3090,55 @@ 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.
if (DS.isFriendSpecifiedFirst() && Tok.isOneOf(tok::comma, tok::ellipsis)) {
Diag(Tok.getLocation(), getLangOpts().CPlusPlus26
? diag::warn_cxx23_variadic_friends
: diag::ext_variadic_friends);
SourceLocation FriendLoc = DS.getFriendSpecLoc();
SmallVector<Decl *> Decls;
// Handles a single friend-type-specifier.
auto ParsedFriendDecl = [&](ParsingDeclSpec &DeclSpec) {
SourceLocation VariadicLoc;
TryConsumeToken(tok::ellipsis, VariadicLoc);
RecordDecl *AnonRecord = nullptr;
Decl *D = Actions.ParsedFreeStandingDeclSpec(
getCurScope(), AS, DeclSpec, DeclAttrs, TemplateParams, false,
AnonRecord, VariadicLoc);
DeclSpec.complete(D);
if (!D) {
SkipUntil(tok::semi, tok::r_brace);
return true;
}
Decls.push_back(D);
return false;
};
if (ParsedFriendDecl(DS))
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)

View File

@ -5000,7 +5000,8 @@ Decl *Sema::ParsedFreeStandingDeclSpec(Scope *S, AccessSpecifier AS,
const ParsedAttributesView &DeclAttrs,
MultiTemplateParamsArg TemplateParams,
bool IsExplicitInstantiation,
RecordDecl *&AnonRecord) {
RecordDecl *&AnonRecord,
SourceLocation EllipsisLoc) {
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, EllipsisLoc);
}
assert(EllipsisLoc.isInvalid() &&
"Friend ellipsis but not friend-specified?");
// Track whether this decl-specifier declares anything.
bool DeclaresAnything = true;

View File

@ -17386,7 +17386,8 @@ Decl *Sema::BuildStaticAssertDeclaration(SourceLocation StaticAssertLoc,
DeclResult Sema::ActOnTemplatedFriendTag(
Scope *S, SourceLocation FriendLoc, unsigned TagSpec, SourceLocation TagLoc,
CXXScopeSpec &SS, IdentifierInfo *Name, SourceLocation NameLoc,
const ParsedAttributesView &Attr, MultiTemplateParamsArg TempParamLists) {
SourceLocation EllipsisLoc, const ParsedAttributesView &Attr,
MultiTemplateParamsArg TempParamLists) {
TagTypeKind Kind = TypeWithKeyword::getTagTypeKindForTypeSpec(TagSpec);
bool IsMemberSpecialization = false;
@ -17430,6 +17431,7 @@ DeclResult Sema::ActOnTemplatedFriendTag(
// If it's explicit specializations all the way down, just forget
// about the template header and build an appropriate non-templated
// friend. TODO: for source fidelity, remember the headers.
NestedNameSpecifierLoc QualifierLoc = SS.getWithLocInContext(Context);
if (isAllExplicitSpecializations) {
if (SS.isEmpty()) {
bool Owned = false;
@ -17445,7 +17447,6 @@ DeclResult Sema::ActOnTemplatedFriendTag(
/*IsTemplateParamOrArg=*/false, /*OOK=*/OOK_Outside);
}
NestedNameSpecifierLoc QualifierLoc = SS.getWithLocInContext(Context);
ElaboratedTypeKeyword Keyword
= TypeWithKeyword::getKeywordForTagTypeKind(Kind);
QualType T = CheckTypenameType(Keyword, TagLoc, QualifierLoc,
@ -17467,8 +17468,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, TempParamLists);
Friend->setAccess(AS_public);
CurContext->addDecl(Friend);
return Friend;
@ -17476,7 +17478,22 @@ DeclResult Sema::ActOnTemplatedFriendTag(
assert(SS.isNotEmpty() && "valid templated tag with no SS and no direct?");
// CWG 2917: if it (= the friend-type-specifier) is a pack expansion
// (13.7.4 [temp.variadic]), any packs expanded by that pack expansion
// shall not have been introduced by the template-declaration.
SmallVector<UnexpandedParameterPack, 1> Unexpanded;
collectUnexpandedParameterPacks(QualifierLoc, Unexpanded);
unsigned FriendDeclDepth = TempParamLists.front()->getDepth();
for (UnexpandedParameterPack &U : Unexpanded) {
if (getDepthAndIndex(U).first >= FriendDeclDepth) {
auto *ND = U.first.dyn_cast<NamedDecl *>();
if (!ND)
ND = U.first.get<const TemplateTypeParmType *>()->getDecl();
Diag(U.second, diag::friend_template_decl_malformed_pack_expansion)
<< ND->getDeclName() << SourceRange(SS.getBeginLoc(), EllipsisLoc);
return true;
}
}
// Handle the case of a templated-scope friend class. e.g.
// template <class T> class A<T>::B;
@ -17491,8 +17508,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, TempParamLists);
Friend->setAccess(AS_public);
Friend->setUnsupportedFriend(true);
CurContext->addDecl(Friend);
@ -17500,7 +17518,8 @@ DeclResult Sema::ActOnTemplatedFriendTag(
}
Decl *Sema::ActOnFriendTypeDecl(Scope *S, const DeclSpec &DS,
MultiTemplateParamsArg TempParams) {
MultiTemplateParamsArg TempParams,
SourceLocation EllipsisLoc) {
SourceLocation Loc = DS.getBeginLoc();
SourceLocation FriendLoc = DS.getFriendSpecLoc();
@ -17541,8 +17560,18 @@ 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.
bool Invalid = false;
if (EllipsisLoc.isInvalid() &&
DiagnoseUnexpandedParameterPack(Loc, TSI, UPPC_FriendDeclaration))
return nullptr;
if (EllipsisLoc.isValid() &&
!TSI->getType()->containsUnexpandedParameterPack()) {
Diag(EllipsisLoc, diag::err_pack_expansion_without_parameter_packs)
<< TSI->getTypeLoc().getSourceRange();
Invalid = true;
}
if (!T->isElaboratedTypeSpecifier()) {
if (TempParams.size()) {
@ -17588,11 +17617,12 @@ 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
D = FriendDecl::Create(Context, CurContext, TSI->getTypeLoc().getBeginLoc(),
TSI, FriendLoc);
TSI, FriendLoc, EllipsisLoc);
if (!D)
return nullptr;
@ -17600,6 +17630,9 @@ Decl *Sema::ActOnFriendTypeDecl(Scope *S, const DeclSpec &DS,
D->setAccess(AS_public);
CurContext->addDecl(D);
if (Invalid)
D->setInvalidDecl();
return D;
}

View File

@ -1442,8 +1442,47 @@ Decl *TemplateDeclInstantiator::VisitFriendDecl(FriendDecl *D) {
if (D->isUnsupportedFriend()) {
InstTy = Ty;
} else {
InstTy = SemaRef.SubstType(Ty, TemplateArgs,
D->getLocation(), DeclarationName());
if (D->isPackExpansion()) {
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 variadic friend decl");
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);
}
// Just drop this node; we have no use for it anymore.
return nullptr;
}
}
InstTy = SemaRef.SubstType(Ty, TemplateArgs, D->getLocation(),
DeclarationName());
}
if (!InstTy)
return nullptr;

View File

@ -2361,6 +2361,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) {

View File

@ -1660,6 +1660,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;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,81 @@
// 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;
// PRINT-NEXT: friend long;
friend int, long;
// CHECK-NEXT: FriendDecl {{.*}} 'int'
// CHECK-NEXT: FriendDecl {{.*}} 'long'
// CHECK-NEXT: FriendDecl {{.*}} 'char'
// PRINT-NEXT: friend int;
// PRINT-NEXT: friend long;
// PRINT-NEXT: friend 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;
// PRINT-NEXT: friend S;
// PRINT-NEXT: friend 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'...
// CHECK-NEXT: FriendDecl {{.*}} 'long'
// CHECK-NEXT: FriendDecl {{.*}} 'Pack'...
// PRINT-NEXT: friend Pack...;
// PRINT-NEXT: friend long;
// PRINT-NEXT: friend Pack...;
friend Pack..., long, Pack...;
// CHECK-NEXT: FriendDecl {{.*}} 'TS<Pack>'...
// 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>'...
// PRINT-NEXT: friend class C<Ts>...;
friend class C<Ts>...;
// CHECK-NEXT: FriendDecl {{.*}} 'class N::C<Ts>':'C<Ts>'...
// PRINT-NEXT: friend class N::C<Ts>...
friend class N::C<Ts>...;
};
}

View File

@ -0,0 +1,25 @@
// RUN: %clang_cc1 -std=c++98 -pedantic-errors -verify=expected,cxx98 %s
// RUN: %clang_cc1 -std=c++11 -pedantic-errors -verify=expected %s
// RUN: %clang_cc1 -std=c++14 -pedantic-errors -verify=expected %s
// RUN: %clang_cc1 -std=c++17 -pedantic-errors -verify=expected %s
// RUN: %clang_cc1 -std=c++20 -pedantic-errors -verify=expected %s
// RUN: %clang_cc1 -std=c++23 -pedantic-errors -verify=expected %s
// RUN: %clang_cc1 -std=c++2c -pedantic-errors -verify=expected %s
namespace cwg2917 { // cwg2917: 20 open 2024-07-30
template <typename>
class Foo;
template<class ...> // cxx98-error {{variadic templates are a C++11 extension}}
struct C {
struct Nested { };
};
struct S {
template <typename>
friend class Foo, int; // expected-error {{a friend declaration that befriends a template must contain exactly one type-specifier}}
template <typename ...Ts> // cxx98-error {{variadic templates are a C++11 extension}}
friend class C<Ts>::Nested...; // expected-error {{friend declaration expands pack 'Ts' that is declared it its own template parameter list}}
};
} // namespace cwg2917

View File

@ -34,6 +34,10 @@
// --- C++26 features ---
#if check(variadic_friend, 202403, 202403, 202403, 202403, 202403, 202403, 202403)
#error "wrong value for __cpp_variadic_friend"
#endif
#if check(deleted_function, 202403, 202403, 202403, 202403, 202403, 202403, 202403)
#error "wrong value for __cpp_deleted_function"
#endif

View File

@ -0,0 +1,16 @@
// RUN: %clang_cc1 -std=c++2c -verify=compat -fsyntax-only -Wpre-c++26-compat %s
// RUN: %clang_cc1 -std=c++11 -verify=pre2c -fsyntax-only -Wc++26-extensions %s
struct S {
friend int, long, char; // compat-warning {{variadic 'friend' declarations are incompatible with C++ standards before C++2c}} \
// pre2c-warning {{variadic 'friend' declarations are a C++2c extension}}
};
template <typename ...Types>
struct TS {
friend Types...; // compat-warning {{variadic 'friend' declarations are incompatible with C++ standards before C++2c}} \
// pre2c-warning {{variadic 'friend' declarations are a C++2c extension}}
friend int, Types..., Types...; // compat-warning {{variadic 'friend' declarations are incompatible with C++ standards before C++2c}} \
// pre2c-warning {{variadic 'friend' declarations are a C++2c extension}}
};

View File

@ -0,0 +1,91 @@
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++2c %s
template <typename> struct TS; // #template
struct Errors {
friend int, int;
friend int, long, char;
// We simply diagnose and ignore the '...' here.
friend float...; // expected-error {{pack expansion does not contain any unexpanded parameter packs}}
friend short..., unsigned, unsigned short...; // expected-error 2 {{pack expansion does not contain any unexpanded parameter packs}}
template <typename>
friend struct TS, int; // expected-error {{a friend declaration that befriends a template must contain exactly one type-specifier}}
double friend; // expected-error {{'friend' must appear first in a non-function declaration}}
double friend, double; // expected-error {{expected member name or ';' after declaration specifiers}}
};
template <typename>
struct C { template<class T> class Nested; };
template <typename, typename>
struct D { template<class T> class Nested; };
template <bool>
struct E { template<class T> class Nested; };
template<class... Ts> // expected-note {{template parameter is declared here}}
struct VS {
friend Ts...;
friend class Ts...; // expected-error {{declaration of 'Ts' shadows template parameter}}
// expected-error@-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}}
template<class U>
friend class C<Ts>::template Nested<U>...; // expected-error {{cannot specialize a dependent template}}
template<class... Us>
friend class C<Ts...>::template Nested<Us>...; // expected-error {{cannot specialize a dependent template}}
// Nonsense (see CWG 2917).
template<class... Us>
friend class C<Us>::Nested...; // expected-error {{friend declaration expands pack 'Us' that is declared it its own template parameter list}}
template<bool... Bs>
friend class E<Bs>::Nested...; // expected-error {{friend declaration expands pack 'Bs' that is declared it its own template parameter list}}
// FIXME: Both of these should be valid, but we can't handle these at
// the moment because the NNS is dependent.
template<class ...T>
friend class TS<Ts>::Nested...; // expected-warning {{dependent nested name specifier 'TS<Ts>::' for friend template declaration is not supported; ignoring this friend declaration}}
template<class T>
friend class D<T, Ts>::Nested...; // expected-warning {{dependent nested name specifier 'D<T, Ts>::' for friend class declaration is not supported; turning off access control for 'VS'}}
};
namespace length_mismatch {
struct A {
template <typename...>
struct Nested {
struct Foo{};
};
};
template <typename ...Ts>
struct S {
template <typename ...Us>
struct T {
// expected-error@+2 {{pack expansion contains parameter packs 'Ts' and 'Us' that have different lengths (1 vs. 2)}}
// expected-error@+1 {{pack expansion contains parameter packs 'Ts' and 'Us' that have different lengths (2 vs. 1)}}
friend class Ts::template Nested<Us>::Foo...;
};
};
void f() {
S<A>::T<int> s;
S<A, A>::T<int, long> s2;
S<A>::T<int, long> s3; // expected-note {{in instantiation of}}
S<A, A>::T<int> s4; // expected-note {{in instantiation of}}
}
}

View File

@ -0,0 +1,156 @@
// RUN: %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
namespace template_template {
template <typename U, template <typename> typename... Friend>
class S {
friend class Friend<U>...;
static constexpr int a = 42;
};
template <typename U>
struct T {
static_assert(S<U, T>::a == 42);
static_assert(S<U, T>::a == 43); // expected-error {{static assertion failed due to requirement 'S<int, template_template::T>::a == 43'}} \
// expected-note {{expression evaluates to '42 == 43'}}
};
void f() {
T<int> t; // expected-note {{in instantiation of}}
}
}

View File

@ -17318,7 +17318,7 @@ objects</td>
<td><a href="https://cplusplus.github.io/CWG/issues/2917.html">2917</a></td>
<td>open</td>
<td>Disallow multiple <I>friend-type-specifier</I>s for a friend template</td>
<td align="center">Not resolved</td>
<td title="Clang 20 implements 2024-07-30 resolution" align="center">Not Resolved*</td>
</tr>
<tr class="open" id="2918">
<td><a href="https://cplusplus.github.io/CWG/issues/2918.html">2918</a></td>

View File

@ -202,7 +202,7 @@ C++23, informally referred to as C++26.</p>
<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>