https://github.com/StefanPaulet updated https://github.com/llvm/llvm-project/pull/191419
>From 3e8d8ddd869cb5e41e02f3c590c7e318de0d42dd Mon Sep 17 00:00:00 2001 From: StefanPaulet <[email protected]> Date: Fri, 10 Apr 2026 16:04:51 +0300 Subject: [PATCH 1/2] Add diagnostic for friend declaration of lambda member --- .../clang/Basic/DiagnosticSemaKinds.td | 2 + clang/lib/Sema/SemaDeclCXX.cpp | 5 ++ .../test/SemaCXX/cxx1z-constexpr-lambdas.cpp | 4 +- clang/test/SemaCXX/lambda-expressions.cpp | 58 ++++++++++++++++--- clang/test/SemaTemplate/GH75426.cpp | 3 +- clang/test/SemaTemplate/concepts.cpp | 2 +- 6 files changed, 61 insertions(+), 13 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 6d2fae551566f..6563dc11e39f1 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -8907,6 +8907,8 @@ let CategoryName = "Lambda Issue" in { "capture of variable '%0' as type %1 calls %select{private|protected}3 " "%select{default |copy |move |*ERROR* |*ERROR* |*ERROR* |}2constructor">, AccessControl; + def err_friend_lambda_decl : Error< + "a member of a lambda should not be the target of a friend declaration">; def note_lambda_to_block_conv : Note< "implicit capture of lambda object due to conversion to block pointer " "here">; diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index c1d3960e65ef6..ba1b4c4e5ef7e 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -18465,6 +18465,11 @@ NamedDecl *Sema::ActOnFriendFunctionDecl(Scope *S, Declarator &D, Diag(Loc, diag::err_introducing_special_friend) << DiagArg; return nullptr; } + } else { + CXXRecordDecl *RC = dyn_cast<CXXRecordDecl>(DC); + if (RC->isLambda()) { + Diag(NameInfo.getBeginLoc(), diag::err_friend_lambda_decl); + } } // FIXME: This is an egregious hack to cope with cases where the scope stack diff --git a/clang/test/SemaCXX/cxx1z-constexpr-lambdas.cpp b/clang/test/SemaCXX/cxx1z-constexpr-lambdas.cpp index aa8d055e44971..20922f2fdaa4f 100644 --- a/clang/test/SemaCXX/cxx1z-constexpr-lambdas.cpp +++ b/clang/test/SemaCXX/cxx1z-constexpr-lambdas.cpp @@ -57,8 +57,8 @@ struct test_never_constant { // expected-error@+3 {{non-constexpr declaration of 'operator()' follows constexpr declaration}} // expected-error@+3 {{non-constexpr declaration of 'operator()' follows constexpr declaration}} #endif - friend auto decltype(never_constant_1)::operator()() const; - friend int decltype(never_constant_2)::operator()() const; + friend auto decltype(never_constant_1)::operator()() const; // expected-error {{a member of a lambda should not be the target of a friend declaration}} + friend int decltype(never_constant_2)::operator()() const; // expected-error {{a member of a lambda should not be the target of a friend declaration}} }; } // end ns test_constexpr_checking diff --git a/clang/test/SemaCXX/lambda-expressions.cpp b/clang/test/SemaCXX/lambda-expressions.cpp index 00c52efb78a7d..6ae7ac9888b41 100644 --- a/clang/test/SemaCXX/lambda-expressions.cpp +++ b/clang/test/SemaCXX/lambda-expressions.cpp @@ -657,26 +657,29 @@ namespace ConversionOperatorDoesNotHaveDeducedReturnType { struct X { #if __cplusplus > 201402L - friend constexpr auto T::operator()(int) const; - friend constexpr T::operator ExpectedTypeT() const noexcept; + friend constexpr auto T::operator()(int) const; // expected-error {{a member of a lambda should not be the target of a friend declaration}} + friend constexpr T::operator ExpectedTypeT() const noexcept; // expected-error {{a member of a lambda should not be the target of a friend declaration}} template<typename T> - friend constexpr void U::operator()(T&) const; + friend constexpr void U::operator()(T&) const; // expected-error {{a member of a lambda should not be the target of a friend declaration}} // FIXME: This should not match; the return type is specified as behaving // "as if it were a decltype-specifier denoting the return type of // [operator()]", which is not equivalent to this alias template. template<typename T> - friend constexpr U::operator ExpectedTypeU<T>() const noexcept; + friend constexpr U::operator ExpectedTypeU<T>() const noexcept; // expected-error {{a member of a lambda should not be the target of a friend declaration}} #else friend auto T::operator()(int) const; // cxx11-error {{'auto' return without trailing return type; deduced return types are a C++14 extension}} \ - cxx03-error {{'auto' not allowed in function return type}} - friend T::operator ExpectedTypeT() const; + cxx03-error {{'auto' not allowed in function return type}} \ + expected-error {{a member of a lambda should not be the target of a friend declaration}} + friend T::operator ExpectedTypeT() const; // expected-error {{a member of a lambda should not be the target of a friend declaration}} template<typename T> - friend void U::operator()(T&) const; // cxx03-cxx11-error {{friend declaration of 'operator()' does not match any declaration}} + friend void U::operator()(T&) const; // cxx03-cxx11-error {{friend declaration of 'operator()' does not match any declaration}} \ + expected-error {{a member of a lambda should not be the target of a friend declaration}} // FIXME: This should not match, as above. template<typename T> - friend U::operator ExpectedTypeU<T>() const; // cxx03-cxx11-error {{friend declaration of 'operator void (*)(type-parameter-0-0 &)' does not match any declaration}} + friend U::operator ExpectedTypeU<T>() const; // cxx03-cxx11-error {{friend declaration of 'operator void (*)(type-parameter-0-0 &)' does not match any declaration}} \ + expected-error {{a member of a lambda should not be the target of a friend declaration}} #endif private: @@ -812,3 +815,42 @@ void test_lambda_return_type() { }; } } + +namespace GH26540 { +#if __cplusplus >= 201703L +#define CONSTEXPR17 constexpr +#define NOEXCEPT17 noexcept +#else +#define CONSTEXPR17 +#define NOEXCEPT17 +#endif + auto l = []() -> int { + return 5; + }; + using L = decltype(l); + using ConvertL = int(*)(); + + class NonGenericLambdaFriend { + friend CONSTEXPR17 int L::operator()() const; // expected-error{{a member of a lambda should not be the target of a friend declaration}} + friend CONSTEXPR17 L::operator ConvertL() const NOEXCEPT17; // expected-error{{a member of a lambda should not be the target of a friend declaration}} + }; + +#if __cplusplus > 201103L + auto gl = [](auto t) -> int { + return t.x; + }; + using GL = decltype(gl); + template <typename T> + using ConvertGL = int(*)(T); + + class GenericLambdaFriend { + int x{2}; + template <typename T> + friend CONSTEXPR17 auto GL::operator()(T t) const -> int; // expected-error{{a member of a lambda should not be the target of a friend declaration}} + template <typename T> + friend CONSTEXPR17 GL::operator ConvertGL<T>() const NOEXCEPT17; // expected-error{{a member of a lambda should not be the target of a friend declaration}} + }; +#endif +#undef NOEXCEPT17 +#undef CONSTEXPR17 +} diff --git a/clang/test/SemaTemplate/GH75426.cpp b/clang/test/SemaTemplate/GH75426.cpp index faf70699f9c5f..776db332358a8 100644 --- a/clang/test/SemaTemplate/GH75426.cpp +++ b/clang/test/SemaTemplate/GH75426.cpp @@ -1,5 +1,4 @@ // RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %s -// expected-no-diagnostics template<typename T> concept C = true; @@ -12,5 +11,5 @@ auto L = []<C T>{}; template<typename X> class Friends { template<C T> friend void A::f(); - template<C T> friend void decltype(L)::operator()(); + template<C T> friend void decltype(L)::operator()(); // expected-error {{a member of a lambda should not be the target of a friend declaration}} }; diff --git a/clang/test/SemaTemplate/concepts.cpp b/clang/test/SemaTemplate/concepts.cpp index ac80d16b4ccf8..b2faf13d53033 100644 --- a/clang/test/SemaTemplate/concepts.cpp +++ b/clang/test/SemaTemplate/concepts.cpp @@ -849,7 +849,7 @@ template<typename T, typename U> concept C = true; template<typename T> auto L = []<C<T> U>() {}; struct Q { - template<C<int> U> friend constexpr auto decltype(L<int>)::operator()() const; + template<C<int> U> friend constexpr auto decltype(L<int>)::operator()() const; // expected-error {{a member of a lambda should not be the target of a friend declaration}} }; template <class T> >From 1eddfbf5f7c481b9418230e90f7e284aa676a049 Mon Sep 17 00:00:00 2001 From: StefanPaulet <[email protected]> Date: Tue, 14 Apr 2026 16:43:50 +0300 Subject: [PATCH 2/2] Added release note; Added test in cwg and regenerated dr status html --- clang/docs/ReleaseNotes.rst | 1 + clang/test/CXX/drs/cwg17xx.cpp | 16 ++++++++++++++++ clang/www/cxx_dr_status.html | 2 +- 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index b2e62106506f0..99281237b4ada 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -408,6 +408,7 @@ Bug Fixes in This Version - Fixed incorrect rejection of ``auto`` with reordered declaration specifiers in C23. (#GH164121) - Fixed a crash where constexpr evaluation encountered invalid overrides. (#GH183290) - Fixed a crash when assigning to an element of an ``ext_vector_type`` with ``bool`` element type. (#GH189260) +- Clang now emits an error for friend declarations of lambda members. (#GH26540) Bug Fixes to Compiler Builtins ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/test/CXX/drs/cwg17xx.cpp b/clang/test/CXX/drs/cwg17xx.cpp index 46693eaa5a169..228182b12805e 100644 --- a/clang/test/CXX/drs/cwg17xx.cpp +++ b/clang/test/CXX/drs/cwg17xx.cpp @@ -222,3 +222,19 @@ template <template <typename> class Template, typename Argument> using Bind = Instantiate<Internal<Template>::template Bind, Argument>; #endif } // namespace cwg1794 + +namespace cwg1780 { // cwg1780: 22 +#if __cplusplus >= 201103L + +auto l = []() -> int { return 5; }; +using L = decltype(l); +class A { +#if __cplusplus >= 201703L + friend constexpr auto L::operator()() const -> int; // expected-error{{a member of a lambda should not be the target of a friend declaration}} +#else + friend auto L::operator()() const -> int; // expected-error{{a member of a lambda should not be the target of a friend declaration}} +#endif +}; + +#endif +} // namespace cwg1780 diff --git a/clang/www/cxx_dr_status.html b/clang/www/cxx_dr_status.html index cb0236b46e580..864fe425867c7 100755 --- a/clang/www/cxx_dr_status.html +++ b/clang/www/cxx_dr_status.html @@ -12245,7 +12245,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2> <td>[<a href="https://wg21.link/expr.prim.lambda.closure">expr.prim.lambda.closure</a>]</td> <td>CD4</td> <td>Explicit instantiation/specialization of generic lambda <TT>operator()</TT></td> - <td class="unknown" align="center">Unknown</td> + <td class="unreleased" align="center">Clang 22</td> </tr> <tr id="1781"> <td><a href="https://cplusplus.github.io/CWG/issues/1781.html">1781</a></td> _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
