[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:
parent
b5e63cc533
commit
2b0a8fcf70
@ -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
|
||||
|
||||
@ -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
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
||||
@ -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; }
|
||||
|
||||
@ -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 "
|
||||
|
||||
@ -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 "
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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());
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -461,6 +461,7 @@ public:
|
||||
} else {
|
||||
AddDecl(D->getFriendDecl());
|
||||
}
|
||||
Hash.AddBoolean(D->isPackExpansion());
|
||||
}
|
||||
|
||||
void VisitTemplateTypeParmDecl(const TemplateTypeParmDecl *D) {
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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");
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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
81
clang/test/AST/cxx2c-variadic-friends.cpp
Normal file
81
clang/test/AST/cxx2c-variadic-friends.cpp
Normal 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>...;
|
||||
};
|
||||
}
|
||||
25
clang/test/CXX/drs/cwg29xx.cpp
Normal file
25
clang/test/CXX/drs/cwg29xx.cpp
Normal 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
|
||||
@ -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
|
||||
|
||||
16
clang/test/Parser/cxx2c-variadic-friends-ext-diags.cpp
Normal file
16
clang/test/Parser/cxx2c-variadic-friends-ext-diags.cpp
Normal 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}}
|
||||
};
|
||||
91
clang/test/Parser/cxx2c-variadic-friends.cpp
Normal file
91
clang/test/Parser/cxx2c-variadic-friends.cpp
Normal 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}}
|
||||
}
|
||||
}
|
||||
156
clang/test/SemaCXX/cxx2c-variadic-friends.cpp
Normal file
156
clang/test/SemaCXX/cxx2c-variadic-friends.cpp
Normal 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}}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user