llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang Author: Krystian Stasiowski (sdkrystian) <details> <summary>Changes</summary> According to [[dcl.type.elab]p4](http://eel.is/c++draft/dcl.type.elab#<!-- -->4): > If an _elaborated-type-specifier_ appears with the `friend` specifier as an entire _member-declaration_, the _member-declaration_ shall have one of the following forms: > `friend` _class-key_ _nested-name-specifier_(opt) _identifier_ `;` > `friend` _class-key_ _simple-template-id_ `;` > `friend` _class-key_ _nested-name-specifier_ `template`(opt) _simple-template-id_ `;` Notably absent from this list is the `enum` form of an _elaborated-type-specifier_ "`enum` _nested-name-specifier_(opt) _identifier_", which appears to be intentional per the resolution of [CWG2363](http://wg21.link/CWG2363). Most major implementations accept these declarations, so the diagnostic is a pedantic warning across all C++ versions. In addition to the trivial cases previously diagnosed in C++98, we now diagnose cases where the _elaborated-type-specifier_ has a dependent _nested-name-specifier_: ```cpp template<typename T> struct A { enum class E; }; struct B { template<typename T> friend enum A<T>::E; // pedantic warning: elaborated enumeration type cannot be a friend }; template<typename T> struct C { friend enum T::E; // pedantic warning: elaborated enumeration type cannot be a friend }; ``` --- Patch is 24.44 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/80171.diff 20 Files Affected: - (modified) clang/docs/ReleaseNotes.rst (+2) - (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+2-4) - (modified) clang/include/clang/Parse/Parser.h (+4-4) - (modified) clang/include/clang/Sema/DeclSpec.h (+8-12) - (modified) clang/include/clang/Sema/Sema.h (-3) - (modified) clang/lib/Parse/ParseDecl.cpp (+1-1) - (modified) clang/lib/Parse/ParseTentative.cpp (+1-1) - (modified) clang/lib/Sema/DeclSpec.cpp (+3-7) - (modified) clang/lib/Sema/SemaDecl.cpp (+16) - (modified) clang/lib/Sema/SemaDeclCXX.cpp (+38-99) - (modified) clang/lib/Sema/SemaTemplateInstantiateDecl.cpp (+2-5) - (modified) clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.elab/p3.cpp (+2-4) - (added) clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.elab/p4.cpp (+33) - (modified) clang/test/CXX/drs/dr16xx.cpp (+1) - (modified) clang/test/CXX/temp/temp.decls/temp.class/temp.mem.enum/p1.cpp (+3-2) - (modified) clang/test/FixIt/fixit-c++11.cpp (+1) - (modified) clang/test/Parser/cxx-decl.cpp (-3) - (modified) clang/test/Parser/cxx0x-decl.cpp (+1-1) - (modified) clang/test/SemaCXX/cxx98-compat.cpp (+1-1) - (modified) clang/test/SemaCXX/enum-scoped.cpp (+5) ``````````diff diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 323157c4db1f1..68987cb179221 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -143,6 +143,8 @@ Improvements to Clang's diagnostics - Clang now applies syntax highlighting to the code snippets it prints. +- Clang now diagnoses friend declarations with an `enum` elaborated-type-specifier outside of C++98. + Improvements to Clang's time-trace ---------------------------------- diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 1c0ebbe4e6834..7bb026679f6cd 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -1637,10 +1637,8 @@ def err_inline_namespace_std : Error< def err_unexpected_friend : Error< "friends can only be classes or functions">; def ext_enum_friend : ExtWarn< - "befriending enumeration type %0 is a C++11 extension">, InGroup<CXX11>; -def warn_cxx98_compat_enum_friend : Warning< - "befriending enumeration type %0 is incompatible with C++98">, - InGroup<CXX98Compat>, DefaultIgnore; + "elaborated enumeration type cannot be a friend">, + InGroup<DiagGroup<"friend-enum">>; def ext_nonclass_type_friend : ExtWarn< "non-class friend type %0 is a C++11 extension">, InGroup<CXX11>; def warn_cxx98_compat_nonclass_type_friend : Warning< diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index 4a066acf511a1..16411e1947ffc 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -2551,10 +2551,10 @@ class Parser : public CodeCompletionHandler { /// Starting with a scope specifier, identifier, or /// template-id that refers to the current class, determine whether /// this is a constructor declarator. - bool isConstructorDeclarator( - bool Unqualified, bool DeductionGuide = false, - DeclSpec::FriendSpecified IsFriend = DeclSpec::FriendSpecified::No, - const ParsedTemplateInfo *TemplateInfo = nullptr); + bool + isConstructorDeclarator(bool Unqualified, bool DeductionGuide = false, + bool IsFriend = false, + const ParsedTemplateInfo *TemplateInfo = nullptr); /// Specifies the context in which type-id/expression /// disambiguation will occur. diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h index 77638def60063..c7266b66c014d 100644 --- a/clang/include/clang/Sema/DeclSpec.h +++ b/clang/include/clang/Sema/DeclSpec.h @@ -346,11 +346,6 @@ class DeclSpec { // FIXME: Attributes should be included here. }; - enum FriendSpecified : bool { - No, - Yes, - }; - private: // storage-class-specifier /*SCS*/unsigned StorageClassSpec : 3; @@ -380,7 +375,8 @@ class DeclSpec { unsigned FS_noreturn_specified : 1; // friend-specifier - unsigned Friend_specified : 1; + unsigned FriendSpecified : 1; + unsigned FriendSpecifiedFirst : 1; // constexpr-specifier unsigned ConstexprSpecifier : 2; @@ -470,9 +466,9 @@ class DeclSpec { TypeSpecPipe(false), TypeSpecSat(false), ConstrainedAuto(false), TypeQualifiers(TQ_unspecified), FS_inline_specified(false), FS_forceinline_specified(false), FS_virtual_specified(false), - FS_noreturn_specified(false), Friend_specified(false), - ConstexprSpecifier( - static_cast<unsigned>(ConstexprSpecKind::Unspecified)), + FS_noreturn_specified(false), FriendSpecified(false), + FriendSpecifiedFirst(false), ConstexprSpecifier(static_cast<unsigned>( + ConstexprSpecKind::Unspecified)), Attrs(attrFactory), writtenBS(), ObjCQualifiers(nullptr) {} // storage-class-specifier @@ -796,9 +792,9 @@ class DeclSpec { bool SetConstexprSpec(ConstexprSpecKind ConstexprKind, SourceLocation Loc, const char *&PrevSpec, unsigned &DiagID); - FriendSpecified isFriendSpecified() const { - return static_cast<FriendSpecified>(Friend_specified); - } + bool isFriendSpecified() const { return FriendSpecified; } + + bool isFriendSpecifiedFirst() const { return FriendSpecifiedFirst; } SourceLocation getFriendSpecLoc() const { return FriendLoc; } diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 59eab0185ae63..491a7b9893472 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -7975,9 +7975,6 @@ class Sema final { SourceLocation RParenLoc, bool Failed); void DiagnoseStaticAssertDetails(const Expr *E); - FriendDecl *CheckFriendTypeDecl(SourceLocation LocStart, - SourceLocation FriendLoc, - TypeSourceInfo *TSInfo); Decl *ActOnFriendTypeDecl(Scope *S, const DeclSpec &DS, MultiTemplateParamsArg TemplateParams); NamedDecl *ActOnFriendFunctionDecl(Scope *S, Declarator &D, diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index bcbe2d9c635a6..80622543ce88b 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -5876,7 +5876,7 @@ bool Parser::isDeclarationSpecifier( } bool Parser::isConstructorDeclarator(bool IsUnqualified, bool DeductionGuide, - DeclSpec::FriendSpecified IsFriend, + bool IsFriend, const ParsedTemplateInfo *TemplateInfo) { RevertingTentativeParsingAction TPA(*this); // Parse the C++ scope specifier. diff --git a/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp index f1737cb844767..64aaa4ea0b206 100644 --- a/clang/lib/Parse/ParseTentative.cpp +++ b/clang/lib/Parse/ParseTentative.cpp @@ -81,7 +81,7 @@ bool Parser::isCXXDeclarationStatement( isDeductionGuide) { if (isConstructorDeclarator(/*Unqualified=*/SS.isEmpty(), isDeductionGuide, - DeclSpec::FriendSpecified::No)) + /*IsFriend*/ false)) return true; } else if (SS.isNotEmpty()) { // If the scope is not empty, it could alternatively be something like diff --git a/clang/lib/Sema/DeclSpec.cpp b/clang/lib/Sema/DeclSpec.cpp index 313f073445e8f..1c99508802ee7 100644 --- a/clang/lib/Sema/DeclSpec.cpp +++ b/clang/lib/Sema/DeclSpec.cpp @@ -1102,18 +1102,14 @@ bool DeclSpec::setFunctionSpecNoreturn(SourceLocation Loc, bool DeclSpec::SetFriendSpec(SourceLocation Loc, const char *&PrevSpec, unsigned &DiagID) { - if (Friend_specified) { + if (FriendSpecified) { PrevSpec = "friend"; - // Keep the later location, so that we can later diagnose ill-formed - // declarations like 'friend class X friend;'. Per [class.friend]p3, - // 'friend' must be the first token in a friend declaration that is - // not a function declaration. - FriendLoc = Loc; DiagID = diag::warn_duplicate_declspec; return true; } - Friend_specified = true; + FriendSpecified = true; + FriendSpecifiedFirst = isEmpty(); FriendLoc = Loc; return false; } diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index c89c3c487272d..edd9408ebb2c4 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -17258,6 +17258,22 @@ Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, SourceLocation KWLoc, return true; } + if (TUK == TUK_Friend && Kind == TagTypeKind::Enum) { + // C++23 [dcl.type.elab]p4: + // If an elaborated-type-specifier appears with the friend specifier as + // an entire member-declaration, the member-declaration shall have one + // of the following forms: + // friend class-key nested-name-specifier(opt) identifier ; + // friend class-key simple-template-id ; + // friend class-key nested-name-specifier template(opt) + // simple-template-id ; + // + // Since enum is not a class-key, so declarations like "friend enum E;" + // are ill-formed. Although CWG2363 reaffirms that such declarations are + // invalid, most implementations accept so we issue a pedantic warning. + Diag(KWLoc, diag::ext_enum_friend); + } + // Figure out the underlying type if this a enum declaration. We need to do // this early, because it's needed to detect if this is an incompatible // redeclaration. diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 5adc262cd6bc9..c0a6c8ca81a49 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -17534,79 +17534,6 @@ Decl *Sema::BuildStaticAssertDeclaration(SourceLocation StaticAssertLoc, return Decl; } -/// Perform semantic analysis of the given friend type declaration. -/// -/// \returns A friend declaration that. -FriendDecl *Sema::CheckFriendTypeDecl(SourceLocation LocStart, - SourceLocation FriendLoc, - TypeSourceInfo *TSInfo) { - assert(TSInfo && "NULL TypeSourceInfo for friend type declaration"); - - QualType T = TSInfo->getType(); - SourceRange TypeRange = TSInfo->getTypeLoc().getSourceRange(); - - // C++03 [class.friend]p2: - // An elaborated-type-specifier shall be used in a friend declaration - // for a class.* - // - // * The class-key of the elaborated-type-specifier is required. - if (!CodeSynthesisContexts.empty()) { - // Do not complain about the form of friend template types during any kind - // of code synthesis. For template instantiation, we will have complained - // when the template was defined. - } else { - if (!T->isElaboratedTypeSpecifier()) { - // If we evaluated the type to a record type, suggest putting - // a tag in front. - if (const RecordType *RT = T->getAs<RecordType>()) { - RecordDecl *RD = RT->getDecl(); - - SmallString<16> InsertionText(" "); - InsertionText += RD->getKindName(); - - Diag(TypeRange.getBegin(), - getLangOpts().CPlusPlus11 ? - diag::warn_cxx98_compat_unelaborated_friend_type : - diag::ext_unelaborated_friend_type) - << (unsigned) RD->getTagKind() - << T - << FixItHint::CreateInsertion(getLocForEndOfToken(FriendLoc), - InsertionText); - } else { - Diag(FriendLoc, - getLangOpts().CPlusPlus11 ? - diag::warn_cxx98_compat_nonclass_type_friend : - diag::ext_nonclass_type_friend) - << T - << TypeRange; - } - } else if (T->getAs<EnumType>()) { - Diag(FriendLoc, - getLangOpts().CPlusPlus11 ? - diag::warn_cxx98_compat_enum_friend : - diag::ext_enum_friend) - << T - << TypeRange; - } - - // C++11 [class.friend]p3: - // A friend declaration that does not declare a function shall have one - // of the following forms: - // friend elaborated-type-specifier ; - // friend simple-type-specifier ; - // friend typename-specifier ; - if (getLangOpts().CPlusPlus11 && LocStart != FriendLoc) - Diag(FriendLoc, diag::err_friend_not_first_in_declaration) << T; - } - - // If the type specifier in a friend declaration designates a (possibly - // cv-qualified) class type, that class is declared as a friend; otherwise, - // the friend declaration is ignored. - return FriendDecl::Create(Context, CurContext, - TSInfo->getTypeLoc().getBeginLoc(), TSInfo, - FriendLoc); -} - /// Handle a friend tag declaration where the scope specifier was /// templated. DeclResult Sema::ActOnTemplatedFriendTag( @@ -17744,6 +17671,7 @@ DeclResult Sema::ActOnTemplatedFriendTag( Decl *Sema::ActOnFriendTypeDecl(Scope *S, const DeclSpec &DS, MultiTemplateParamsArg TempParams) { SourceLocation Loc = DS.getBeginLoc(); + SourceLocation FriendLoc = DS.getFriendSpecLoc(); assert(DS.isFriendSpecified()); assert(DS.getStorageClassSpec() == DeclSpec::SCS_unspecified); @@ -17755,9 +17683,10 @@ Decl *Sema::ActOnFriendTypeDecl(Scope *S, const DeclSpec &DS, // friend simple-type-specifier ; // friend typename-specifier ; // - // Any declaration with a type qualifier does not have that form. (It's - // legal to specify a qualified type as a friend, you just can't write the - // keywords.) + // If the friend keyword isn't first, or if the declarations has any type + // qualifiers, then the declaration doesn't have that form. + if (getLangOpts().CPlusPlus11 && !DS.isFriendSpecifiedFirst()) + Diag(FriendLoc, diag::err_friend_not_first_in_declaration); if (DS.getTypeQualifiers()) { if (DS.getTypeQualifiers() & DeclSpec::TQ_const) Diag(DS.getConstSpecLoc(), diag::err_friend_decl_spec) << "const"; @@ -17784,24 +17713,35 @@ Decl *Sema::ActOnFriendTypeDecl(Scope *S, const DeclSpec &DS, if (DiagnoseUnexpandedParameterPack(Loc, TSI, UPPC_FriendDeclaration)) return nullptr; - // This is definitely an error in C++98. It's probably meant to - // be forbidden in C++0x, too, but the specification is just - // poorly written. - // - // The problem is with declarations like the following: - // template <T> friend A<T>::foo; - // where deciding whether a class C is a friend or not now hinges - // on whether there exists an instantiation of A that causes - // 'foo' to equal C. There are restrictions on class-heads - // (which we declare (by fiat) elaborated friend declarations to - // be) that makes this tractable. - // - // FIXME: handle "template <> friend class A<T>;", which - // is possibly well-formed? Who even knows? - if (TempParams.size() && !T->isElaboratedTypeSpecifier()) { - Diag(Loc, diag::err_tagless_friend_type_template) - << DS.getSourceRange(); - return nullptr; + if (!T->isElaboratedTypeSpecifier()) { + if (TempParams.size()) { + // C++23 [dcl.pre]p5: + // In a simple-declaration, the optional init-declarator-list can be + // omitted only when declaring a class or enumeration, that is, when + // the decl-specifier-seq contains either a class-specifier, an + // elaborated-type-specifier with a class-key, or an enum-specifier. + // + // The declaration of a template-declaration or explicit-specialization + // is never a member-declaration, so this must be a simple-declaration + // with no init-declarator-list. Therefore, this is ill-formed. + Diag(Loc, diag::err_tagless_friend_type_template) << DS.getSourceRange(); + return nullptr; + } else if (const RecordDecl *RD = T->getAsRecordDecl()) { + SmallString<16> InsertionText(" "); + InsertionText += RD->getKindName(); + + Diag(Loc, getLangOpts().CPlusPlus11 + ? diag::warn_cxx98_compat_unelaborated_friend_type + : diag::ext_unelaborated_friend_type) + << (unsigned)RD->getTagKind() << T + << FixItHint::CreateInsertion(getLocForEndOfToken(FriendLoc), + InsertionText); + } else { + Diag(FriendLoc, getLangOpts().CPlusPlus11 + ? diag::warn_cxx98_compat_nonclass_type_friend + : diag::ext_nonclass_type_friend) + << T << DS.getSourceRange(); + } } // C++98 [class.friend]p1: A friend of a class is a function @@ -17817,12 +17757,11 @@ Decl *Sema::ActOnFriendTypeDecl(Scope *S, const DeclSpec &DS, Decl *D; if (!TempParams.empty()) - D = FriendTemplateDecl::Create(Context, CurContext, Loc, - TempParams, - TSI, - DS.getFriendSpecLoc()); + D = FriendTemplateDecl::Create(Context, CurContext, Loc, TempParams, TSI, + FriendLoc); else - D = CheckFriendTypeDecl(Loc, DS.getFriendSpecLoc(), TSI); + D = FriendDecl::Create(Context, CurContext, TSI->getTypeLoc().getBeginLoc(), + TSI, FriendLoc); if (!D) return nullptr; diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index fcb27a880290b..36d2c441391d8 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -1407,11 +1407,8 @@ Decl *TemplateDeclInstantiator::VisitFriendDecl(FriendDecl *D) { if (!InstTy) return nullptr; - FriendDecl *FD = SemaRef.CheckFriendTypeDecl(D->getBeginLoc(), - D->getFriendLoc(), InstTy); - if (!FD) - return nullptr; - + FriendDecl *FD = FriendDecl::Create( + SemaRef.Context, Owner, D->getLocation(), InstTy, D->getFriendLoc()); FD->setAccess(AS_public); FD->setUnsupportedFriend(D->isUnsupportedFriend()); Owner->addDecl(FD); diff --git a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.elab/p3.cpp b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.elab/p3.cpp index 19406518402ff..75a33c0390897 100644 --- a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.elab/p3.cpp +++ b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.elab/p3.cpp @@ -16,10 +16,8 @@ class A1 { friend union A; // expected-error {{use of 'A' with tag type that does not match previous declaration}} friend enum A; // expected-error {{use of 'A' with tag type that does not match previous declaration}} - friend enum E; -#if __cplusplus <= 199711L // C++03 or earlier modes - // expected-warning@-2 {{befriending enumeration type 'enum E' is a C++11 extension}} -#endif + // expected-warning@-1 {{cannot be a friend}} + friend enum E; // expected-warning {{cannot be a friend}} }; template <class T> struct B { // expected-note {{previous use is here}} diff --git a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.elab/p4.cpp b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.elab/p4.cpp new file mode 100644 index 0000000000000..c7792f3dd29ac --- /dev/null +++ b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.elab/p4.cpp @@ -0,0 +1,33 @@ +// RUN: %clang_cc1 -verify %s -std=c++11 -pedantic-errors + +enum class E; + +template<typename T> +struct A { + enum class F; +}; + +struct B { + template<typename T> + friend enum A<T>::F; // expected-error {{elaborated enumeration type cannot be a friend}} + + // FIXME: Per [temp.expl.spec]p19, a friend declaration cannot be an explicit specialization + template<> + friend enum A<int>::F; // expected-error {{elaborated enumeration type cannot be a friend}} + + enum class G; + + friend enum E; // expected-error {{elaborated enumeration type cannot be a friend}} +}; + +template<typename T> +struct C { + friend enum T::G; // expected-error {{elaborated enumeration type cannot be a friend}} + friend enum A<T>::G; // expected-error {{elaborated enumeration type cannot be a friend}} +}; + +struct D { + friend enum B::G; // expected-error {{elaborated enumeration type cannot be a friend}} + friend enum class B::G; // expected-error {{elaborated enumeration type cannot be a friend}} + // expected-error@-1 {{reference to enumeration must use 'enum' not 'enum class'}} +}; diff --git a/clang/test/CXX/drs/dr16xx.cpp b/clang/test/CXX/drs/dr16xx.cpp index 6ce77fbba7cee..a6de1887baa87 100644 --- a/clang/test/CXX/drs/dr16xx.cpp +++ b/clang/test/CXX/drs/dr16xx.cpp @@ -107,6 +107,7 @@ namespace dr1638 { // dr1638: 3.1 struct B { friend enum class A<unsigned>::E; // since-cxx11-error@-1 {{reference to enumeration must use 'enum' not 'enum class'}} + // since-cxx11-error@-2 {{cannot be a friend}} }; #endif } diff --git a/clang/test/CXX/temp/temp.decls/temp.class/temp.mem.enum/p1.cpp b/clang/test/CXX/temp/temp.decls/temp.class/temp.mem.enum/p1.cpp index 2884be146c7c3..012248de06057 100644 --- a/clang/test/CXX/temp/temp.decls/temp.class/temp.mem.enum/p1.cpp +++ b/clang/test/CXX/temp/temp.decls/temp.class/temp.mem.enum/p1.cpp @@ -101,10 +101,11 @@ template<> enum class D<short>::E; struct F { // Pe... [truncated] `````````` </details> https://github.com/llvm/llvm-project/pull/80171 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits