[clang] [clang] Add diagnostic for friend declaration of closure type member (PR #191419)
via cfe-commits
cfe-commits at lists.llvm.org
Fri Apr 10 06:50:10 PDT 2026
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: StefanPaulet
<details>
<summary>Changes</summary>
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.
---
Full diff: https://github.com/llvm/llvm-project/pull/191419.diff
6 Files Affected:
- (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+2)
- (modified) clang/lib/Sema/SemaDeclCXX.cpp (+5)
- (modified) clang/test/SemaCXX/cxx1z-constexpr-lambdas.cpp (+2-2)
- (modified) clang/test/SemaCXX/lambda-expressions.cpp (+50-8)
- (modified) clang/test/SemaTemplate/GH75426.cpp (+1-2)
- (modified) clang/test/SemaTemplate/concepts.cpp (+1-1)
``````````diff
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>
``````````
</details>
https://github.com/llvm/llvm-project/pull/191419
More information about the cfe-commits
mailing list