https://github.com/zyn0217 updated https://github.com/llvm/llvm-project/pull/75001
>From 8681b3c9f5e19b6ae977321d5d4154113273c2a0 Mon Sep 17 00:00:00 2001 From: Younan Zhang <zyn7...@gmail.com> Date: Sun, 12 Nov 2023 13:21:03 +0800 Subject: [PATCH 1/2] [clang] Correctly implement CWG 2672 This is a follow-up patch for [D156993](https://reviews.llvm.org/D156993), that marks only the lambda body as non-immediate context. Fixes https://github.com/llvm/llvm-project/issues/71684 --- clang/lib/Sema/SemaTemplateInstantiate.cpp | 11 +------- clang/lib/Sema/TreeTransform.h | 7 +++++ clang/test/CXX/drs/dr26xx.cpp | 2 +- .../expr.prim.lambda/default-arguments.cpp | 6 ++-- .../expr.prim/expr.prim.lambda/p11-1y.cpp | 2 -- .../expr/expr.prim/expr.prim.lambda/p23.cpp | 1 - .../expr/expr.prim/expr.prim.lambda/p4.cpp | 3 +- clang/test/CXX/temp/temp.deduct/p9.cpp | 8 ++---- clang/test/SemaCXX/cxx1y-init-captures.cpp | 8 +++--- clang/test/SemaCXX/cxx1z-lambda-star-this.cpp | 4 +-- clang/test/SemaCXX/lambda-expressions.cpp | 4 +-- clang/test/SemaCXX/lambda-pack-expansion.cpp | 1 - clang/test/SemaCXX/vartemplate-lambda.cpp | 1 - .../SemaCXX/warn-unused-lambda-capture.cpp | 28 +++++++++---------- .../SemaTemplate/instantiate-local-class.cpp | 4 +-- 15 files changed, 37 insertions(+), 53 deletions(-) diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index df6b40999e645c..88bd44f7d6934d 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -35,7 +35,6 @@ #include "clang/Sema/Template.h" #include "clang/Sema/TemplateDeduction.h" #include "clang/Sema/TemplateInstCallback.h" -#include "llvm/ADT/ScopeExit.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/TimeProfiler.h" @@ -1142,8 +1141,7 @@ std::optional<TemplateDeductionInfo *> Sema::isSFINAEContext() const { case CodeSynthesisContext::DeducedTemplateArgumentSubstitution: // We're either substituting explicitly-specified template arguments, // deduced template arguments. SFINAE applies unless we are in a lambda - // expression, see [temp.deduct]p9. - [[fallthrough]]; + // body, see [temp.deduct]p9. case CodeSynthesisContext::ConstraintSubstitution: case CodeSynthesisContext::RequirementInstantiation: case CodeSynthesisContext::RequirementParameterInstantiation: @@ -1444,13 +1442,6 @@ namespace { LocalInstantiationScope Scope(SemaRef, /*CombineWithOuterScope=*/true); Sema::ConstraintEvalRAII<TemplateInstantiator> RAII(*this); - Sema::CodeSynthesisContext C; - C.Kind = clang::Sema::CodeSynthesisContext::LambdaExpressionSubstitution; - C.PointOfInstantiation = E->getBeginLoc(); - SemaRef.pushCodeSynthesisContext(C); - auto PopCtx = - llvm::make_scope_exit([this] { SemaRef.popCodeSynthesisContext(); }); - ExprResult Result = inherited::TransformLambdaExpr(E); if (Result.isInvalid()) return Result; diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 1ad843d0bf4e0c..55e5c3c9dedc56 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -13648,10 +13648,17 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) { getSema().PushExpressionEvaluationContext( Sema::ExpressionEvaluationContext::PotentiallyEvaluated); + Sema::CodeSynthesisContext C; + C.Kind = clang::Sema::CodeSynthesisContext::LambdaExpressionSubstitution; + C.PointOfInstantiation = E->getBody()->getBeginLoc(); + getSema().pushCodeSynthesisContext(C); + // Instantiate the body of the lambda expression. StmtResult Body = Invalid ? StmtError() : getDerived().TransformLambdaBody(E, E->getBody()); + getSema().popCodeSynthesisContext(); + // ActOnLambda* will pop the function scope for us. FuncScopeCleanup.disable(); diff --git a/clang/test/CXX/drs/dr26xx.cpp b/clang/test/CXX/drs/dr26xx.cpp index dd4bb1ff6ae2e1..8a22dbeb98a3d5 100644 --- a/clang/test/CXX/drs/dr26xx.cpp +++ b/clang/test/CXX/drs/dr26xx.cpp @@ -211,7 +211,7 @@ void f(...); template <class T> void bar(T) requires requires { - decltype([]() -> T {})::foo(); + []() -> decltype(T::foo()) {}; }; void bar(...); diff --git a/clang/test/CXX/expr/expr.prim/expr.prim.lambda/default-arguments.cpp b/clang/test/CXX/expr/expr.prim/expr.prim.lambda/default-arguments.cpp index c5d08ec404a7c3..72265d77700aaf 100644 --- a/clang/test/CXX/expr/expr.prim/expr.prim.lambda/default-arguments.cpp +++ b/clang/test/CXX/expr/expr.prim/expr.prim.lambda/default-arguments.cpp @@ -35,8 +35,7 @@ struct NoDefaultCtor { template<typename T> void defargs_in_template_unused(T t) { auto l1 = [](const T& value = T()) { }; // expected-error{{no matching constructor for initialization of 'NoDefaultCtor'}} \ - // expected-note {{in instantiation of default function argument expression for 'operator()<NoDefaultCtor>' required here}} \ - // expected-note {{while substituting into a lambda expression here}} + // expected-note {{in instantiation of default function argument expression for 'operator()<NoDefaultCtor>' required here}} l1(t); } @@ -46,8 +45,7 @@ template void defargs_in_template_unused(NoDefaultCtor); // expected-note{{in i template<typename T> void defargs_in_template_used() { auto l1 = [](const T& value = T()) { }; // expected-error{{no matching constructor for initialization of 'NoDefaultCtor'}} \ - // expected-note {{in instantiation of default function argument expression for 'operator()<NoDefaultCtor>' required here}} \ - // expected-note {{while substituting into a lambda expression here}} + // expected-note {{in instantiation of default function argument expression for 'operator()<NoDefaultCtor>' required here}} l1(); } diff --git a/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p11-1y.cpp b/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p11-1y.cpp index ed36a33850ce45..8e13cc8a7ff564 100644 --- a/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p11-1y.cpp +++ b/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p11-1y.cpp @@ -35,7 +35,6 @@ auto init_kind_2 = [ec = ExplicitCopy()] {}; // expected-error {{no matching con template<typename T> void init_kind_template() { auto init_kind_1 = [ec(T())] {}; auto init_kind_2 = [ec = T()] {}; // expected-error {{no matching constructor}} - // expected-note@-1 {{while substituting into a lambda expression here}} } template void init_kind_template<int>(); template void init_kind_template<ExplicitCopy>(); // expected-note {{instantiation of}} @@ -53,7 +52,6 @@ auto bad_init_6 = [a{overload_fn}] {}; // expected-error {{cannot deduce type fo auto bad_init_7 = [a{{1}}] {}; // expected-error {{cannot deduce type for lambda capture 'a' from nested initializer list}} template<typename...T> void pack_1(T...t) { (void)[a(t...)] {}; } // expected-error {{initializer missing for lambda capture 'a'}} - // expected-note@-1 {{while substituting into a lambda expression here}} template void pack_1<>(); // expected-note {{instantiation of}} // No lifetime-extension of the temporary here. diff --git a/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p23.cpp b/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p23.cpp index a5278c27bf25c2..028fcee5fda433 100644 --- a/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p23.cpp +++ b/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p23.cpp @@ -85,7 +85,6 @@ void init_capture_pack_err(Args ...args) { template<typename ...Args> void init_capture_pack_multi(Args ...args) { [as(args...)] {} (); // expected-error {{initializer missing for lambda capture 'as'}} expected-error {{multiple}} - // expected-note@-1 2{{while substituting into a lambda expression}} } template void init_capture_pack_multi(); // expected-note {{instantiation}} template void init_capture_pack_multi(int); diff --git a/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p4.cpp b/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p4.cpp index d933f765004304..e95a0965a2a35c 100644 --- a/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p4.cpp +++ b/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p4.cpp @@ -55,12 +55,11 @@ void test_result_type(int N) { // expected-note {{declared here}} template <typename T> void test_result_type_tpl(int N) { // expected-note 2{{declared here}} auto l1 = []() -> T {}; // expected-error{{incomplete result type 'Incomplete' in lambda expression}} - // expected-note@-1{{while substituting into a lambda expression here}} typedef int vla[N]; // expected-warning 2{{variable length arrays in C++ are a Clang extension}} \ expected-note 2{{function parameter 'N' with unknown value cannot be used in a constant expression}} auto l2 = []() -> vla {}; // expected-error{{function cannot return array type 'vla' (aka 'int[N]')}} } void test_result_type_call() { - test_result_type_tpl<Incomplete>(10); // expected-note 2{{requested here}} + test_result_type_tpl<Incomplete>(10); // expected-note {{requested here}} } diff --git a/clang/test/CXX/temp/temp.deduct/p9.cpp b/clang/test/CXX/temp/temp.deduct/p9.cpp index 23bcd2a1892e7a..7b661c275211b9 100644 --- a/clang/test/CXX/temp/temp.deduct/p9.cpp +++ b/clang/test/CXX/temp/temp.deduct/p9.cpp @@ -28,18 +28,14 @@ template <class T> auto h(T) -> decltype([x = T::invalid]() { }); void h(...); void test_h() { - h(0); // expected-error@-3 {{type 'int' cannot be used prior to '::'}} - // expected-note@-1 {{while substituting deduced template arguments}} - // expected-note@-5 {{while substituting into a lambda expression here}} + h(0); } template <class T> auto i(T) -> decltype([]() -> typename T::invalid { }); void i(...); void test_i() { - i(0); // expected-error@-3 {{type 'int' cannot be used prior to '::'}} - // expected-note@-1 {{while substituting deduced template arguments}} - // expected-note@-5 {{while substituting into a lambda expression here}} + i(0); } diff --git a/clang/test/SemaCXX/cxx1y-init-captures.cpp b/clang/test/SemaCXX/cxx1y-init-captures.cpp index 4cb53cc8bfc1c5..5340c6c7d0bf34 100644 --- a/clang/test/SemaCXX/cxx1y-init-captures.cpp +++ b/clang/test/SemaCXX/cxx1y-init-captures.cpp @@ -21,17 +21,17 @@ namespace variadic_expansion { return a; }() ...); }; - auto N2 = [x = y, //expected-note3{{begins here}} expected-note 6 {{default capture by}} expected-note 2 {{substituting into a lambda}} + auto N2 = [x = y, //expected-note3{{begins here}} expected-note 6 {{default capture by}} &z = y, n = f(t...), - o = f([&a(t)](T& ... t)->decltype(auto) { return a; }(t...)...)](T& ... s) { // expected-note 6 {{capture 't' by}} - fv([&a(t)]()->decltype(auto) { //expected-error 3{{captured}} expected-note 2{{substituting into a lambda}} + o = f([&a(t)](T& ... t)->decltype(auto) { return a; }(t...)...)](T& ... s) { // expected-note 6 {{capture 't' by}} expected-note {{substituting into a lambda}} + fv([&a(t)]()->decltype(auto) { //expected-error 3{{captured}} return a; }() ...); }; } - void h(int i, char c) { g(i, c); } //expected-note 2{{in instantiation}} + void h(int i, char c) { g(i, c); } // expected-note {{requested here}} } namespace odr_use_within_init_capture { diff --git a/clang/test/SemaCXX/cxx1z-lambda-star-this.cpp b/clang/test/SemaCXX/cxx1z-lambda-star-this.cpp index 0cee41ff5ed384..95bc32b603ddf0 100644 --- a/clang/test/SemaCXX/cxx1z-lambda-star-this.cpp +++ b/clang/test/SemaCXX/cxx1z-lambda-star-this.cpp @@ -46,7 +46,7 @@ class B { template <class T = int> void foo() { (void)[this] { return x; }; - (void)[*this] { return x; }; //expected-error2{{call to deleted}} expected-note {{while substituting into a lambda}} + (void)[*this] { return x; }; //expected-error2{{call to deleted}} } B() = default; @@ -63,7 +63,7 @@ class B { public: template <class T = int> auto foo() { - const auto &L = [*this](auto a) mutable { //expected-error{{call to deleted}} expected-note {{while substituting into a lambda}} + const auto &L = [*this](auto a) mutable { //expected-error{{call to deleted}} d += a; return [this](auto b) { return d += b; }; }; diff --git a/clang/test/SemaCXX/lambda-expressions.cpp b/clang/test/SemaCXX/lambda-expressions.cpp index 1797eef320b865..41cf5a46c38922 100644 --- a/clang/test/SemaCXX/lambda-expressions.cpp +++ b/clang/test/SemaCXX/lambda-expressions.cpp @@ -393,7 +393,7 @@ struct S { namespace PR18473 { template<typename T> void f() { T t(0); - (void) [=]{ int n = t; }; // expected-error {{deleted}} expected-note {{while substituting into a lambda}} + (void) [=]{ int n = t; }; // expected-error {{deleted}} } template void f<int>(); @@ -476,7 +476,7 @@ namespace error_in_transform_prototype { void f(T t) { // expected-error@+2 {{type 'int' cannot be used prior to '::' because it has no members}} // expected-error@+1 {{no member named 'ns' in 'error_in_transform_prototype::S'}} - auto x = [](typename T::ns::type &k) {}; // expected-note 2 {{while substituting into a lambda}} + auto x = [](typename T::ns::type &k) {}; } class S {}; void foo() { diff --git a/clang/test/SemaCXX/lambda-pack-expansion.cpp b/clang/test/SemaCXX/lambda-pack-expansion.cpp index 936e7c6b0e5c5a..e3e968e2704ed9 100644 --- a/clang/test/SemaCXX/lambda-pack-expansion.cpp +++ b/clang/test/SemaCXX/lambda-pack-expansion.cpp @@ -8,7 +8,6 @@ struct X { void take_by_copy(auto &...args) { [...args = args] {}(); // expected-error {{call to deleted constructor}} - // expected-note@-1 {{substituting into a lambda}} } void take_by_ref(auto &...args) { diff --git a/clang/test/SemaCXX/vartemplate-lambda.cpp b/clang/test/SemaCXX/vartemplate-lambda.cpp index d2b53b53dcd492..8b232abe976b63 100644 --- a/clang/test/SemaCXX/vartemplate-lambda.cpp +++ b/clang/test/SemaCXX/vartemplate-lambda.cpp @@ -8,7 +8,6 @@ template<typename T> auto v1 = [](int a = T()) { return a; }(); // expected-error@-1{{cannot initialize a parameter of type 'int' with an rvalue of type 'int *'}} // expected-note@-2{{in instantiation of default function argument expression for 'operator()<int *>' required here}} // expected-note@-3{{passing argument to parameter 'a' here}} -// expected-note@-4{{substituting into a lambda}} struct S { template<class T> diff --git a/clang/test/SemaCXX/warn-unused-lambda-capture.cpp b/clang/test/SemaCXX/warn-unused-lambda-capture.cpp index 4214a3c6ce09a5..73459496e4cd9a 100644 --- a/clang/test/SemaCXX/warn-unused-lambda-capture.cpp +++ b/clang/test/SemaCXX/warn-unused-lambda-capture.cpp @@ -145,38 +145,38 @@ void test_templated() { auto explicit_by_value_used_generic = [i](auto c) { return i + 1; }; auto explicit_by_value_used_void = [i] { (void)i; }; - auto explicit_by_value_unused = [i] {}; // expected-warning{{lambda capture 'i' is not used}} expected-note {{substituting into a lambda}} - auto explicit_by_value_unused_sizeof = [i] { return sizeof(i); }; // expected-warning{{lambda capture 'i' is not required to be captured for this use}} expected-note {{substituting into a lambda}} - auto explicit_by_value_unused_decltype = [i] { decltype(i) j = 0; }; // expected-warning{{lambda capture 'i' is not used}} expected-note {{substituting into a lambda}} - auto explicit_by_value_unused_const = [k] { return k + 1; }; // expected-warning{{lambda capture 'k' is not required to be captured for this use}} expected-note {{substituting into a lambda}} - auto explicit_by_value_unused_const_generic = [k](auto c) { return k + 1; }; // expected-warning{{lambda capture 'k' is not required to be captured for this use}} expected-note {{substituting into a lambda}} + auto explicit_by_value_unused = [i] {}; // expected-warning{{lambda capture 'i' is not used}} + auto explicit_by_value_unused_sizeof = [i] { return sizeof(i); }; // expected-warning{{lambda capture 'i' is not required to be captured for this use}} + auto explicit_by_value_unused_decltype = [i] { decltype(i) j = 0; }; // expected-warning{{lambda capture 'i' is not used}} + auto explicit_by_value_unused_const = [k] { return k + 1; }; // expected-warning{{lambda capture 'k' is not required to be captured for this use}} + auto explicit_by_value_unused_const_generic = [k](auto c) { return k + 1; }; // expected-warning{{lambda capture 'k' is not required to be captured for this use}} auto explicit_by_reference_used = [&i] { i++; }; - auto explicit_by_reference_unused = [&i] {}; // expected-warning{{lambda capture 'i' is not used}} expected-note {{substituting into a lambda}} + auto explicit_by_reference_unused = [&i] {}; // expected-warning{{lambda capture 'i' is not used}} auto explicit_initialized_reference_used = [&j = i] { return j + 1; }; - auto explicit_initialized_reference_unused = [&j = i]{}; // expected-warning{{lambda capture 'j' is not used}} expected-note {{substituting into a lambda}} + auto explicit_initialized_reference_unused = [&j = i]{}; // expected-warning{{lambda capture 'j' is not used}} auto explicit_initialized_value_used = [j = 1] { return j + 1; }; - auto explicit_initialized_value_unused = [j = 1] {}; // expected-warning{{lambda capture 'j' is not used}} expected-note {{substituting into a lambda}} + auto explicit_initialized_value_unused = [j = 1] {}; // expected-warning{{lambda capture 'j' is not used}} auto explicit_initialized_value_non_trivial_constructor = [j = NonTrivialConstructor()]{}; auto explicit_initialized_value_non_trivial_destructor = [j = NonTrivialDestructor()]{}; - auto explicit_initialized_value_trivial_init = [j = Trivial()]{}; // expected-warning{{lambda capture 'j' is not used}} expected-note {{substituting into a lambda}} + auto explicit_initialized_value_trivial_init = [j = Trivial()]{}; // expected-warning{{lambda capture 'j' is not used}} auto explicit_initialized_value_non_trivial_init = [j = Trivial(42)]{}; auto explicit_initialized_value_with_side_effect = [j = side_effect()]{}; auto explicit_initialized_value_generic_used = [i = 1](auto c) mutable { i++; }; - auto explicit_initialized_value_generic_unused = [i = 1](auto c) mutable {}; // expected-warning{{lambda capture 'i' is not used}} expected-note {{substituting into a lambda}} + auto explicit_initialized_value_generic_unused = [i = 1](auto c) mutable {}; // expected-warning{{lambda capture 'i' is not used}} auto nested = [&i] { // expected-note {{substituting into a lambda}} auto explicit_by_value_used = [i] { return i + 1; }; - auto explicit_by_value_unused = [i] {}; // expected-warning{{lambda capture 'i' is not used}} expected-note {{substituting into a lambda}} + auto explicit_by_value_unused = [i] {}; // expected-warning{{lambda capture 'i' is not used}} }; Trivial trivial; - auto explicit_by_value_trivial = [trivial] {}; // expected-warning{{lambda capture 'trivial' is not used}} expected-note {{substituting into a lambda}} + auto explicit_by_value_trivial = [trivial] {}; // expected-warning{{lambda capture 'trivial' is not used}} NonTrivialConstructor cons; - auto explicit_by_value_non_trivial_constructor = [cons] {}; // expected-warning{{lambda capture 'cons' is not used}} expected-note {{substituting into a lambda}} + auto explicit_by_value_non_trivial_constructor = [cons] {}; // expected-warning{{lambda capture 'cons' is not used}} NonTrivialCopyConstructor copy_cons; auto explicit_by_value_non_trivial_copy_constructor = [copy_cons] {}; @@ -189,7 +189,7 @@ void test_templated() { } void test_use_template() { - test_templated<int>(); // expected-note 13{{in instantiation of function template specialization 'test_templated<int>' requested here}} + test_templated<int>(); // expected-note 3 {{requested here}} } namespace pr35555 { diff --git a/clang/test/SemaTemplate/instantiate-local-class.cpp b/clang/test/SemaTemplate/instantiate-local-class.cpp index f9553e334c7043..47591045fd26e5 100644 --- a/clang/test/SemaTemplate/instantiate-local-class.cpp +++ b/clang/test/SemaTemplate/instantiate-local-class.cpp @@ -473,8 +473,7 @@ namespace rdar23721638 { template <typename T> void bar() { auto lambda = [](T a = "") {}; // expected-error {{conversion function from 'const char[1]' to 'rdar23721638::A' invokes a deleted function}} \ // expected-note {{in instantiation of default function argument expression for 'operator()<rdar23721638::A>' required here}} \ - // expected-note {{passing argument to parameter 'a' here}} \ - // expected-note {{while substituting into a lambda}} + // expected-note {{passing argument to parameter 'a' here}} lambda(); } template void bar<A>(); // expected-note {{in instantiation}} @@ -497,7 +496,6 @@ namespace PR45000 { // expected-error@-1 {{cannot initialize a parameter of type 'int' with an rvalue of type 'std::nullptr_t'}} // expected-note@-2 {{in instantiation of default function argument expression for 'operator()<int>' required here}} // expected-note@-3 {{passing argument to parameter 'x' here}} - // expected-note@-4 {{while substituting into a lambda}} void g() { f<int>(); } // expected-note@-1 {{in instantiation of default function argument expression for 'f<int>' required here}} >From d4243f8cd7d5c814306bff80ab798375aae05f5a Mon Sep 17 00:00:00 2001 From: Younan Zhang <zyn7...@gmail.com> Date: Fri, 5 Jan 2024 18:02:48 +0800 Subject: [PATCH 2/2] Mention #71684 in the release note --- clang/docs/ReleaseNotes.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index b4b5352a306c1c..09e1429f1071ec 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -704,7 +704,8 @@ Bug Fixes to C++ Support - Clang emits an error on substitution failure within lambda body inside a requires-expression. This fixes: - (`#64138 <https://github.com/llvm/llvm-project/issues/64138>`_). + (`#64138 <https://github.com/llvm/llvm-project/issues/64138>`_) and + (`#71684 <https://github.com/llvm/llvm-project/issues/71684>`_). - Update ``FunctionDeclBitfields.NumFunctionDeclBits``. This fixes: (`#64171 <https://github.com/llvm/llvm-project/issues/64171>`_). _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits