https://gcc.gnu.org/g:611d59589d8c48db051b31e727d7f52910a4cbcd
commit r16-1019-g611d59589d8c48db051b31e727d7f52910a4cbcd Author: Jason Merrill <ja...@redhat.com> Date: Fri May 30 08:59:31 2025 -0400 c++: more xobj lambda 'this' capture [PR113563] Nathaniel shared a more extensive test, which revealed more needed fixes. PR c++/113563 gcc/cp/ChangeLog: * lambda.cc (lambda_capture_field_type): Handle 'this' normally. (build_capture_proxy): Special-case 'this' by-ref capture more. (nonlambda_method_basetype): Look through xobj lambdas. gcc/testsuite/ChangeLog: * g++.dg/cpp23/explicit-obj-lambda17.C: New test. Diff: --- gcc/cp/lambda.cc | 28 ++-- gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda17.C | 144 +++++++++++++++++++++ 2 files changed, 161 insertions(+), 11 deletions(-) diff --git a/gcc/cp/lambda.cc b/gcc/cp/lambda.cc index 34c7defb6049..2a9061acf550 100644 --- a/gcc/cp/lambda.cc +++ b/gcc/cp/lambda.cc @@ -218,9 +218,7 @@ lambda_capture_field_type (tree expr, bool explicit_init_p, tree type; bool is_this = is_this_parameter (tree_strip_nop_conversions (expr)); - if (is_this) - type = TREE_TYPE (expr); - else if (explicit_init_p) + if (explicit_init_p) { tree auto_node = make_auto (); @@ -259,7 +257,7 @@ lambda_capture_field_type (tree expr, bool explicit_init_p, type = non_reference (unlowered_expr_type (expr)); - if (by_reference_p || TREE_CODE (type) == FUNCTION_TYPE) + if ((by_reference_p && !is_this) || TREE_CODE (type) == FUNCTION_TYPE) type = build_reference_type (type); } @@ -440,13 +438,21 @@ build_capture_proxy (tree member, tree init) else name = get_identifier (IDENTIFIER_POINTER (DECL_NAME (member)) + 2); - type = lambda_proxy_type (object); - - if (name == this_identifier && !INDIRECT_TYPE_P (TREE_TYPE (member))) + if (name == this_identifier && TYPE_PTR_P (TREE_TYPE (member))) + /* Avoid DECLTYPE_TYPE for by-ref 'this' capture in an xobj lambda; the + constness of the closure doesn't matter just like it doesn't matter to + other by-ref capture. It's simpler to handle this special case here + than in lambda_proxy_type. */ + type = TREE_TYPE (member); + else { - type = build_pointer_type (type); - type = cp_build_qualified_type (type, TYPE_QUAL_CONST); - object = build_fold_addr_expr_with_type (object, type); + type = lambda_proxy_type (object); + if (name == this_identifier) + { + type = build_pointer_type (type); + type = cp_build_qualified_type (type, TYPE_QUAL_CONST); + object = build_fold_addr_expr_with_type (object, type); + } } if (DECL_VLA_CAPTURE_P (member)) @@ -1052,7 +1058,7 @@ nonlambda_method_basetype (void) tree fn = TYPE_CONTEXT (type); if (!fn || TREE_CODE (fn) != FUNCTION_DECL - || !DECL_IOBJ_MEMBER_FUNCTION_P (fn)) + || !DECL_OBJECT_MEMBER_FUNCTION_P (fn)) /* No enclosing non-lambda method. */ return NULL_TREE; if (!LAMBDA_FUNCTION_P (fn)) diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda17.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda17.C new file mode 100644 index 000000000000..ee95fff52b3b --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda17.C @@ -0,0 +1,144 @@ +// PR c++/113563 +// { dg-do run { target c++23 } } + +template <typename T> +T&& move(T& value) { return static_cast<T&&>(value); } + +template <typename T, typename U> constexpr bool is_same = false; +template <typename T> constexpr bool is_same<T, T> = true; + +template <typename T> constexpr bool is_const_ref = false; +template <typename T> constexpr bool is_const_ref<const T> = true; +template <typename T> constexpr bool is_const_ref<const T&> = true; +template <typename T> constexpr bool is_const_ref<const T&&> = true; + +template <typename T> constexpr int refqual = 0; +template <typename T> constexpr int refqual<T&> = 1; +template <typename T> constexpr int refqual<T&&> = 2; + +struct S { + int x; + + // 'this' properly acts as a pointer + auto byref_a() { + return [this](this auto) { return this->x; }; + } + auto byref_b() { + return [this](this auto) { return x; }; + } + auto byref_c() { + return [&](this auto) { return x; }; + } + auto byref_d() { + return [=](this auto) { return x; }; // { dg-warning "implicit capture" } + } + auto byref_e() { + return [this](this auto) { + return [this](this auto) { + return this->x; + }(); + }; + } + auto byref_f() { + return [&](this auto) { + return [&](this auto) { + return x; + }(); + }; + } + + // capturing '*this' stores a copy + auto byval_a() { + return [*this](this auto) { return this->x; }; + } + auto byval_b() { + return [*this](this auto) { return x; }; + } + auto byval_c() { + return [*this](this auto) { + return [=](this auto) { // { dg-warning "implicit capture" } + return this->x; + }(); + }; + } + auto byval_d() { + return [*this](this auto) { + return [&](this auto) { + return x; + }(); + }; + } + + // value category doesn't change with self when capture ref + auto byref_cat_1() { + return [this](this auto&& self) { + static_assert(is_same<decltype((x)), int&>); + }; + } + auto byref_cat_2() const { + return [this](this auto&& self) { + static_assert(is_same<decltype((x)), const int&>); + }; + } + + // value category does change with self when capture val + auto byval_cat() { + return [*this](this auto&& self) { + static_assert(is_const_ref<decltype((x))> == is_const_ref<decltype((self))>); + static_assert(refqual<decltype((x))> == refqual<decltype((self))>); + }; + } +}; + +int main() { + S s{ 5 }; + + auto ra = s.byref_a(); + auto rb = s.byref_b(); + auto rc = s.byref_c(); + auto rd = s.byref_d(); + auto re = s.byref_e(); + auto rf = s.byref_f(); + + auto va = s.byval_a(); + auto vb = s.byval_b(); + auto vc = s.byval_c(); + auto vd = s.byval_d(); + + s.x = 10; + + if (ra() != 10 + || rb() != 10 + || rc() != 10 + || rd() != 10 + || re() != 10 + || rf() != 10) + __builtin_abort(); + + if (va() != 5 + || vb() != 5 + || vc() != 5 + || vd() != 5) + __builtin_abort(); + + auto r_nonconst_1 = s.byref_cat_1(); + const auto r_const_1 = r_nonconst_1; + r_nonconst_1(); + move(r_nonconst_1)(); + r_const_1(); + move(r_nonconst_1)(); + + auto r_nonconst_2 = s.byref_cat_2(); + const auto r_const_2 = r_nonconst_2; + r_nonconst_2(); + move(r_nonconst_2)(); + r_const_2(); + move(r_nonconst_2)(); + + auto v_nonconst = s.byval_cat(); + const auto v_const = v_nonconst; + v_nonconst(); + move(v_nonconst)(); + v_const(); + move(v_nonconst)(); +}