https://github.com/StefanPaulet created 
https://github.com/llvm/llvm-project/pull/191419

Clang allows friend declarations of closure type members, which is disallowed 
per CWG 1780 (Issue #26540). 

Added a new diagnostic when the friend declaration targets a member of a 
`CXXRecordDecl` that is a lambda.

>From 3e8d8ddd869cb5e41e02f3c590c7e318de0d42dd Mon Sep 17 00:00:00 2001
From: StefanPaulet <[email protected]>
Date: Fri, 10 Apr 2026 16:04:51 +0300
Subject: [PATCH] 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>

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to