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)();
+}

Reply via email to