llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang Author: None (yronglin) <details> <summary>Changes</summary> The PR reapply https://github.com/llvm/llvm-project/pull/97308. Implement [CWG1815](https://wg21.link/CWG1815): Support lifetime extension of temporary created by aggregate initialization using a default member initializer. Fix crash that introduced in https://github.com/llvm/llvm-project/pull/97308. In `InitListChecker::FillInEmptyInitForField`, when we enter rebuild-default-init context, we copy all the contents of the parent context to the current context, which will cause the `MaybeODRUseExprs` to be lost. But we don't need to copy the entire context, only the `DelayedDefaultInitializationContext` was required, which is used to build `SourceLocExpr`, etc. --- Patch is 29.68 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/108039.diff 21 Files Affected: - (modified) clang/docs/ReleaseNotes.rst (+3) - (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (-7) - (modified) clang/include/clang/Sema/Sema.h (+8-15) - (modified) clang/lib/Parse/ParseDecl.cpp (+2-1) - (modified) clang/lib/Sema/CheckExprLifetime.cpp (+1-17) - (modified) clang/lib/Sema/SemaExpr.cpp (+27-9) - (modified) clang/lib/Sema/SemaExprCXX.cpp (-3) - (modified) clang/lib/Sema/SemaInit.cpp (+17-6) - (modified) clang/lib/Sema/SemaTemplateInstantiateDecl.cpp (+4-1) - (modified) clang/lib/Sema/TreeTransform.h (+14-6) - (modified) clang/test/AST/ast-dump-default-init-json.cpp (+3-3) - (modified) clang/test/AST/ast-dump-default-init.cpp (+1-1) - (modified) clang/test/Analysis/lifetime-extended-regions.cpp (+5-4) - (modified) clang/test/CXX/drs/cwg16xx.cpp (+21-2) - (modified) clang/test/CXX/drs/cwg18xx.cpp (+14-5) - (modified) clang/test/CXX/special/class.temporary/p6.cpp (+34) - (added) clang/test/SemaCXX/PR97308.cpp (+21) - (modified) clang/test/SemaCXX/constexpr-default-arg.cpp (+2-2) - (modified) clang/test/SemaCXX/cxx11-default-member-initializers.cpp (+97) - (modified) clang/test/SemaCXX/eval-crashes.cpp (+2-4) - (modified) clang/www/cxx_dr_status.html (+1-1) ``````````diff diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 250821a9f9c45c..07f3544e2324e3 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -108,6 +108,9 @@ C++ Language Changes - Allow single element access of GCC vector/ext_vector_type object to be constant expression. Supports the `V.xyzw` syntax and other tidbits as seen in OpenCL. Selecting multiple elements is left as a future work. +- Implement `CWG1815 <https://wg21.link/CWG1815>`_. Support lifetime extension + of temporary created by aggregate initialization using a default member + initializer. - Accept C++26 user-defined ``static_assert`` messages in C++11 as an extension. diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 58819a64813fce..4609d2ec2b7209 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -10159,13 +10159,6 @@ def warn_dangling_pointer_assignment : Warning< "will be destroyed at the end of the full-expression">, InGroup<DanglingAssignment>; -def warn_unsupported_lifetime_extension : Warning< - "lifetime extension of " - "%select{temporary|backing array of initializer list}0 created " - "by aggregate initialization using a default member initializer " - "is not yet supported; lifetime of %select{temporary|backing array}0 " - "will end at the end of the full-expression">, InGroup<Dangling>; - // For non-floating point, expressions of the form x == x or x != x // should result in a warning, since these always evaluate to a constant. // Array comparisons have similar warnings diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 68c782a15c6f1b..ac4c11964b126f 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -6403,6 +6403,9 @@ class Sema final : public SemaBase { /// example, in a for-range initializer). bool InLifetimeExtendingContext = false; + /// Whether we should rebuild CXXDefaultArgExpr and CXXDefaultInitExpr. + bool RebuildDefaultArgOrDefaultInit = false; + // When evaluating immediate functions in the initializer of a default // argument or default member initializer, this is the declaration whose // default initializer is being evaluated and the location of the call @@ -7810,9 +7813,11 @@ class Sema final : public SemaBase { } bool isInLifetimeExtendingContext() const { - assert(!ExprEvalContexts.empty() && - "Must be in an expression evaluation context"); - return ExprEvalContexts.back().InLifetimeExtendingContext; + return currentEvaluationContext().InLifetimeExtendingContext; + } + + bool needRebuildDefaultArgOrInit() const { + return currentEvaluationContext().RebuildDefaultArgOrDefaultInit; } bool isCheckingDefaultArgumentOrInitializer() const { @@ -7854,18 +7859,6 @@ class Sema final : public SemaBase { return Res; } - /// keepInLifetimeExtendingContext - Pull down InLifetimeExtendingContext - /// flag from previous context. - void keepInLifetimeExtendingContext() { - if (ExprEvalContexts.size() > 2 && - parentEvaluationContext().InLifetimeExtendingContext) { - auto &LastRecord = ExprEvalContexts.back(); - auto &PrevRecord = parentEvaluationContext(); - LastRecord.InLifetimeExtendingContext = - PrevRecord.InLifetimeExtendingContext; - } - } - DefaultedComparisonKind getDefaultedComparisonKind(const FunctionDecl *FD) { return getDefaultedFunctionKind(FD).asComparison(); } diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 61a1ca3da6bca0..1f56884be392d6 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -2509,8 +2509,9 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS, // P2718R0 - Lifetime extension in range-based for loops. if (getLangOpts().CPlusPlus23) { - auto &LastRecord = Actions.ExprEvalContexts.back(); + auto &LastRecord = Actions.currentEvaluationContext(); LastRecord.InLifetimeExtendingContext = true; + LastRecord.RebuildDefaultArgOrDefaultInit = true; } if (getLangOpts().OpenMP) diff --git a/clang/lib/Sema/CheckExprLifetime.cpp b/clang/lib/Sema/CheckExprLifetime.cpp index f1507ebb9a5068..f62e18543851c1 100644 --- a/clang/lib/Sema/CheckExprLifetime.cpp +++ b/clang/lib/Sema/CheckExprLifetime.cpp @@ -871,11 +871,6 @@ static void visitLocalsRetainedByInitializer(IndirectLocalPath &Path, enum PathLifetimeKind { /// Lifetime-extend along this path. Extend, - /// We should lifetime-extend, but we don't because (due to technical - /// limitations) we can't. This happens for default member initializers, - /// which we don't clone for every use, so we don't have a unique - /// MaterializeTemporaryExpr to update. - ShouldExtend, /// Do not lifetime extend along this path. NoExtend }; @@ -887,7 +882,7 @@ shouldLifetimeExtendThroughPath(const IndirectLocalPath &Path) { PathLifetimeKind Kind = PathLifetimeKind::Extend; for (auto Elem : Path) { if (Elem.Kind == IndirectLocalPathEntry::DefaultInit) - Kind = PathLifetimeKind::ShouldExtend; + return PathLifetimeKind::Extend; else if (Elem.Kind != IndirectLocalPathEntry::LambdaCaptureInit) return PathLifetimeKind::NoExtend; } @@ -1034,17 +1029,6 @@ static void checkExprLifetimeImpl(Sema &SemaRef, // Also visit the temporaries lifetime-extended by this initializer. return true; - case PathLifetimeKind::ShouldExtend: - // We're supposed to lifetime-extend the temporary along this path (per - // the resolution of DR1815), but we don't support that yet. - // - // FIXME: Properly handle this situation. Perhaps the easiest approach - // would be to clone the initializer expression on each use that would - // lifetime extend its temporaries. - SemaRef.Diag(DiagLoc, diag::warn_unsupported_lifetime_extension) - << RK << DiagRange; - break; - case PathLifetimeKind::NoExtend: // If the path goes through the initialization of a variable or field, // it can't possibly reach a temporary created in this full-expression. diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 32dac4440fb82a..30e5fe4543bcaa 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -5429,6 +5429,8 @@ struct EnsureImmediateInvocationInDefaultArgs EnsureImmediateInvocationInDefaultArgs(Sema &SemaRef) : TreeTransform(SemaRef) {} + bool AlwaysRebuild() { return true; } + // Lambda can only have immediate invocations in the default // args of their parameters, which is transformed upon calling the closure. // The body is not a subexpression, so we have nothing to do. @@ -5470,7 +5472,7 @@ ExprResult Sema::BuildCXXDefaultArgExpr(SourceLocation CallLoc, assert(Param->hasDefaultArg() && "can't build nonexistent default arg"); bool NestedDefaultChecking = isCheckingDefaultArgumentOrInitializer(); - bool InLifetimeExtendingContext = isInLifetimeExtendingContext(); + bool NeedRebuild = needRebuildDefaultArgOrInit(); std::optional<ExpressionEvaluationContextRecord::InitializationContext> InitializationContext = OutermostDeclarationWithDelayedImmediateInvocations(); @@ -5506,13 +5508,15 @@ ExprResult Sema::BuildCXXDefaultArgExpr(SourceLocation CallLoc, // Rewrite the call argument that was created from the corresponding // parameter's default argument. - if (V.HasImmediateCalls || InLifetimeExtendingContext) { + if (V.HasImmediateCalls || + (NeedRebuild && isa_and_present<ExprWithCleanups>(Param->getInit()))) { if (V.HasImmediateCalls) ExprEvalContexts.back().DelayedDefaultInitializationContext = { CallLoc, Param, CurContext}; // Pass down lifetime extending flag, and collect temporaries in // CreateMaterializeTemporaryExpr when we rewrite the call argument. - keepInLifetimeExtendingContext(); + currentEvaluationContext().InLifetimeExtendingContext = + parentEvaluationContext().InLifetimeExtendingContext; EnsureImmediateInvocationInDefaultArgs Immediate(*this); ExprResult Res; runWithSufficientStackSpace(CallLoc, [&] { @@ -5558,7 +5562,7 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) { Expr *Init = nullptr; bool NestedDefaultChecking = isCheckingDefaultArgumentOrInitializer(); - + bool NeedRebuild = needRebuildDefaultArgOrInit(); EnterExpressionEvaluationContext EvalContext( *this, ExpressionEvaluationContext::PotentiallyEvaluated, Field); @@ -5593,12 +5597,27 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) { ImmediateCallVisitor V(getASTContext()); if (!NestedDefaultChecking) V.TraverseDecl(Field); - if (V.HasImmediateCalls) { + + // CWG1815 + // Support lifetime extension of temporary created by aggregate + // initialization using a default member initializer. We should rebuild + // the initializer in a lifetime extension context if the initializer + // expression is an ExprWithCleanups. Then make sure the normal lifetime + // extension code recurses into the default initializer and does lifetime + // extension when warranted. + bool ContainsAnyTemporaries = + isa_and_present<ExprWithCleanups>(Field->getInClassInitializer()); + if (Field->getInClassInitializer() && + !Field->getInClassInitializer()->containsErrors() && + (V.HasImmediateCalls || (NeedRebuild && ContainsAnyTemporaries))) { ExprEvalContexts.back().DelayedDefaultInitializationContext = {Loc, Field, CurContext}; ExprEvalContexts.back().IsCurrentlyCheckingDefaultArgumentOrInitializer = NestedDefaultChecking; - + // Pass down lifetime extending flag, and collect temporaries in + // CreateMaterializeTemporaryExpr when we rewrite the call argument. + currentEvaluationContext().InLifetimeExtendingContext = + parentEvaluationContext().InLifetimeExtendingContext; EnsureImmediateInvocationInDefaultArgs Immediate(*this); ExprResult Res; runWithSufficientStackSpace(Loc, [&] { @@ -17675,11 +17694,10 @@ void Sema::PopExpressionEvaluationContext() { // Append the collected materialized temporaries into previous context before // exit if the previous also is a lifetime extending context. - auto &PrevRecord = parentEvaluationContext(); if (getLangOpts().CPlusPlus23 && Rec.InLifetimeExtendingContext && - PrevRecord.InLifetimeExtendingContext && + parentEvaluationContext().InLifetimeExtendingContext && !Rec.ForRangeLifetimeExtendTemps.empty()) { - PrevRecord.ForRangeLifetimeExtendTemps.append( + parentEvaluationContext().ForRangeLifetimeExtendTemps.append( Rec.ForRangeLifetimeExtendTemps); } diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index 14feafd1e6b17f..e086c601107041 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -1540,9 +1540,6 @@ Sema::BuildCXXTypeConstructExpr(TypeSourceInfo *TInfo, bool ListInitialization) { QualType Ty = TInfo->getType(); SourceLocation TyBeginLoc = TInfo->getTypeLoc().getBeginLoc(); - - assert((!ListInitialization || Exprs.size() == 1) && - "List initialization must have exactly one expression."); SourceRange FullRange = SourceRange(TyBeginLoc, RParenOrBraceLoc); InitializedEntity Entity = diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index 7dc17187524621..d21b8cb8c04e63 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -750,8 +750,21 @@ void InitListChecker::FillInEmptyInitForField(unsigned Init, FieldDecl *Field, if (Field->hasInClassInitializer()) { if (VerifyOnly) return; - - ExprResult DIE = SemaRef.BuildCXXDefaultInitExpr(Loc, Field); + ExprResult DIE; + { + // Enter a default initializer rebuild context, then we can support + // lifetime extension of temporary created by aggregate initialization + // using a default member initializer. + // CWG1815 (https://wg21.link/CWG1815). + EnterExpressionEvaluationContext RebuildDefaultInit( + SemaRef, Sema::ExpressionEvaluationContext::PotentiallyEvaluated); + SemaRef.currentEvaluationContext().RebuildDefaultArgOrDefaultInit = + true; + SemaRef.currentEvaluationContext().DelayedDefaultInitializationContext = + SemaRef.parentEvaluationContext() + .DelayedDefaultInitializationContext; + DIE = SemaRef.BuildCXXDefaultInitExpr(Loc, Field); + } if (DIE.isInvalid()) { hadError = true; return; @@ -7521,10 +7534,8 @@ Sema::CreateMaterializeTemporaryExpr(QualType T, Expr *Temporary, // are done in both CreateMaterializeTemporaryExpr and MaybeBindToTemporary, // but there may be a chance to merge them. Cleanup.setExprNeedsCleanups(false); - if (isInLifetimeExtendingContext()) { - auto &Record = ExprEvalContexts.back(); - Record.ForRangeLifetimeExtendTemps.push_back(MTE); - } + if (isInLifetimeExtendingContext()) + currentEvaluationContext().ForRangeLifetimeExtendTemps.push_back(MTE); return MTE; } diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index 6df412cbb09c83..bb311e38409280 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -5481,7 +5481,10 @@ void Sema::InstantiateVariableInitializer( EnterExpressionEvaluationContext Evaluated( *this, Sema::ExpressionEvaluationContext::PotentiallyEvaluated, Var); - keepInLifetimeExtendingContext(); + currentEvaluationContext().InLifetimeExtendingContext = + parentEvaluationContext().InLifetimeExtendingContext; + currentEvaluationContext().RebuildDefaultArgOrDefaultInit = + parentEvaluationContext().RebuildDefaultArgOrDefaultInit; // Instantiate the initializer. ExprResult Init; diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 0daf620b4123e4..4bbc024587915c 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -4254,7 +4254,10 @@ ExprResult TreeTransform<Derived>::TransformInitializer(Expr *Init, getSema(), EnterExpressionEvaluationContext::InitList, Construct->isListInitialization()); - getSema().keepInLifetimeExtendingContext(); + getSema().currentEvaluationContext().InLifetimeExtendingContext = + getSema().parentEvaluationContext().InLifetimeExtendingContext; + getSema().currentEvaluationContext().RebuildDefaultArgOrDefaultInit = + getSema().parentEvaluationContext().RebuildDefaultArgOrDefaultInit; SmallVector<Expr*, 8> NewArgs; bool ArgChanged = false; if (getDerived().TransformExprs(Construct->getArgs(), Construct->getNumArgs(), @@ -8924,8 +8927,9 @@ TreeTransform<Derived>::TransformCXXForRangeStmt(CXXForRangeStmt *S) { // P2718R0 - Lifetime extension in range-based for loops. if (getSema().getLangOpts().CPlusPlus23) { - auto &LastRecord = getSema().ExprEvalContexts.back(); + auto &LastRecord = getSema().currentEvaluationContext(); LastRecord.InLifetimeExtendingContext = true; + LastRecord.RebuildDefaultArgOrDefaultInit = true; } StmtResult Init = S->getInit() ? getDerived().TransformStmt(S->getInit()) : StmtResult(); @@ -14443,6 +14447,13 @@ TreeTransform<Derived>::TransformCXXTemporaryObjectExpr( if (TransformExprs(E->getArgs(), E->getNumArgs(), true, Args, &ArgumentChanged)) return ExprError(); + + if (E->isListInitialization() && !E->isStdInitListInitialization()) { + ExprResult Res = RebuildInitList(E->getBeginLoc(), Args, E->getEndLoc()); + if (Res.isInvalid()) + return ExprError(); + Args = {Res.get()}; + } } if (!getDerived().AlwaysRebuild() && @@ -14454,12 +14465,9 @@ TreeTransform<Derived>::TransformCXXTemporaryObjectExpr( return SemaRef.MaybeBindToTemporary(E); } - // FIXME: We should just pass E->isListInitialization(), but we're not - // prepared to handle list-initialization without a child InitListExpr. SourceLocation LParenLoc = T->getTypeLoc().getEndLoc(); return getDerived().RebuildCXXTemporaryObjectExpr( - T, LParenLoc, Args, E->getEndLoc(), - /*ListInitialization=*/LParenLoc.isInvalid()); + T, LParenLoc, Args, E->getEndLoc(), E->isListInitialization()); } template<typename Derived> diff --git a/clang/test/AST/ast-dump-default-init-json.cpp b/clang/test/AST/ast-dump-default-init-json.cpp index 1058b4e3ea4d93..f4949a9c9eedf4 100644 --- a/clang/test/AST/ast-dump-default-init-json.cpp +++ b/clang/test/AST/ast-dump-default-init-json.cpp @@ -789,10 +789,10 @@ void test() { // CHECK-NEXT: "valueCategory": "lvalue", // CHECK-NEXT: "extendingDecl": { // CHECK-NEXT: "id": "0x{{.*}}", -// CHECK-NEXT: "kind": "FieldDecl", -// CHECK-NEXT: "name": "a", +// CHECK-NEXT: "kind": "VarDecl", +// CHECK-NEXT: "name": "b", // CHECK-NEXT: "type": { -// CHECK-NEXT: "qualType": "const A &" +// CHECK-NEXT: "qualType": "B" // CHECK-NEXT: } // CHECK-NEXT: }, // CHECK-NEXT: "storageDuration": "automatic", diff --git a/clang/test/AST/ast-dump-default-init.cpp b/clang/test/AST/ast-dump-default-init.cpp index 15b29f04bf21bf..26864fbf15424d 100644 --- a/clang/test/AST/ast-dump-default-init.cpp +++ b/clang/test/AST/ast-dump-default-init.cpp @@ -13,7 +13,7 @@ void test() { } // CHECK: -CXXDefaultInitExpr 0x{{[^ ]*}} <{{.*}}> 'const A' lvalue has rewritten init // CHECK-NEXT: `-ExprWithCleanups 0x{{[^ ]*}} <{{.*}}> 'const A' lvalue -// CHECK-NEXT: `-MaterializeTemporaryExpr 0x{{[^ ]*}} <{{.*}}> 'const A' lvalue extended by Field 0x{{[^ ]*}} 'a' 'const A &' +// CHECK-NEXT: `-MaterializeTemporaryExpr 0x{{[^ ]*}} <{{.*}}> 'const A' lvalue extended by Var 0x{{[^ ]*}} 'b' 'B' // CHECK-NEXT: `-ImplicitCastExpr 0x{{[^ ]*}} <{{.*}}> 'const A' <NoOp> // CHECK-NEXT: `-CXXFunctionalCastExpr 0x{{[^ ]*}} <{{.*}}> 'A' functional cast to A <NoOp> // CHECK-NEXT: `-InitListExpr 0x{{[^ ]*}} <{{.*}}> 'A' diff --git a/clang/test/Analysis/lifetime-extended-regions.cpp b/clang/test/Analysis/lifetime-extended-regions.cpp index 4e98bd4b0403eb..4458ad294af7cb 100644 --- a/clang/test/Analysis/lifetime-extended-regions.cpp +++ b/clang/test/Analysis/lifetime-extended-regions.cpp @@ -120,10 +120,11 @@ void aggregateWithReferences() { clang_analyzer_dump(viaReference); // expected-warning-re {{&lifetime_extended_object{RefAggregate, viaReference, S{{[0-9]+}}} }} clang_analyzer_dump(viaReference.rx); // expected-warning-re {{&lifetime_extended_object{int, viaReference, S{{[0-9]+}}} }} clang_analyzer_dump(viaReference.ry); // expected-warning-re {{&lifetime_extended_object{Composite, viaReference, S{{[0-9]+}}} }} - - // clang does not currently implement extending lifetime of object bound to reference members of aggregates, - // that are created from default member initializer (see `warn_unsupported_lifetime_extension` from `-Wdangling`) - RefAggregate defaultInitExtended{i}; // clang-bug does not extend `Composite` + + // FIXME: clang currently support extending lifetime of object bound to reference members of aggregates, + // that are created from default member initializer. But CFG and ExprEngine need to be updated to address this change. + // The following expect warning: {{&lifetime_extended_object{Composite, defaultInitExtended, S{{[0-9]+}}} }} + RefAggregate defaultInitExtended{i}; clang_analyzer_dump(defaultInitExtended.ry); // expected-warning {{Unknown }} } diff --git a/clang/test/CXX/drs/cwg16xx.cpp b/clang/test/CXX/drs/cwg16xx.cpp index cf6b45ceabf2cc..95e241f0d03e9b 100644 --- a/clang/test/CXX/drs/cwg... [truncated] `````````` </details> https://github.com/llvm/llvm-project/pull/108039 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits