https://github.com/Sirraide updated https://github.com/llvm/llvm-project/pull/101448
>From 1fd8db659961fd1702c0c786feb71ad6c5998aa8 Mon Sep 17 00:00:00 2001 From: Sirraide <aeternalm...@gmail.com> Date: Wed, 31 Jul 2024 21:44:02 +0200 Subject: [PATCH 01/11] [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@-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@-1 {{no template named 'Nested' in the global namespace}} + // expected-error@-2 {{friends can only be classes or functions}} + // expected-error@-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@-1 {{no template named 'Nested' in the global namespace}} + // expected-error@-2 {{friends can only be classes or functions}} + // expected-error@-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@-1 {{friends can only be classes or functions}} + // expected-error@-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@-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 <aeternalm...@gmail.com> Date: Wed, 31 Jul 2024 23:38:01 +0200 Subject: [PATCH 02/11] [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@-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 <aeternalm...@gmail.com> Date: Thu, 1 Aug 2024 00:19:54 +0200 Subject: [PATCH 03/11] [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 <aeternalm...@gmail.com> Date: Thu, 1 Aug 2024 04:58:06 +0200 Subject: [PATCH 04/11] [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@-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@-1 {{no template named 'Nested' in the global namespace}} - // expected-error@-2 {{friends can only be classes or functions}} - // expected-error@-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@-1 {{no template named 'Nested' in the global namespace}} - // expected-error@-2 {{friends can only be classes or functions}} - // expected-error@-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@-1 {{friends can only be classes or functions}} - // expected-error@-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@-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@-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@-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 <aeternalm...@gmail.com> Date: Thu, 1 Aug 2024 05:03:31 +0200 Subject: [PATCH 05/11] 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> >From 070e5bcfab0aa400be432305e87a51776cf92e89 Mon Sep 17 00:00:00 2001 From: Sirraide <aeternalm...@gmail.com> Date: Thu, 1 Aug 2024 05:35:52 +0200 Subject: [PATCH 06/11] clang-format --- clang/lib/Frontend/InitPreprocessor.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/clang/lib/Frontend/InitPreprocessor.cpp b/clang/lib/Frontend/InitPreprocessor.cpp index d17e213ea4d3f..bb5822e36cbfa 100644 --- a/clang/lib/Frontend/InitPreprocessor.cpp +++ b/clang/lib/Frontend/InitPreprocessor.cpp @@ -769,7 +769,6 @@ 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"); >From 70e76216a2cbb25f179dccf3b04b6d3076281a1d Mon Sep 17 00:00:00 2001 From: Sirraide <aeternalm...@gmail.com> Date: Mon, 5 Aug 2024 12:22:24 +0200 Subject: [PATCH 07/11] [NFC] auto -> auto* --- clang/lib/AST/DeclPrinter.cpp | 2 +- clang/lib/Sema/SemaTemplateInstantiateDecl.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clang/lib/AST/DeclPrinter.cpp b/clang/lib/AST/DeclPrinter.cpp index 0b4faaf0b39ef..d769a1f1458cc 100644 --- a/clang/lib/AST/DeclPrinter.cpp +++ b/clang/lib/AST/DeclPrinter.cpp @@ -499,7 +499,7 @@ void DeclPrinter::VisitDeclContext(DeclContext *DC, bool Indent) { // 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); + auto *NextFriend = dyn_cast<FriendDecl>(*It); if (!NextFriend || NextFriend->getFriendLoc() != FriendLoc) break; VisitFriendDecl(NextFriend, false); diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index 66ecee046c3b4..0b3b1e49a295b 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -1476,7 +1476,7 @@ Decl *TemplateDeclInstantiator::VisitFriendDecl(FriendDecl *D) { Decls.push_back(FD); } - auto FPD = FriendPackDecl::Create(SemaRef.Context, Owner, D, Decls); + auto *FPD = FriendPackDecl::Create(SemaRef.Context, Owner, D, Decls); FPD->setAccess(AS_public); Owner->addDecl(FPD); return FPD; >From a520f3363c0d4624c187b342cd14d16dcf73d482 Mon Sep 17 00:00:00 2001 From: Sirraide <aeternalm...@gmail.com> Date: Mon, 5 Aug 2024 13:39:31 +0200 Subject: [PATCH 08/11] [Clang] Add feature test macro and compat warnings --- clang/docs/LanguageExtensions.rst | 1 + .../clang/Basic/DiagnosticParseKinds.td | 6 +++++ clang/lib/Frontend/InitPreprocessor.cpp | 1 + clang/lib/Parse/ParseDeclCXX.cpp | 23 ++++++++++++------- clang/test/Lexer/cxx-features.cpp | 4 ++++ .../cxx2c-variadic-friends-ext-diags.cpp | 16 +++++++++++++ 6 files changed, 43 insertions(+), 8 deletions(-) create mode 100644 clang/test/Parser/cxx2c-variadic-friends-ext-diags.cpp diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst index a747464582e77..df9cae817c761 100644 --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -1504,6 +1504,7 @@ Conditional ``explicit`` __cpp_conditional_explicit C+ Attributes on Lambda-Expressions C++23 C++11 Attributes on Structured Bindings __cpp_structured_bindings 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 diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index f8d50d12bb935..87449db3053f0 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -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 " diff --git a/clang/lib/Frontend/InitPreprocessor.cpp b/clang/lib/Frontend/InitPreprocessor.cpp index bb5822e36cbfa..6e29efe8fc661 100644 --- a/clang/lib/Frontend/InitPreprocessor.cpp +++ b/clang/lib/Frontend/InitPreprocessor.cpp @@ -768,6 +768,7 @@ static void InitializeCPlusPlusFeatureTestMacros(const LangOptions &LangOpts, // C++26 features supported in earlier language modes. Builder.defineMacro("__cpp_deleted_function", "202403L"); + Builder.defineMacro("__cpp_variadic_friend", "202403L"); if (LangOpts.Char8) Builder.defineMacro("__cpp_char8_t", "202207L"); diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp index d5c4390e221e5..f7381ae33658b 100644 --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -3068,15 +3068,17 @@ Parser::DeclGroupPtrTy Parser::ParseCXXClassMemberDeclaration( // 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)) { + if (DS.isFriendSpecifiedFirst() && Tok.isOneOf(tok::comma, tok::ellipsis)) { SourceLocation FriendLoc = DS.getFriendSpecLoc(); SmallVector<Decl *> Decls; + auto DiagnoseCompat = [&, Diagnosed = false] () mutable { + if (Diagnosed) + return; + Diagnosed = true; + Diag(Tok.getLocation(), getLangOpts().CPlusPlus26 + ? diag::warn_cxx23_variadic_friends + : diag::ext_variadic_friends); + }; // Handles a single friend-type-specifier. auto ParsedFriendDecl = [&](ParsingDeclSpec &DeclSpec) { @@ -3093,8 +3095,10 @@ Parser::DeclGroupPtrTy Parser::ParseCXXClassMemberDeclaration( } // Eat the '...'. - if (Variadic) + if (Variadic) { + DiagnoseCompat(); ConsumeToken(); + } Decls.push_back(D); return false; @@ -3121,6 +3125,9 @@ Parser::DeclGroupPtrTy Parser::ParseCXXClassMemberDeclaration( return nullptr; }*/ + if (Tok.is(tok::comma)) + DiagnoseCompat(); + while (TryConsumeToken(tok::comma)) { ParsingDeclSpec DeclSpec(*this, TemplateDiags); const char *PrevSpec = nullptr; diff --git a/clang/test/Lexer/cxx-features.cpp b/clang/test/Lexer/cxx-features.cpp index 4c2aa3ae2c544..cca9288826849 100644 --- a/clang/test/Lexer/cxx-features.cpp +++ b/clang/test/Lexer/cxx-features.cpp @@ -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 diff --git a/clang/test/Parser/cxx2c-variadic-friends-ext-diags.cpp b/clang/test/Parser/cxx2c-variadic-friends-ext-diags.cpp new file mode 100644 index 0000000000000..ffcc97ffd6352 --- /dev/null +++ b/clang/test/Parser/cxx2c-variadic-friends-ext-diags.cpp @@ -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}} +}; >From 99b85b40e1cc5a160d9c7daad3c4459371ff554c Mon Sep 17 00:00:00 2001 From: Sirraide <aeternalm...@gmail.com> Date: Mon, 5 Aug 2024 18:36:08 +0200 Subject: [PATCH 09/11] Handle more invalid cases --- clang/include/clang/Sema/Sema.h | 1 + clang/lib/Parse/ParseDeclCXX.cpp | 55 +++++++++++--------- clang/lib/Sema/SemaDeclCXX.cpp | 13 +++-- clang/test/Parser/cxx2c-variadic-friends.cpp | 31 +++++++---- 4 files changed, 61 insertions(+), 39 deletions(-) diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 07c8c2cef9cc3..05f573d208e00 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -5854,6 +5854,7 @@ class Sema final : public SemaBase { unsigned TagSpec, SourceLocation TagLoc, CXXScopeSpec &SS, IdentifierInfo *Name, SourceLocation NameLoc, + SourceLocation EllipsisLoc, const ParsedAttributesView &Attr, MultiTemplateParamsArg TempParamLists); diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp index 085c7b52aa097..247777e2b80f7 100644 --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -2004,7 +2004,14 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind, const PrintingPolicy &Policy = Actions.getASTContext().getPrintingPolicy(); TagUseKind TUK; - if (isDefiningTypeSpecifierContext(DSC, getLangOpts().CPlusPlus) == + + // 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; @@ -2238,9 +2245,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 { @@ -3063,10 +3089,6 @@ Parser::DeclGroupPtrTy Parser::ParseCXXClassMemberDeclaration( // 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. if (DS.isFriendSpecifiedFirst() && Tok.isOneOf(tok::comma, tok::ellipsis)) { SourceLocation FriendLoc = DS.getFriendSpecLoc(); SmallVector<Decl *> Decls; @@ -3083,13 +3105,12 @@ Parser::DeclGroupPtrTy Parser::ParseCXXClassMemberDeclaration( 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); + SkipUntil(tok::semi, tok::r_brace); return true; } @@ -3106,24 +3127,6 @@ Parser::DeclGroupPtrTy Parser::ParseCXXClassMemberDeclaration( 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; - }*/ - if (Tok.is(tok::comma)) DiagnoseCompat(); diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 7e66c66d1cd82..ebc1ab30e053a 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -17354,12 +17354,18 @@ 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; bool Invalid = false; + // FIXME: This works for now; revisit once we support packs in NNSs. + if (EllipsisLoc.isValid()) + Diag(EllipsisLoc, diag::err_pack_expansion_without_parameter_packs) + << SourceRange(FriendLoc, NameLoc); + if (TemplateParameterList *TemplateParams = MatchTemplateParametersToScopeSpecifier( TagLoc, NameLoc, SS, nullptr, TempParamLists, /*friend*/ true, @@ -17369,6 +17375,7 @@ DeclResult Sema::ActOnTemplatedFriendTag( if (Invalid) return true; + return CheckClassTemplate(S, TagSpec, TagUseKind::Friend, TagLoc, SS, Name, NameLoc, Attr, TemplateParams, AS_public, /*ModulePrivateLoc=*/SourceLocation(), @@ -17437,7 +17444,7 @@ DeclResult Sema::ActOnTemplatedFriendTag( FriendDecl *Friend = FriendDecl::Create(Context, CurContext, NameLoc, TSI, FriendLoc, - /*EllipsisLoc=*/SourceLocation(), TempParamLists); + EllipsisLoc, TempParamLists); Friend->setAccess(AS_public); CurContext->addDecl(Friend); return Friend; @@ -17462,7 +17469,7 @@ DeclResult Sema::ActOnTemplatedFriendTag( FriendDecl *Friend = FriendDecl::Create(Context, CurContext, NameLoc, TSI, FriendLoc, - /*EllipsisLoc=*/SourceLocation(), TempParamLists); + EllipsisLoc, TempParamLists); Friend->setAccess(AS_public); Friend->setUnsupportedFriend(true); CurContext->addDecl(Friend); diff --git a/clang/test/Parser/cxx2c-variadic-friends.cpp b/clang/test/Parser/cxx2c-variadic-friends.cpp index ac99e40fc6b8c..17e894e711224 100644 --- a/clang/test/Parser/cxx2c-variadic-friends.cpp +++ b/clang/test/Parser/cxx2c-variadic-friends.cpp @@ -12,15 +12,13 @@ struct Errors { friend int, int; friend int, long, char; - // We simply ignore the '...' here. + // 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}} - // 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}} + 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}} @@ -29,6 +27,9 @@ struct Errors { template <typename> struct C { template<class T> class Nested; }; +template <typename, typename> +struct D { template<class T> class Nested; }; + template<class... Ts> // expected-note {{template parameter is declared here}} struct VS { friend Ts...; @@ -46,15 +47,25 @@ struct VS { friend class Us...; // expected-error {{declaration of 'Us' shadows template parameter}} // expected-error@-1 {{pack expansion does not contain any unexpanded parameter packs}} - // FIXME: Ill-formed. template<class U> - friend class C<Ts>::template Nested<U>...; + friend class C<Ts>::template Nested<U>...; // expected-error {{cannot specialize a dependent template}} - // FIXME: Ill-formed. template<class... Us> - friend class C<Ts...>::template Nested<Us>...; + friend class C<Ts...>::template Nested<Us>...; // expected-error {{cannot specialize a dependent template}} + + // FIXME: Should be valid, but we currently can’t handle packs in NNSs. + template<class ...T> + friend class TS<Ts>::Nested...; // expected-error {{pack expansion does not contain any unexpanded parameter packs}} + // expected-warning@-1 {{dependent nested name specifier 'TS<Ts>::' for friend template declaration is not supported; ignoring this friend declaration}} + + // FIXME: This I legitimately have no idea what to do with. I *think* it might + // be well-formed by the same logic as the previous one? + template<class T> + friend class D<T, Ts>::Nested...; // expected-error {{pack expansion does not contain any unexpanded parameter packs}} + // expected-warning@-1 {{dependent nested name specifier 'D<T, Ts>::' for friend class declaration is not supported; turning off access control for 'VS'}} - // FIXME: Ill-formed. + // FIXME: Ill-formed... probably? template<class... Us> - friend class C<Us>::Nested...; + friend class C<Us>::Nested...; // expected-error {{pack expansion does not contain any unexpanded parameter packs}} + // expected-warning@-1 {{dependent nested name specifier 'C<Us>::' for friend class declaration is not supported; turning off access control for 'VS'}} }; >From b6547be7766899238b50018fbc69358eb95a8122 Mon Sep 17 00:00:00 2001 From: Sirraide <aeternalm...@gmail.com> Date: Mon, 5 Aug 2024 18:37:38 +0200 Subject: [PATCH 10/11] clang-format --- clang/lib/Parse/ParseDeclCXX.cpp | 4 ++-- clang/lib/Sema/SemaDeclCXX.cpp | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp index 247777e2b80f7..7ee79d7515722 100644 --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -2012,8 +2012,8 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind, Tok.isOneOf(tok::comma, tok::ellipsis)) TUK = TagUseKind::Friend; else if (isDefiningTypeSpecifierContext(DSC, getLangOpts().CPlusPlus) == - AllowDefiningTypeSpec::No || - (getLangOpts().OpenMP && OpenMPDirectiveParsing)) + AllowDefiningTypeSpec::No || + (getLangOpts().OpenMP && OpenMPDirectiveParsing)) TUK = TagUseKind::Reference; else if (Tok.is(tok::l_brace) || (DSC != DeclSpecContext::DSC_association && diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index ebc1ab30e053a..3e71774a04db9 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -17363,8 +17363,8 @@ DeclResult Sema::ActOnTemplatedFriendTag( // FIXME: This works for now; revisit once we support packs in NNSs. if (EllipsisLoc.isValid()) - Diag(EllipsisLoc, diag::err_pack_expansion_without_parameter_packs) - << SourceRange(FriendLoc, NameLoc); + Diag(EllipsisLoc, diag::err_pack_expansion_without_parameter_packs) + << SourceRange(FriendLoc, NameLoc); if (TemplateParameterList *TemplateParams = MatchTemplateParametersToScopeSpecifier( @@ -17375,7 +17375,6 @@ DeclResult Sema::ActOnTemplatedFriendTag( if (Invalid) return true; - return CheckClassTemplate(S, TagSpec, TagUseKind::Friend, TagLoc, SS, Name, NameLoc, Attr, TemplateParams, AS_public, /*ModulePrivateLoc=*/SourceLocation(), >From 4214b46f967b11dcb261fdfdfd3a428864796e0e Mon Sep 17 00:00:00 2001 From: Sirraide <aeternalm...@gmail.com> Date: Mon, 5 Aug 2024 18:47:10 +0200 Subject: [PATCH 11/11] clang-format, again --- clang/lib/Parse/ParseDeclCXX.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp index 7ee79d7515722..a09016e07426f 100644 --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -3092,7 +3092,7 @@ Parser::DeclGroupPtrTy Parser::ParseCXXClassMemberDeclaration( if (DS.isFriendSpecifiedFirst() && Tok.isOneOf(tok::comma, tok::ellipsis)) { SourceLocation FriendLoc = DS.getFriendSpecLoc(); SmallVector<Decl *> Decls; - auto DiagnoseCompat = [&, Diagnosed = false] () mutable { + auto DiagnoseCompat = [&, Diagnosed = false]() mutable { if (Diagnosed) return; Diagnosed = true; _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits