[clang] [clang] Add diagnostic for friend declaration of closure type member (PR #191419)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Apr 14 06:49:55 PDT 2026
https://github.com/StefanPaulet updated https://github.com/llvm/llvm-project/pull/191419
>From 3e8d8ddd869cb5e41e02f3c590c7e318de0d42dd Mon Sep 17 00:00:00 2001
From: StefanPaulet <tudor.stefan.paulet at gmail.com>
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 at +3 {{non-constexpr declaration of 'operator()' follows constexpr declaration}}
// expected-error at +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 <tudor.stefan.paulet at gmail.com>
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>
More information about the cfe-commits
mailing list