On Saturday, January 6th, 2024 at 5:17 PM, Jakub Jelinek <ja...@redhat.com> 
wrote:


> 
> 
> On Sun, Jan 07, 2024 at 12:05:50AM +0000, waffl3x wrote:
> 
> > Bootstrapped and tested on x86_64-linux with no regressions.
> > 
> > That's it for now. If I manage to finish CWG2586 in time I guess I'll
> > submit it as patch 5/4? I'm definitely locked in on all these changes
> > unless there's a really good reason.
> 
> 
> No patch attached nor included here...
> 
> Jakub

MY BAD, thank you very much. 

Alex
From aac380e6f7e7fddfa7acf3c166a75bda56d54f7a Mon Sep 17 00:00:00 2001
From: Waffl3x <waff...@protonmail.com>
Date: Sat, 6 Jan 2024 16:29:45 -0700
Subject: [PATCH 4/4] C++23 P0847R7 (deducing this) - xobj lambdas. [PR102609]

This implements support for xobj lambdas.  There are extensive tests included,
but not exhaustive.  Dependent lambdas should work and have been tested
lightly, but we need more exhaustive tests for them.

	PR c++/102609

gcc/cp/ChangeLog:

	PR c++/102609
	C++23 P0847R7 (deducing this) - xobj lambdas.
	* lambda.cc (build_capture_proxy): Don't fold direct object types.
	* parser.cc (cp_parser_lambda_declarator_opt): Handle xobj lambdas,
	diagnostics.  Comments also updated.
	* pt.cc (tsubst_function_decl): Handle xobj lambdas.  Check object
	type of xobj lambda call operator, diagnose incorrect types.
	(tsubst_lambda_expr): Update comment.
	* semantics.cc (finish_decltype_type): Also consider by-value object
	parameter qualifications.

gcc/testsuite/ChangeLog:

	PR c++/102609
	C++23 P0847R7 (deducing this) - xobj lambdas.
	* g++.dg/cpp23/explicit-obj-diagnostics8.C: New test.
	* g++.dg/cpp23/explicit-obj-lambda1.C: New test.
	* g++.dg/cpp23/explicit-obj-lambda10.C: New test.
	* g++.dg/cpp23/explicit-obj-lambda11.C: New test.
	* g++.dg/cpp23/explicit-obj-lambda12.C: New test.
	* g++.dg/cpp23/explicit-obj-lambda13.C: New test.
	* g++.dg/cpp23/explicit-obj-lambda2.C: New test.
	* g++.dg/cpp23/explicit-obj-lambda3.C: New test.
	* g++.dg/cpp23/explicit-obj-lambda4.C: New test.
	* g++.dg/cpp23/explicit-obj-lambda5.C: New test.
	* g++.dg/cpp23/explicit-obj-lambda6.C: New test.
	* g++.dg/cpp23/explicit-obj-lambda7.C: New test.
	* g++.dg/cpp23/explicit-obj-lambda8.C: New test.
	* g++.dg/cpp23/explicit-obj-lambda9.C: New test.

Signed-off-by: Waffl3x <waff...@protonmail.com>
---
 gcc/cp/lambda.cc                              |   4 +-
 gcc/cp/parser.cc                              |  84 +-
 gcc/cp/pt.cc                                  |  74 +-
 gcc/cp/semantics.cc                           |  10 +-
 .../g++.dg/cpp23/explicit-obj-diagnostics8.C  |  68 ++
 .../g++.dg/cpp23/explicit-obj-lambda1.C       |  25 +
 .../g++.dg/cpp23/explicit-obj-lambda10.C      |  39 +
 .../g++.dg/cpp23/explicit-obj-lambda11.C      |  46 +
 .../g++.dg/cpp23/explicit-obj-lambda12.C      | 103 +++
 .../g++.dg/cpp23/explicit-obj-lambda13.C      | 103 +++
 .../g++.dg/cpp23/explicit-obj-lambda2.C       |  23 +
 .../g++.dg/cpp23/explicit-obj-lambda3.C       |  64 ++
 .../g++.dg/cpp23/explicit-obj-lambda4.C       |  23 +
 .../g++.dg/cpp23/explicit-obj-lambda5.C       |  21 +
 .../g++.dg/cpp23/explicit-obj-lambda6.C       | 873 ++++++++++++++++++
 .../g++.dg/cpp23/explicit-obj-lambda7.C       |  20 +
 .../g++.dg/cpp23/explicit-obj-lambda8.C       |  87 ++
 .../g++.dg/cpp23/explicit-obj-lambda9.C       |  46 +
 18 files changed, 1699 insertions(+), 14 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics8.C
 create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda1.C
 create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda10.C
 create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda11.C
 create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda12.C
 create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda13.C
 create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda2.C
 create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda3.C
 create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda4.C
 create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda5.C
 create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda6.C
 create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda7.C
 create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda8.C
 create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda9.C

diff --git a/gcc/cp/lambda.cc b/gcc/cp/lambda.cc
index fc6a0708b66..09a9f148252 100644
--- a/gcc/cp/lambda.cc
+++ b/gcc/cp/lambda.cc
@@ -404,8 +404,10 @@ build_capture_proxy (tree member, tree init)
   fn = lambda_function (closure);
   lam = CLASSTYPE_LAMBDA_EXPR (closure);
 
+  object = DECL_ARGUMENTS (fn);
   /* The proxy variable forwards to the capture field.  */
-  object = build_fold_indirect_ref (DECL_ARGUMENTS (fn));
+  if (INDIRECT_TYPE_P (TREE_TYPE (object)))
+    object = build_fold_indirect_ref (object);
   object = finish_non_static_data_member (member, object, NULL_TREE);
   if (REFERENCE_REF_P (object))
     object = TREE_OPERAND (object, 0);
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index f3b228d3251..ec8314b0642 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -11791,8 +11791,12 @@ cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr)
   else if (cxx_dialect < cxx23)
     omitted_parms_loc = cp_lexer_peek_token (parser->lexer)->location;
 
-  /* In the decl-specifier-seq of the lambda-declarator, each
-     decl-specifier shall either be mutable or constexpr.  */
+  /* [expr.prim.lambda.general]
+     lambda-specifier:
+	consteval, constexpr, mutable, static
+     [4] A lambda-specifier-seq shall contain at most one of each
+	 lambda-specifier and shall not contain both constexpr and consteval.
+	 The lambda-specifier-seq shall not contain both mutable and static.  */
   int declares_class_or_enum;
   if (cp_lexer_next_token_is_decl_specifier_keyword (parser->lexer))
     cp_parser_decl_specifier_seq (parser,
@@ -11807,13 +11811,83 @@ cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr)
 	       "%<-std=gnu++2b%>");
       omitted_parms_loc = UNKNOWN_LOCATION;
     }
-
-  if (lambda_specs.storage_class == sc_mutable)
+  /* Peek at the params, see if we have an xobj parameter.  */
+  if (param_list && TREE_PURPOSE (param_list) == this_identifier)
+    {
+      quals = TYPE_UNQUALIFIED;
+      /* We still need grokdeclarator to see that this is an xobj function
+	 and finish the rest of the work, don't mutate it.  */
+      tree const xobj_param = TREE_VALUE (param_list);
+      tree const param_type = TREE_TYPE (xobj_param);
+      /* [expr.prim.lambda.closure-5]
+	 Given a lambda with a lambda-capture, the type of the explicit object
+	 parameter, if any, of the lambda's function call operator (possibly
+	 instantiated from a function call operator template) shall be either:
+	 -- the closure type,
+	 -- a class type derived from the closure type, or
+	 -- a reference to a possibly cv-qualified such type.  */
+      bool const unrelated_with_captures
+	= (LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (lambda_expr) != CPLD_NONE
+	   || LAMBDA_EXPR_CAPTURE_LIST (lambda_expr))
+	  /* Since a lambda's type is anonymous, we can assume an xobj
+	     parameter is unrelated to the closure if it is non-dependent.
+	     If it is dependent we handle it at instantiation time.  */
+	  && !WILDCARD_TYPE_P (non_reference (param_type));
+      if (unrelated_with_captures)
+	{
+	  error_at (DECL_SOURCE_LOCATION (xobj_param),
+		    "a lambda with captures may not have an explicit object "
+		    "parameter of an unrelated type");
+	  LAMBDA_EXPR_CAPTURE_LIST (lambda_expr) = NULL_TREE;
+	}
+
+      /* [expr.prim.lambda.general-4]
+	 If the lambda-declarator contains an explicit object parameter
+	 ([dcl.fct]), then no lambda-specifier in the lambda-specifier-seq
+	 shall be mutable or static.  */
+      if (lambda_specs.storage_class == sc_mutable)
+	{
+	  auto_diagnostic_group d;
+	  error_at (lambda_specs.locations[ds_storage_class],
+		    "%<mutable%> lambda specifier "
+		    "with explicit object parameter");
+	  /* Tell the user how to do what they probably meant, maybe fixits
+	     would be appropriate later?  */
+	  if (unrelated_with_captures)
+	    /* The following hints don't make sense when we already have an
+	       unrelated type with captures, don't emit them.  */;
+	  else if (!TYPE_REF_P (param_type))
+	    inform (DECL_SOURCE_LOCATION (xobj_param),
+		    "the passed in closure object will not be mutated because "
+		    "it is taken by value");
+	  else if (TYPE_READONLY (TREE_TYPE (param_type)))
+	    inform (DECL_SOURCE_LOCATION (xobj_param),
+		    "declare the explicit object parameter as non-const "
+		    "reference instead");
+	  else
+	    inform (DECL_SOURCE_LOCATION (xobj_param),
+		    "explicit object parameter is already a mutable "
+		    "reference");
+	}
+      else if (lambda_specs.storage_class == sc_static)
+	{
+	  auto_diagnostic_group d;
+	  error_at (lambda_specs.locations[ds_storage_class],
+		    "%<static%> lambda specifier "
+		    "with explicit object parameter");
+	  inform (DECL_SOURCE_LOCATION (xobj_param),
+		  "explicit object parameter declared here");
+	}
+    }
+  else if (lambda_specs.storage_class == sc_mutable)
     {
       quals = TYPE_UNQUALIFIED;
     }
   else if (lambda_specs.storage_class == sc_static)
     {
+      /* [expr.prim.lambda.general-4]
+	 If the lambda-specifier-seq contains static, there shall be no
+	 lambda-capture.  */
       if (LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (lambda_expr) != CPLD_NONE
 	  || LAMBDA_EXPR_CAPTURE_LIST (lambda_expr))
 	error_at (lambda_specs.locations[ds_storage_class],
@@ -11938,7 +12012,7 @@ cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr)
       {
 	DECL_INITIALIZED_IN_CLASS_P (fco) = 1;
 	DECL_ARTIFICIAL (fco) = 1;
-	if (!LAMBDA_EXPR_STATIC_P (lambda_expr))
+	if (DECL_IOBJ_MEMBER_FUNCTION_P (fco))
 	  /* Give the object parameter a different name.  */
 	  DECL_NAME (DECL_ARGUMENTS (fco)) = closure_identifier;
 	DECL_SET_LAMBDA_FUNCTION (fco, true);
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index 72eb124d116..8d11f3cf2fd 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -14482,9 +14482,9 @@ tsubst_function_decl (tree t, tree args, tsubst_flags_t complain,
   tree ctx = closure ? closure : DECL_CONTEXT (t);
   bool member = ctx && TYPE_P (ctx);
 
-  /* If this is a static lambda, remove the 'this' pointer added in
+  /* If this is a static or xobj lambda, remove the 'this' pointer added in
      tsubst_lambda_expr now that we know the closure type.  */
-  if (lambda_fntype && DECL_STATIC_FUNCTION_P (t))
+  if (lambda_fntype && !DECL_IOBJ_MEMBER_FUNCTION_P (t))
     lambda_fntype = static_fn_type (lambda_fntype);
 
   if (member && !closure)
@@ -14559,12 +14559,12 @@ tsubst_function_decl (tree t, tree args, tsubst_flags_t complain,
     DECL_NAME (r) = make_conv_op_name (TREE_TYPE (type));
 
   tree parms = DECL_ARGUMENTS (t);
-  if (closure && !DECL_STATIC_FUNCTION_P (t))
+  if (closure && DECL_IOBJ_MEMBER_FUNCTION_P (t))
     parms = DECL_CHAIN (parms);
   parms = tsubst (parms, args, complain, t);
   for (tree parm = parms; parm; parm = DECL_CHAIN (parm))
     DECL_CONTEXT (parm) = r;
-  if (closure && !DECL_STATIC_FUNCTION_P (t))
+  if (closure && DECL_IOBJ_MEMBER_FUNCTION_P (t))
     {
       tree tparm = build_this_parm (r, closure, type_memfn_quals (type));
       DECL_NAME (tparm) = closure_identifier;
@@ -14600,6 +14600,66 @@ tsubst_function_decl (tree t, tree args, tsubst_flags_t complain,
       && !grok_op_properties (r, /*complain=*/false))
     return error_mark_node;
 
+  /* If we are looking at an xobj lambda, we might need to check the type of
+     its xobj parameter.  */
+  if (LAMBDA_FUNCTION_P (r) && DECL_XOBJ_MEMBER_FUNCTION_P (r))
+    {
+      tree closure_obj = DECL_CONTEXT (r);
+      tree lambda_expr = CLASSTYPE_LAMBDA_EXPR (closure_obj);
+      tree obj_param = TREE_TYPE (DECL_ARGUMENTS (r));
+
+      if (!(LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (lambda_expr) != CPLD_NONE
+	    || LAMBDA_EXPR_CAPTURE_LIST (lambda_expr)))
+	/* If a lambda has an empty capture clause, an xobj parameter of
+	   unrelated type is not an error.  */;
+      else if (dependent_type_p (obj_param))
+	/* If we are coming from tsubst_lambda_expr we might not have
+	   substituted into our xobj parameter yet.  We can't error out until
+	   we know what the type really is so do nothing...
+	   ...but if we are instantiating the call op for real and we don't
+	   have a real type then something has gone incredibly wrong.  */
+	gcc_assert (lambda_fntype);
+      else
+	{
+	  /* We have a lambda with captures, and know the type of the xobj
+	     parameter, time to check it.  */
+	  tree obj_param_type = TYPE_MAIN_VARIANT (non_reference (obj_param));
+	  if (!same_or_base_type_p (closure_obj, obj_param_type))
+	    {
+	      /* This error does not emit when the lambda's call operator
+		 template is instantiated by taking its address, such as in
+		 the following case:
+
+		 auto f = [x = 0](this auto&&){};
+		 int (*fp)(int&) = &decltype(f)::operator();
+
+		 It only emits when explicitly calling the call operator with
+		 an explicit template parameter:
+
+		 template<typename T>
+		 struct S : T {
+		   using T::operator();
+		   operator int() const {return {};}
+		 };
+
+		 auto s = S{[x = 0](this auto&&) {}};
+		 s.operator()<int>();
+
+		 This is due to resolve_address_of_overloaded_function being
+		 deficient at reporting candidates when overload resolution
+		 fails.
+
+		 This diagnostic will be active in the first case if/when
+		 resolve_address_of_overloaded_function is fixed to properly
+		 emit candidates upon failure to resolve to an overload.  */
+	      if (complain & tf_error)
+		error ("a lambda with captures may not have an explicit "
+		       "object parameter of an unrelated type");
+	      return error_mark_node;
+	    }
+	}
+    }
+
   /* Associate the constraints directly with the instantiation. We
      don't substitute through the constraints; that's only done when
      they are checked.  */
@@ -19571,7 +19631,11 @@ tsubst_lambda_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl)
 	 which would be skipped if cp_unevaluated_operand.  */
       cp_evaluated ev;
 
-      /* Fix the type of 'this'.  */
+      /* Fix the type of 'this'.
+	 For static and xobj member functions we use this to transport the
+	 lambda's closure type.  It appears that in the regular case the
+	 object parameter is still pulled off, and then re-added again anyway.
+	 So perhaps we could do something better here?  */
       fntype = build_memfn_type (fntype, type,
 				 type_memfn_quals (fntype),
 				 type_memfn_rqual (fntype));
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index c0c97600483..08d58fc16e2 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -11850,9 +11850,13 @@ finish_decltype_type (tree expr, bool id_expression_or_member_access_p,
 	      if (WILDCARD_TYPE_P (non_reference (obtype)))
 		/* We don't know what the eventual obtype quals will be.  */
 		goto dependent;
-	      int quals = cp_type_quals (type);
-	      if (INDIRECT_TYPE_P (obtype))
-		quals |= cp_type_quals (TREE_TYPE (obtype));
+	      auto direct_type = [](tree t){
+		  if (INDIRECT_TYPE_P (t))
+		    return TREE_TYPE (t);
+		  return t;
+	       };
+	      int const quals = cp_type_quals (type)
+			      | cp_type_quals (direct_type (obtype));
 	      type = cp_build_qualified_type (type, quals);
 	      type = build_reference_type (type);
 	    }
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics8.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics8.C
new file mode 100644
index 00000000000..7b75dc97a71
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics8.C
@@ -0,0 +1,68 @@
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+// xobj lambda with invalid decl specs
+
+void test()
+{
+  auto f0 = [](this auto) mutable {}; // { dg-line line_f0 }
+  auto f1 = [](this auto&) mutable {}; // { dg-line line_f1 }
+  auto f2 = [](this auto const&) mutable {}; // { dg-line line_f2 }
+  auto f3 = [](this auto&&) mutable {}; // { dg-line line_f3 }
+
+  auto g0 = [](this auto) static {}; // { dg-line line_g0 }
+  auto g1 = [](this auto&) static {}; // { dg-line line_g1 }
+  auto g2 = [](this auto const&) static {}; // { dg-line line_g2 }
+  auto g3 = [](this auto&&) static {}; // { dg-line line_g3 }
+
+  auto fc0 = [n = 0](this auto) mutable {}; // { dg-line line_fc0 }
+  auto fc1 = [n = 0](this auto&) mutable {}; // { dg-line line_fc1 }
+  auto fc2 = [n = 0](this auto const&) mutable {}; // { dg-line line_fc2 }
+  auto fc3 = [n = 0](this auto&&) mutable {}; // { dg-line line_fc3 }
+
+  auto gc0 = [n = 0](this auto) static {}; // { dg-line line_gc0 }
+  auto gc1 = [n = 0](this auto&) static {}; // { dg-line line_gc1 }
+  auto gc2 = [n = 0](this auto const&) static {}; // { dg-line line_gc2 }
+  auto gc3 = [n = 0](this auto&&) static {}; // { dg-line line_gc3 }
+}
+
+// { dg-error {'mutable' lambda specifier with explicit object parameter} {} { target *-*-* } line_f0 }
+// { dg-error {'mutable' lambda specifier with explicit object parameter} {} { target *-*-* } line_f1 }
+// { dg-error {'mutable' lambda specifier with explicit object parameter} {} { target *-*-* } line_f2 }
+// { dg-error {'mutable' lambda specifier with explicit object parameter} {} { target *-*-* } line_f3 }
+
+// { dg-note {the passed in closure object will not be mutated because it is taken by value} {} { target *-*-* } line_f0 }
+// { dg-note {explicit object parameter is already a mutable reference} {} { target *-*-* } line_f1 }
+// { dg-note {declare the explicit object parameter as non-const reference instead} {} { target *-*-* } line_f2 }
+// { dg-note {explicit object parameter is already a mutable reference} {} { target *-*-* } line_f3 }
+
+// { dg-error {'static' lambda specifier with explicit object parameter} {} { target *-*-* } line_g0 }
+// { dg-error {'static' lambda specifier with explicit object parameter} {} { target *-*-* } line_g1 }
+// { dg-error {'static' lambda specifier with explicit object parameter} {} { target *-*-* } line_g2 }
+// { dg-error {'static' lambda specifier with explicit object parameter} {} { target *-*-* } line_g3 }
+
+// { dg-note {explicit object parameter declared here} {} { target *-*-* } line_g0 }
+// { dg-note {explicit object parameter declared here} {} { target *-*-* } line_g1 }
+// { dg-note {explicit object parameter declared here} {} { target *-*-* } line_g2 }
+// { dg-note {explicit object parameter declared here} {} { target *-*-* } line_g3 }
+
+// { dg-error {'mutable' lambda specifier with explicit object parameter} {} { target *-*-* } line_fc0 }
+// { dg-error {'mutable' lambda specifier with explicit object parameter} {} { target *-*-* } line_fc1 }
+// { dg-error {'mutable' lambda specifier with explicit object parameter} {} { target *-*-* } line_fc2 }
+// { dg-error {'mutable' lambda specifier with explicit object parameter} {} { target *-*-* } line_fc3 }
+
+// { dg-note {the passed in closure object will not be mutated because it is taken by value} {} { target *-*-* } line_fc0 }
+// { dg-note {explicit object parameter is already a mutable reference} {} { target *-*-* } line_fc1 }
+// { dg-note {declare the explicit object parameter as non-const reference instead} {} { target *-*-* } line_fc2 }
+// { dg-note {explicit object parameter is already a mutable reference} {} { target *-*-* } line_fc3 }
+
+// { dg-error {'static' lambda specifier with explicit object parameter} {} { target *-*-* } line_gc0 }
+// { dg-error {'static' lambda specifier with explicit object parameter} {} { target *-*-* } line_gc1 }
+// { dg-error {'static' lambda specifier with explicit object parameter} {} { target *-*-* } line_gc2 }
+// { dg-error {'static' lambda specifier with explicit object parameter} {} { target *-*-* } line_gc3 }
+
+// { dg-note {explicit object parameter declared here} {} { target *-*-* } line_gc0 }
+// { dg-note {explicit object parameter declared here} {} { target *-*-* } line_gc1 }
+// { dg-note {explicit object parameter declared here} {} { target *-*-* } line_gc2 }
+// { dg-note {explicit object parameter declared here} {} { target *-*-* } line_gc3 }
+
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda1.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda1.C
new file mode 100644
index 00000000000..86e0471eb7f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda1.C
@@ -0,0 +1,25 @@
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+// lambda declaration with xobj parameter
+
+struct S{};
+
+void test()
+{
+  (void)[](this auto&& self){};
+  (void)[](this auto& self){};
+  (void)[](this auto const& self){};
+  (void)[](this auto self){};
+
+  (void)[](this S&& self){};
+  (void)[](this S& self){};
+  (void)[](this S const& self){};
+  (void)[](this S self){};
+
+  (void)[x = 0](this auto&& self){};
+  (void)[x = 0](this auto& self){};
+  (void)[x = 0](this auto const& self){};
+  (void)[x = 0](this auto self){};
+}
+
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda10.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda10.C
new file mode 100644
index 00000000000..715a2457945
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda10.C
@@ -0,0 +1,39 @@
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+// instantiating captureless lambda call operator with unrelated explicit object parameter
+
+void test0()
+{
+  auto f0 = [](this auto self) { return self; };
+  auto fp0_value     = static_cast<int(*)(int)        >(&decltype(f0)::operator());
+  auto fp0_lref      = static_cast<int(*)(int&)       >(&decltype(f0)::operator());
+  auto fp0_rref      = static_cast<int(*)(int&&)      >(&decltype(f0)::operator());
+  auto fp0_constlref = static_cast<int(*)(int const&) >(&decltype(f0)::operator());
+  auto fp0_constrref = static_cast<int(*)(int const&&)>(&decltype(f0)::operator());
+
+  auto f1 = [](this auto&& self) { return self; };
+  auto fp1_value      = static_cast<int(*)(int)        >(&decltype(f1)::operator()); // { dg-error {invalid 'static_cast' from type} }
+  auto fp1_lref       = static_cast<int(*)(int&)       >(&decltype(f1)::operator());
+  auto fp1_rref       = static_cast<int(*)(int&&)      >(&decltype(f1)::operator());
+  auto fp1_constlref  = static_cast<int(*)(int const&) >(&decltype(f1)::operator());
+  auto fp1_constrref  = static_cast<int(*)(int const&&)>(&decltype(f1)::operator());
+}
+
+void test1()
+{
+  auto f0 = [](this auto self) { return self; };
+  int (*fp0_value)(int)             = &decltype(f0)::operator();
+  int (*fp0_lref)(int&)             = &decltype(f0)::operator();
+  int (*fp0_rref)(int&&)            = &decltype(f0)::operator();
+  int (*fp0_constlref)(int const&)  = &decltype(f0)::operator();
+  int (*fp0_constrref)(int const&&) = &decltype(f0)::operator();
+
+  auto f1 = [](this auto&& self) { return self; };
+  int (*fp1_value)(int)              = &decltype(f1)::operator(); // { dg-error {no matches converting function} }
+  int (*fp1_lref)(int&)              = &decltype(f1)::operator();
+  int (*fp1_rref)(int&&)             = &decltype(f1)::operator();
+  int (*fp1_constlref)(int const&)   = &decltype(f1)::operator();
+  int (*fp1_constrref)(int const&&)  = &decltype(f1)::operator();
+}
+
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda11.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda11.C
new file mode 100644
index 00000000000..7f2bdb809d3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda11.C
@@ -0,0 +1,46 @@
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+// unrelated xobj parameter type in captureless lambdas and lambdas with captures
+
+struct S0{};
+
+void test0()
+{
+  auto f0 = [](this S0){ return 5; }; // { dg-bogus "a lambda with captures may not have an explicit object parameter of an unrelated type" }
+  auto f1 = [x = 42](this S0){ return 5; }; // { dg-error "a lambda with captures may not have an explicit object parameter of an unrelated type" }
+}
+
+// instantiation by calling with explicit template arguments
+
+template<typename T>
+struct S1 : T {
+  using T::operator();
+  operator int() const {return {};}
+};
+
+void test1()
+{
+  auto s0 = S1{[](this auto&& self) { return self; }}; // { dg-bogus {a lambda with captures may not have an explicit object parameter of an unrelated type} }
+  s0.operator()<int>(); // { dg-bogus {no matching function for call to} }
+
+  auto s1 = S1{[x = 0](this auto&& self) { return self; }}; // { dg-line t1_s1 }
+  s1.operator()<int>(); // { dg-error {no matching function for call to} }
+}
+// { dg-note {candidate:} {} { target *-*-* } t1_s1 }
+// { dg-note {template argument deduction/substitution failed} {} { target *-*-* } t1_s1 }
+// { dg-error {a lambda with captures may not have an explicit object parameter of an unrelated type} {} { target *-*-* } t1_s1 }
+
+// instantiation from overload resolution when taking address of call operator
+
+void test2()
+{
+  auto f = [x = 42](this auto&&){ return x; }; // { dg-line t2_f }
+
+  int (*fp0)(decltype(f)&) = &decltype(f)::operator();
+  int (*fp1)(int&) = &decltype(f)::operator(); // { dg-error {no matches converting function} }
+}
+
+// { dg-error "a lambda with captures may not have an explicit object parameter of an unrelated type" {depends on PR112874} { xfail *-*-* } t2_f }
+// { dg-note "candidate is" "" { target *-*-* } t2_f }
+
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda12.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda12.C
new file mode 100644
index 00000000000..8b6c2bacc07
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda12.C
@@ -0,0 +1,103 @@
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+// SFINAE when the call operator for a lambda with captures is instantiated
+// with an unrelated type.
+// diagnose ambiguous overloads when the call operator for a captureless lambda is instantiated
+// with an unrelated type.
+
+// overload resolution when taking address of function
+
+/* [expr.prim.lambda.general-5]
+
+   Given a lambda with a lambda-capture, the type of the explicit object
+   parameter, if any, of the lambda's function call operator (possibly
+   instantiated from a function call operator template) shall be either:
+
+   --(5.1) the closure type,
+   --(5.2) a class type derived from the closure type, or
+   --(5.3) a reference to a possibly cv-qualified such type.  */
+
+// The above wording is similar to [dcl.fct-15] which is handled by SFINAE,
+// thus we also handle the following cases the same way.
+
+// We need the 2 overloads to be ambiguous to observe substitution failure
+// for the lambda's call operator when instantiated with an unrelated type.
+// We accomplish this by introducing both overloads through using declarations.
+
+struct B0 {
+  void operator()(this auto) {}
+};
+template<typename T>
+struct S0 : T, B0 {
+  using B0::operator();
+  using T::operator();
+};
+
+void test0()
+{
+  auto s0 = S0{[](this auto){}};
+  void (*p0)(int) = &decltype(s0)::operator(); // { dg-error {converting overloaded function '[^\n\r]+' to type '[^\n\r]+' is ambiguous} }
+
+  auto s1 = S0{[x = 42](this auto){}};
+  void (*p1)(int) = &decltype(s1)::operator(); // { dg-bogus {converting overloaded function '[^\n\r]+' to type '[^\n\r]+' is ambiguous} {Substitution failure for a captureful lambda with an unrelated xobj parameter type failed!} }
+}
+
+struct B1 {
+  void operator()(this auto&&) {}
+};
+template<typename T>
+struct S1 : T, B1 {
+  using B1::operator();
+  using T::operator();
+};
+
+void test1()
+{
+  auto s0 = S1{[](this auto&&){}};
+  void (*p0)(int&) = &decltype(s0)::operator(); // { dg-error {converting overloaded function '[^\n\r]+' to type '[^\n\r]+' is ambiguous} }
+
+  auto s1 = S1{[x = 42](this auto&&){}};
+  void (*p1)(int&) = &decltype(s1)::operator(); // { dg-bogus {converting overloaded function '[^\n\r]+' to type '[^\n\r]+' is ambiguous} {Substitution failure for a captureful lambda with an unrelated xobj parameter type failed!} }
+}
+
+
+struct B2 {
+  // not a template, should be taken over the lambda's call operator
+  void operator()(this int&) {}
+};
+template<typename T>
+struct S2 : T, B2 {
+  using T::operator();
+  using B2::operator();
+};
+
+void test2()
+{
+  auto s0 = S2{[](this auto&&){}};
+  void (*p0)(int&) = &decltype(s0)::operator(); // { dg-bogus {converting overloaded function '[^\n\r]+' to type '[^\n\r]+' is ambiguous} }
+
+  auto s1 = S2{[x = 42](this auto&&){}};
+  void (*p1)(int&) = &decltype(s1)::operator(); // { dg-bogus {converting overloaded function '[^\n\r]+' to type '[^\n\r]+' is ambiguous} }
+}
+
+struct B3 {
+  // must be a template so it is not taken over the lambda's call operator
+  template<typename U = void>
+  void operator()(this int&) {}
+};
+template<typename T>
+struct S3 : T, B3 {
+  using B3::operator();
+  using T::operator();
+};
+
+void test3()
+{
+  auto s0 = S3{[](this auto&&){}};
+  void (*p0)(int&) = &decltype(s0)::operator(); // { dg-error {converting overloaded function '[^\n\r]+' to type '[^\n\r]+' is ambiguous} }
+
+  auto s1 = S3{[x = 42](this auto&&){}};
+  void (*p1)(int&) = &decltype(s1)::operator(); // { dg-bogus {converting overloaded function '[^\n\r]+' to type '[^\n\r]+' is ambiguous} {Substitution failure for a captureful lambda with an unrelated xobj parameter type failed!} }
+}
+
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda13.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda13.C
new file mode 100644
index 00000000000..c48de47e1f9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda13.C
@@ -0,0 +1,103 @@
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+// SFINAE when the call operator for a lambda with captures is instantiated
+// with an unrelated type.
+// diagnose ambiguous overloads when the call operator for a captureless lambda is instantiated
+// with an unrelated type.
+
+// overload resolution from call expression
+
+/* [expr.prim.lambda.general-5]
+
+   Given a lambda with a lambda-capture, the type of the explicit object
+   parameter, if any, of the lambda's function call operator (possibly
+   instantiated from a function call operator template) shall be either:
+
+   --(5.1) the closure type,
+   --(5.2) a class type derived from the closure type, or
+   --(5.3) a reference to a possibly cv-qualified such type.  */
+
+// The above wording is similar to [dcl.fct-15] which is handled by SFINAE,
+// thus we also handle the following cases the same way.
+
+// We need the 2 overloads to be ambiguous to observe substitution failure
+// for the lambda's call operator when instantiated with an unrelated type.
+// We accomplish this by introducing both overloads through using declarations.
+
+struct B0 {
+  void operator()(this auto) {}
+};
+
+template<typename T>
+struct S0 : T, B0 {
+  using T::operator();
+  using B0::operator();
+  operator int() const {return {};}
+};
+
+void test0()
+{
+  auto s0 = S0{[](this auto){}};
+  s0.operator()<int>(); // { dg-error {call of overloaded 'operator\(\)\(\)' is ambiguous} }
+
+  auto s1 = S0{[x = 42](this auto){}};
+  s1.operator()<int>(); // { dg-bogus {call of overloaded 'operator\(\)\(\)' is ambiguous} }
+}
+
+
+struct B1 {
+  void operator()(this auto&&) {}
+};
+template<typename T>
+struct S1 : T, B1 {
+  using T::operator();
+  using B1::operator();
+  operator int() const {return {};}
+};
+
+void test1()
+{
+  auto s0 = S1{[](this auto&&){}};
+  s0.operator()<int>(); // { dg-error {call of overloaded 'operator\(\)\(\)' is ambiguous} }
+
+  auto s1 = S1{[x = 42](this auto&&){}};
+  s1.operator()<int>(); // { dg-bogus {call of overloaded 'operator\(\)\(\)' is ambiguous} }
+}
+
+
+struct B2 {
+  // needs to be a template, we are explicitly passing a template argument,
+  // without the parameter here this would not be a candidate
+  template<typename U = void>
+  void operator()(this int) {}
+};
+
+template<typename T>
+struct S2 : T, B2 {
+  using T::operator();
+  using B2::operator();
+  operator int() const {return {};}
+};
+
+// I don't know why the calls to s0::operator() are not ambiguous, it might have to do with one taking less conversions, I'm not sure.
+// Someone who knows better should remove those cases if they are sure they are actually correct.
+
+void test2()
+{
+  auto s0 = S2{[](this auto){}};
+  s0.operator()<int>(); // { dg-error {call of overloaded 'operator\(\)\(\)' is ambiguous} {Not sure if this is a bug, one might be a better conversion} { xfail *-*-* } }
+
+  auto s1 = S2{[x = 42](this auto){}};
+  s1.operator()<int>(); // { dg-bogus {call of overloaded 'operator\(\)\(\)' is ambiguous} }
+}
+
+void test3()
+{
+  auto s0 = S2{[](this auto&&){}};
+  s0.operator()<int>(); // { dg-error {call of overloaded 'operator\(\)\(\)' is ambiguous} {Not sure if this is a bug, one might be a better conversion} { xfail *-*-* } }
+
+  auto s1 = S2{[x = 42](this auto&&){}};
+  s1.operator()<int>(); // { dg-bogus {call of overloaded 'operator\(\)\(\)' is ambiguous} }
+}
+
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda2.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda2.C
new file mode 100644
index 00000000000..827197a6667
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda2.C
@@ -0,0 +1,23 @@
+// P0847R7
+// { dg-do run { target c++23 } }
+
+// recursive lambdas
+
+inline constexpr int correct_result = 5 + 4 + 3 + 2 + 1; 
+
+int main()
+{
+  auto cl0 = [](this auto&& self, int n)      -> int { return n ? self(n - 1) + n : 0; };
+  auto cl1 = [](this auto const& self, int n) -> int { return n ? self(n - 1) + n : 0; };
+  auto cl2 = [](this auto self, int n)        -> int { return n ? self(n - 1) + n : 0; };
+  auto cl3 = [](this auto&& self, int n)     { if (!n) return 0; else return self(n - 1) + n; };
+  auto cl4 = [](this auto const& self, int n){ if (!n) return 0; else return self(n - 1) + n; };
+  auto cl5 = [](this auto self, int n)       { if (!n) return 0; else return self(n - 1) + n; };
+  if (cl0(5) != correct_result) __builtin_abort ();
+  if (cl1(5) != correct_result) __builtin_abort ();
+  if (cl2(5) != correct_result) __builtin_abort ();
+  if (cl3(5) != correct_result) __builtin_abort ();
+  if (cl4(5) != correct_result) __builtin_abort ();
+  if (cl5(5) != correct_result) __builtin_abort ();
+}
+
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda3.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda3.C
new file mode 100644
index 00000000000..9d222b0e547
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda3.C
@@ -0,0 +1,64 @@
+// P0847R7
+// { dg-do run { target c++23 } }
+
+// an adaptation of one of the examples in P0847R7
+
+struct Leaf { };
+struct Node;
+
+struct Tree {
+  enum class stored {leaf, node};
+  stored _discriminator;
+  union {
+    Leaf _leaf;
+    Node* _node;
+  };
+  Tree(Leaf) : _discriminator(stored::leaf), _leaf() {}
+  Tree(Node& node) : _discriminator(stored::node), _node(&node) {}
+};
+
+struct Node {
+    Tree left;
+    Tree right;
+};
+
+template<typename Visitor>
+auto visit_tree(Visitor&& visitor, Tree const& tree)
+{
+  switch (tree._discriminator)
+  {
+    case Tree::stored::leaf:
+      return visitor (tree._leaf);
+    case Tree::stored::node:
+      return visitor (tree._node);
+    default:
+      __builtin_abort (); 
+  }
+}
+
+template<typename... Ts>
+struct overload : Ts... { using Ts::operator()...; };
+
+int main()
+{
+  static constexpr int true_num_leaves = 8;
+  Node branch0{.left = Leaf{}, .right = Leaf{}};
+  Node branch1{.left = Leaf{}, .right = branch0};
+  Node branch2{.left = Leaf{}, .right = Leaf{}};
+  Node branch3{.left = branch1, .right = branch2};
+  Node branch4{.left = branch3, .right = Leaf{}};
+  Node branch5{.left = Leaf{}, .right = Leaf{}};
+  Node branch6{.left = branch4, .right = branch5};
+
+  Tree root (branch6);
+
+  int num_leaves = visit_tree (overload{
+    [](Leaf const&) { return 1; },
+    [](this auto const& self, Node* n) -> int {
+      return visit_tree (self, n->left) + visit_tree (self, n->right);
+    }},
+    root);
+  if (num_leaves != true_num_leaves)
+    __builtin_abort ();
+}
+
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda4.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda4.C
new file mode 100644
index 00000000000..6ce42ebefa8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda4.C
@@ -0,0 +1,23 @@
+// P0847R7
+// { dg-do run { target c++23 } }
+
+// calls to call operator of a lambda with captures with an implicit object argument
+// that derives from the lambda closure object
+
+template<typename T>
+struct S : T {
+    using T::operator();
+};
+
+template<typename T>
+S(T) -> S<T>; 
+
+int main()
+{
+  static constexpr int magic = 42;
+  int n = magic;
+  S s{[n](this auto&&){return n;}};
+  if (s () != magic)
+    __builtin_abort ();
+}
+
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda5.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda5.C
new file mode 100644
index 00000000000..88d45d90db1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda5.C
@@ -0,0 +1,21 @@
+// P0847R7
+// { dg-do run { target c++23 } }
+
+// calls to (captureless) lambda with explicit object parameter of unrelated type
+// with an appropriate converting constructor
+
+inline constexpr int magic = 42;
+
+struct S {
+  int _v;  
+  template<typename T>
+  S(T) : _v(magic) {}
+};
+
+int main()
+{
+  auto f = [](this S self){ return self._v; };
+  if (f () != magic)
+    __builtin_abort ();
+}
+
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda6.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda6.C
new file mode 100644
index 00000000000..aa563064eab
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda6.C
@@ -0,0 +1,873 @@
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+// decltype((x)) and decltype(x) in explicit object lambda
+
+template<typename T> inline constexpr bool is_const_v = false;
+template<typename T> inline constexpr bool is_const_v<T const> = true;
+
+template<typename T> inline constexpr bool is_lvalue_ref = false;
+template<typename T> inline constexpr bool is_lvalue_ref<T&> = true;
+
+void non_dep()
+{
+  int n = 0;
+  int const c = 0;
+  // value
+  auto f0_value = [=]<typename Self>(this Self){
+    static_assert(is_lvalue_ref<decltype((n))>,
+		  "decltype((n)) is not an lvalue ref");
+    static_assert(is_const_v<Self> == is_const_v<__remove_reference (decltype((n)))>, // { dg-bogus {static assertion failed: qualification of decltype\(\(n\)\) does not match qualification of Self} }
+		  "qualification of decltype((n)) does not match qualification of Self");
+    static_assert(__is_same (__remove_cvref (decltype((n))), int),
+		  "decltype((n)) is not an int");
+    static_assert(__is_same (decltype(n), int));
+
+    static_assert(is_lvalue_ref<decltype((c))>,
+		  "decltype((c)) is not an lvalue ref");
+    static_assert(is_const_v<__remove_reference (decltype((c)))>,
+		  "qualification of decltype((c)) is not const");
+    static_assert(__is_same (__remove_cvref (decltype((c))), int),
+		  "decltype((c)) is not an int");
+    static_assert(__is_same (decltype(c), int const));
+  };
+  f0_value();
+  f0_value.operator()<decltype(f0_value) const>();
+
+  auto f1_value = [&]<typename Self>(this Self){
+    static_assert(__is_same (decltype((n)), int&));
+    static_assert(__is_same (decltype(n), int));
+
+    static_assert(__is_same (decltype((c)), int const&));
+    static_assert(__is_same (decltype(c), int const));
+  };
+  f1_value();
+  f1_value.operator()<decltype(f1_value) const>();
+
+  auto f2_value = [n, c]<typename Self>(this Self){
+    static_assert(is_lvalue_ref<decltype((n))>,
+		  "decltype((n)) is not an lvalue ref");
+    static_assert(is_const_v<Self> == is_const_v<__remove_reference (decltype((n)))>, // { dg-bogus {static assertion failed: qualification of decltype\(\(n\)\) does not match qualification of Self} }
+		  "qualification of decltype((n)) does not match qualification of Self");
+    static_assert(__is_same (__remove_cvref (decltype((n))), int),
+		  "decltype((n)) is not an int");
+    static_assert(__is_same (decltype(n), int));
+
+    static_assert(is_lvalue_ref<decltype((c))>,
+		  "decltype((c)) is not an lvalue ref");
+    static_assert(is_const_v<__remove_reference (decltype((c)))>,
+		  "qualification of decltype((c)) is not const");
+    static_assert(__is_same (__remove_cvref (decltype((c))), int),
+		  "decltype((c)) is not an int");
+    static_assert(__is_same (decltype(c), int const));
+  };
+  f2_value();
+  f2_value.operator()<decltype(f2_value) const>();
+
+  auto f3_value = [&n, &c]<typename Self>(this Self){
+    static_assert(__is_same (decltype((n)), int&));
+    static_assert(__is_same (decltype(n), int));
+
+    static_assert(__is_same (decltype((c)), int const&));
+    static_assert(__is_same (decltype(c), int const));
+  };
+  f3_value();
+  f3_value.operator()<decltype(f3_value) const>();
+
+  auto f4_value = []<typename Self>(this Self){
+    static_assert(__is_same (decltype(n), int));
+    static_assert(__is_same (decltype((n)), int&));
+
+    static_assert(__is_same (decltype(c), int const));
+    static_assert(__is_same (decltype((c)), int const&));
+  };
+  f4_value();
+  f4_value.operator()<decltype(f4_value) const>();
+
+  // ref
+  auto f0_ref = [=]<typename Self>(this Self&){
+    static_assert(is_lvalue_ref<decltype((n))>,
+		  "decltype((n)) is not an lvalue ref");
+    static_assert(is_const_v<Self> == is_const_v<__remove_reference (decltype((n)))>, // { dg-bogus {static assertion failed: qualification of decltype\(\(n\)\) does not match qualification of Self} }
+		  "qualification of decltype((n)) does not match qualification of Self");
+    static_assert(__is_same (__remove_cvref (decltype((n))), int),
+		  "decltype((n)) is not an int");
+    static_assert(__is_same (decltype(n), int));
+
+    static_assert(is_lvalue_ref<decltype((c))>,
+		  "decltype((c)) is not an lvalue ref");
+    static_assert(is_const_v<__remove_reference (decltype((c)))>,
+		  "qualification of decltype((c)) is not const");
+    static_assert(__is_same (__remove_cvref (decltype((c))), int),
+		  "decltype((c)) is not an int");
+    static_assert(__is_same (decltype(c), int const));
+  };
+  f0_ref();
+  f0_ref.operator()<decltype(f0_ref) const>();
+  static_cast<decltype(f0_ref) const&>(f0_ref)();
+
+  auto f1_ref = [&]<typename Self>(this Self&){
+    static_assert(__is_same (decltype((n)), int&));
+    static_assert(__is_same (decltype(n), int));
+
+    static_assert(__is_same (decltype((c)), int const&));
+    static_assert(__is_same (decltype(c), int const));
+  };
+  f1_ref();
+  f1_ref.operator()<decltype(f1_ref) const>();
+  static_cast<decltype(f1_ref) const&>(f1_ref)();
+
+  auto f2_ref = [n, c]<typename Self>(this Self&){
+    static_assert(is_lvalue_ref<decltype((n))>,
+		  "decltype((n)) is not an lvalue ref");
+    static_assert(is_const_v<Self> == is_const_v<__remove_reference (decltype((n)))>, // { dg-bogus {static assertion failed: qualification of decltype\(\(n\)\) does not match qualification of Self} }
+		  "qualification of decltype((n)) does not match qualification of Self");
+    static_assert(__is_same (__remove_cvref (decltype((n))), int),
+		  "decltype((n)) is not an int");
+    static_assert(__is_same (decltype(n), int));
+
+    static_assert(is_lvalue_ref<decltype((c))>,
+		  "decltype((c)) is not an lvalue ref");
+    static_assert(is_const_v<__remove_reference (decltype((c)))>,
+		  "qualification of decltype((c)) is not const");
+    static_assert(__is_same (__remove_cvref (decltype((c))), int),
+		  "decltype((c)) is not an int");
+    static_assert(__is_same (decltype(c), int const));
+  };
+  f2_ref();
+  f2_ref.operator()<decltype(f2_ref) const>();
+  static_cast<decltype(f2_ref) const&>(f2_ref)();
+
+  auto f3_ref = [&n, &c]<typename Self>(this Self&){
+    static_assert(__is_same(decltype((n)), int&));
+    static_assert(__is_same (decltype(n), int));
+
+    static_assert(__is_same (decltype((c)), int const&));
+    static_assert(__is_same (decltype(c), int const));
+  };
+  f3_ref();
+  f3_ref.operator()<decltype(f3_ref) const>();
+  static_cast<decltype(f3_ref) const&>(f3_ref)();
+
+  auto f4_ref = []<typename Self>(this Self&){
+    static_assert(__is_same (decltype(n), int));
+    static_assert(__is_same (decltype((n)), int&));
+
+    static_assert(__is_same (decltype(c), int const));
+    static_assert(__is_same (decltype((c)), int const&));
+  };
+  f4_ref();
+  f4_ref.operator()<decltype(f4_ref) const>();
+  static_cast<decltype(f4_ref) const&>(f4_ref)();
+
+  // const value
+  auto f0_const_value = [=]<typename Self>(this Self const){
+    static_assert(is_lvalue_ref<decltype((n))>,
+		  "decltype((n)) is not an lvalue ref");
+    static_assert(is_const_v<__remove_reference (decltype((n)))>, // { dg-bogus {static assertion failed: qualification of decltype\(\(n\)\) does not match qualification of Self} }
+		  "qualification of decltype((n)) does not match qualification of Self");
+    static_assert(__is_same (__remove_cvref (decltype((n))), int),
+		  "decltype((n)) is not an int");
+    static_assert(__is_same (decltype(n), int));
+
+    static_assert(is_lvalue_ref<decltype((c))>,
+		  "decltype((c)) is not an lvalue ref");
+    static_assert(is_const_v<__remove_reference (decltype((c)))>,
+		  "qualification of decltype((c)) is not const");
+    static_assert(__is_same (__remove_cvref (decltype((c))), int),
+		  "decltype((c)) is not an int");
+    static_assert(__is_same (decltype(c), int const));
+  };
+  f0_const_value();
+
+  auto f1_const_value = [&]<typename Self>(this Self const){
+    static_assert(__is_same (decltype((n)), int&));
+    static_assert(__is_same (decltype(n), int));
+
+    static_assert(__is_same (decltype((c)), int const&));
+    static_assert(__is_same (decltype(c), int const));
+  };
+  f1_const_value();
+
+  auto f2_const_value = [n, c]<typename Self>(this Self const){
+    static_assert(is_lvalue_ref<decltype((n))>,
+		  "decltype((n)) is not an lvalue ref");
+    static_assert(is_const_v<__remove_reference (decltype((n)))>, // { dg-bogus {static assertion failed: qualification of decltype\(\(n\)\) does not match qualification of Self} }
+		  "qualification of decltype((n)) does not match qualification of Self");
+    static_assert(__is_same (__remove_cvref (decltype((n))), int),
+		  "decltype((n)) is not an int");
+    static_assert(__is_same (decltype(n), int));
+
+    static_assert(is_lvalue_ref<decltype((c))>,
+		  "decltype((c)) is not an lvalue ref");
+    static_assert(is_const_v<__remove_reference (decltype((c)))>,
+		  "qualification of decltype((c)) is not const");
+    static_assert(__is_same (__remove_cvref (decltype((c))), int),
+		  "decltype((c)) is not an int");
+    static_assert(__is_same (decltype(c), int const));
+  };
+  f2_const_value();
+
+  auto f3_const_value = [&n, &c]<typename Self>(this Self const){
+    static_assert(__is_same (decltype((n)), int&));
+    static_assert(__is_same (decltype(n), int));
+
+    static_assert(__is_same (decltype((c)), int const&));
+    static_assert(__is_same (decltype(c), int const));
+  };
+  f3_const_value();
+
+  auto f4_const_value = []<typename Self>(this Self const){
+    static_assert(__is_same (decltype(n), int));
+    static_assert(__is_same (decltype((n)), int&));
+
+    static_assert(__is_same (decltype(c), int const));
+    static_assert(__is_same (decltype((c)), int const&));
+  };
+  f4_const_value();
+
+  // const ref
+  auto f0_const_ref = [=]<typename Self>(this Self const&){
+    static_assert(is_lvalue_ref<decltype((n))>,
+		  "decltype((n)) is not an lvalue ref");
+    static_assert(is_const_v<__remove_reference (decltype((n)))>, // { dg-bogus {static assertion failed: qualification of decltype\(\(n\)\) does not match qualification of Self} }
+		  "qualification of decltype((n)) does not match qualification of Self");
+    static_assert(__is_same (__remove_cvref (decltype((n))), int),
+		  "decltype((n)) is not an int");
+    static_assert(__is_same (decltype(n), int));
+
+    static_assert(is_lvalue_ref<decltype((c))>,
+		  "decltype((c)) is not an lvalue ref");
+    static_assert(is_const_v<__remove_reference (decltype((c)))>,
+		  "qualification of decltype((c)) is not const");
+    static_assert(__is_same (__remove_cvref (decltype((c))), int),
+		  "decltype((c)) is not an int");
+    static_assert(__is_same (decltype(c), int const));
+  };
+  f0_const_ref();
+
+  auto f1_const_ref = [&]<typename Self>(this Self const&){
+    static_assert(__is_same (decltype((n)), int&));
+    static_assert(__is_same (decltype(n), int));
+
+    static_assert(__is_same (decltype((c)), int const&));
+    static_assert(__is_same (decltype(c), int const));
+  };
+  f1_const_ref();
+
+  auto f2_const_ref = [n, c]<typename Self>(this Self const&){
+    static_assert(is_lvalue_ref<decltype((n))>,
+		  "decltype((n)) is not an lvalue ref");
+    static_assert(is_const_v<__remove_reference (decltype((n)))>, // { dg-bogus {static assertion failed: qualification of decltype\(\(n\)\) does not match qualification of Self} }
+		  "qualification of decltype((n)) does not match qualification of Self");
+    static_assert(__is_same (__remove_cvref (decltype((n))), int),
+		  "decltype((n)) is not an int");
+    static_assert(__is_same (decltype(n), int));
+
+    static_assert(is_lvalue_ref<decltype((c))>,
+		  "decltype((c)) is not an lvalue ref");
+    static_assert(is_const_v<__remove_reference (decltype((c)))>,
+		  "qualification of decltype((c)) is not const");
+    static_assert(__is_same (__remove_cvref (decltype((c))), int),
+		  "decltype((c)) is not an int");
+    static_assert(__is_same (decltype(c), int const));
+  };
+  f2_const_ref();
+
+  auto f3_const_ref = [&n, &c]<typename Self>(this Self const&){
+    static_assert(__is_same (decltype((n)), int&));
+    static_assert(__is_same (decltype(n), int));
+
+    static_assert(__is_same (decltype((c)), int const&));
+    static_assert(__is_same (decltype(c), int const));
+  };
+  f3_const_ref();
+
+  auto f4_const_ref = []<typename Self>(this Self const&){
+    static_assert(__is_same (decltype(n), int));
+    static_assert(__is_same (decltype((n)), int&));
+
+    static_assert(__is_same (decltype(c), int const));
+    static_assert(__is_same (decltype((c)), int const&));
+  };
+  f4_const_ref();
+}
+
+template<typename = void>
+void dep0()
+{
+  int n = 0;
+  int const c = 0;
+  // value
+  auto f0_value = [=]<typename Self>(this Self){
+    static_assert(is_lvalue_ref<decltype((n))>,
+		  "decltype((n)) is not an lvalue ref");
+    static_assert(is_const_v<Self> == is_const_v<__remove_reference (decltype((n)))>, // { dg-bogus {static assertion failed: qualification of decltype\(\(n\)\) does not match qualification of Self} }
+		  "qualification of decltype((n)) does not match qualification of Self");
+    static_assert(__is_same (__remove_cvref (decltype((n))), int),
+		  "decltype((n)) is not an int");
+    static_assert(__is_same (decltype(n), int));
+
+    static_assert(is_lvalue_ref<decltype((c))>,
+		  "decltype((c)) is not an lvalue ref");
+    static_assert(is_const_v<__remove_reference (decltype((c)))>,
+		  "qualification of decltype((c)) is not const");
+    static_assert(__is_same (__remove_cvref (decltype((c))), int),
+		  "decltype((c)) is not an int");
+    static_assert(__is_same (decltype(c), int const));
+  };
+  f0_value();
+  f0_value.template operator()<decltype(f0_value) const>();
+
+  auto f1_value = [&]<typename Self>(this Self){
+    static_assert(__is_same (decltype((n)), int&));
+    static_assert(__is_same (decltype(n), int));
+
+    static_assert(__is_same (decltype((c)), int const&));
+    static_assert(__is_same (decltype(c), int const));
+  };
+  f1_value();
+  f1_value.template operator()<decltype(f1_value) const>();
+
+  auto f2_value = [n, c]<typename Self>(this Self){
+    static_assert(is_lvalue_ref<decltype((n))>,
+		  "decltype((n)) is not an lvalue ref");
+    static_assert(is_const_v<Self> == is_const_v<__remove_reference (decltype((n)))>, // { dg-bogus {static assertion failed: qualification of decltype\(\(n\)\) does not match qualification of Self} }
+		  "qualification of decltype((n)) does not match qualification of Self");
+    static_assert(__is_same (__remove_cvref (decltype((n))), int),
+		  "decltype((n)) is not an int");
+    static_assert(__is_same (decltype(n), int));
+
+    static_assert(is_lvalue_ref<decltype((c))>,
+		  "decltype((c)) is not an lvalue ref");
+    static_assert(is_const_v<__remove_reference (decltype((c)))>,
+		  "qualification of decltype((c)) is not const");
+    static_assert(__is_same (__remove_cvref (decltype((c))), int),
+		  "decltype((c)) is not an int");
+    static_assert(__is_same (decltype(c), int const));
+  };
+  f2_value();
+  f2_value.template operator()<decltype(f2_value) const>();
+
+  auto f3_value = [&n, &c]<typename Self>(this Self){
+    static_assert(__is_same (decltype((n)), int&));
+    static_assert(__is_same (decltype(n), int));
+
+    static_assert(__is_same (decltype((c)), int const&));
+    static_assert(__is_same (decltype(c), int const));
+  };
+  f3_value();
+  f3_value.template operator()<decltype(f3_value) const>();
+
+  auto f4_value = []<typename Self>(this Self){
+    static_assert(__is_same (decltype(n), int));
+    static_assert(__is_same (decltype((n)), int&));
+
+    static_assert(__is_same (decltype(c), int const));
+    static_assert(__is_same (decltype((c)), int const&));
+  };
+  f4_value();
+  f4_value.template operator()<decltype(f4_value) const>();
+
+  // ref
+  auto f0_ref = [=]<typename Self>(this Self&){
+    static_assert(is_lvalue_ref<decltype((n))>,
+		  "decltype((n)) is not an lvalue ref");
+    static_assert(is_const_v<Self> == is_const_v<__remove_reference (decltype((n)))>, // { dg-bogus {static assertion failed: qualification of decltype\(\(n\)\) does not match qualification of Self} }
+		  "qualification of decltype((n)) does not match qualification of Self");
+    static_assert(__is_same (__remove_cvref (decltype((n))), int),
+		  "decltype((n)) is not an int");
+    static_assert(__is_same (decltype(n), int));
+
+    static_assert(is_lvalue_ref<decltype((c))>,
+		  "decltype((c)) is not an lvalue ref");
+    static_assert(is_const_v<__remove_reference (decltype((c)))>,
+		  "qualification of decltype((c)) is not const");
+    static_assert(__is_same (__remove_cvref (decltype((c))), int),
+		  "decltype((c)) is not an int");
+    static_assert(__is_same (decltype(c), int const));
+  };
+  f0_ref();
+  f0_ref.template operator()<decltype(f0_ref) const>();
+  static_cast<decltype(f0_ref) const&>(f0_ref)();
+
+  auto f1_ref = [&]<typename Self>(this Self&){
+    static_assert(__is_same (decltype((n)), int&));
+    static_assert(__is_same (decltype(n), int));
+
+    static_assert(__is_same (decltype((c)), int const&));
+    static_assert(__is_same (decltype(c), int const));
+  };
+  f1_ref();
+  f1_ref.template operator()<decltype(f1_ref) const>();
+  static_cast<decltype(f1_ref) const&>(f1_ref)();
+
+  auto f2_ref = [n, c]<typename Self>(this Self&){
+    static_assert(is_lvalue_ref<decltype((n))>,
+		  "decltype((n)) is not an lvalue ref");
+    static_assert(is_const_v<Self> == is_const_v<__remove_reference (decltype((n)))>, // { dg-bogus {static assertion failed: qualification of decltype\(\(n\)\) does not match qualification of Self} }
+		  "qualification of decltype((n)) does not match qualification of Self");
+    static_assert(__is_same (__remove_cvref (decltype((n))), int),
+		  "decltype((n)) is not an int");
+    static_assert(__is_same (decltype(n), int));
+
+    static_assert(is_lvalue_ref<decltype((c))>,
+		  "decltype((c)) is not an lvalue ref");
+    static_assert(is_const_v<__remove_reference (decltype((c)))>,
+		  "qualification of decltype((c)) is not const");
+    static_assert(__is_same (__remove_cvref (decltype((c))), int),
+		  "decltype((c)) is not an int");
+    static_assert(__is_same (decltype(c), int const));
+  };
+  f2_ref();
+  f2_ref.template operator()<decltype(f2_ref) const>();
+  static_cast<decltype(f2_ref) const&>(f2_ref)();
+
+  auto f3_ref = [&n, &c]<typename Self>(this Self&){
+    static_assert(__is_same(decltype((n)), int&));
+    static_assert(__is_same (decltype(n), int));
+
+    static_assert(__is_same (decltype((c)), int const&));
+    static_assert(__is_same (decltype(c), int const));
+  };
+  f3_ref();
+  f3_ref.template operator()<decltype(f3_ref) const>();
+  static_cast<decltype(f3_ref) const&>(f3_ref)();
+
+  auto f4_ref = []<typename Self>(this Self&){
+    static_assert(__is_same (decltype(n), int));
+    static_assert(__is_same (decltype((n)), int&));
+
+    static_assert(__is_same (decltype(c), int const));
+    static_assert(__is_same (decltype((c)), int const&));
+  };
+  f4_ref();
+  f4_ref.template operator()<decltype(f4_ref) const>();
+  static_cast<decltype(f4_ref) const&>(f4_ref)();
+
+  // const value
+  auto f0_const_value = [=]<typename Self>(this Self const){
+    static_assert(is_lvalue_ref<decltype((n))>,
+		  "decltype((n)) is not an lvalue ref");
+    static_assert(is_const_v<__remove_reference (decltype((n)))>, // { dg-bogus {static assertion failed: qualification of decltype\(\(n\)\) does not match qualification of Self} }
+		  "qualification of decltype((n)) does not match qualification of Self");
+    static_assert(__is_same (__remove_cvref (decltype((n))), int),
+		  "decltype((n)) is not an int");
+    static_assert(__is_same (decltype(n), int));
+
+    static_assert(is_lvalue_ref<decltype((c))>,
+		  "decltype((c)) is not an lvalue ref");
+    static_assert(is_const_v<__remove_reference (decltype((c)))>,
+		  "qualification of decltype((c)) is not const");
+    static_assert(__is_same (__remove_cvref (decltype((c))), int),
+		  "decltype((c)) is not an int");
+    static_assert(__is_same (decltype(c), int const));
+  };
+  f0_const_value();
+
+  auto f1_const_value = [&]<typename Self>(this Self const){
+    static_assert(__is_same (decltype((n)), int&));
+    static_assert(__is_same (decltype(n), int));
+
+    static_assert(__is_same (decltype((c)), int const&));
+    static_assert(__is_same (decltype(c), int const));
+  };
+  f1_const_value();
+
+  auto f2_const_value = [n, c]<typename Self>(this Self const){
+    static_assert(is_lvalue_ref<decltype((n))>,
+		  "decltype((n)) is not an lvalue ref");
+    static_assert(is_const_v<__remove_reference (decltype((n)))>, // { dg-bogus {static assertion failed: qualification of decltype\(\(n\)\) does not match qualification of Self} }
+		  "qualification of decltype((n)) does not match qualification of Self");
+    static_assert(__is_same (__remove_cvref (decltype((n))), int),
+		  "decltype((n)) is not an int");
+    static_assert(__is_same (decltype(n), int));
+
+    static_assert(is_lvalue_ref<decltype((c))>,
+		  "decltype((c)) is not an lvalue ref");
+    static_assert(is_const_v<__remove_reference (decltype((c)))>,
+		  "qualification of decltype((c)) is not const");
+    static_assert(__is_same (__remove_cvref (decltype((c))), int),
+		  "decltype((c)) is not an int");
+    static_assert(__is_same (decltype(c), int const));
+  };
+  f2_const_value();
+
+  auto f3_const_value = [&n, &c]<typename Self>(this Self const){
+    static_assert(__is_same (decltype((n)), int&));
+    static_assert(__is_same (decltype(n), int));
+
+    static_assert(__is_same (decltype((c)), int const&));
+    static_assert(__is_same (decltype(c), int const));
+  };
+  f3_const_value();
+
+  auto f4_const_value = []<typename Self>(this Self const){
+    static_assert(__is_same (decltype(n), int));
+    static_assert(__is_same (decltype((n)), int&));
+
+    static_assert(__is_same (decltype(c), int const));
+    static_assert(__is_same (decltype((c)), int const&));
+  };
+  f4_const_value();
+
+  // const ref
+  auto f0_const_ref = [=]<typename Self>(this Self const&){
+    static_assert(is_lvalue_ref<decltype((n))>,
+		  "decltype((n)) is not an lvalue ref");
+    static_assert(is_const_v<__remove_reference (decltype((n)))>, // { dg-bogus {static assertion failed: qualification of decltype\(\(n\)\) does not match qualification of Self} }
+		  "qualification of decltype((n)) does not match qualification of Self");
+    static_assert(__is_same (__remove_cvref (decltype((n))), int),
+		  "decltype((n)) is not an int");
+    static_assert(__is_same (decltype(n), int));
+
+    static_assert(is_lvalue_ref<decltype((c))>,
+		  "decltype((c)) is not an lvalue ref");
+    static_assert(is_const_v<__remove_reference (decltype((c)))>,
+		  "qualification of decltype((c)) is not const");
+    static_assert(__is_same (__remove_cvref (decltype((c))), int),
+		  "decltype((c)) is not an int");
+    static_assert(__is_same (decltype(c), int const));
+  };
+  f0_const_ref();
+
+  auto f1_const_ref = [&]<typename Self>(this Self const&){
+    static_assert(__is_same (decltype((n)), int&));
+    static_assert(__is_same (decltype(n), int));
+
+    static_assert(__is_same (decltype((c)), int const&));
+    static_assert(__is_same (decltype(c), int const));
+  };
+  f1_const_ref();
+
+  auto f2_const_ref = [n, c]<typename Self>(this Self const&){
+    static_assert(is_lvalue_ref<decltype((n))>,
+		  "decltype((n)) is not an lvalue ref");
+    static_assert(is_const_v<__remove_reference (decltype((n)))>, // { dg-bogus {static assertion failed: qualification of decltype\(\(n\)\) does not match qualification of Self} }
+		  "qualification of decltype((n)) does not match qualification of Self");
+    static_assert(__is_same (__remove_cvref (decltype((n))), int),
+		  "decltype((n)) is not an int");
+    static_assert(__is_same (decltype(n), int));
+
+    static_assert(is_lvalue_ref<decltype((c))>,
+		  "decltype((c)) is not an lvalue ref");
+    static_assert(is_const_v<__remove_reference (decltype((c)))>,
+		  "qualification of decltype((c)) is not const");
+    static_assert(__is_same (__remove_cvref (decltype((c))), int),
+		  "decltype((c)) is not an int");
+    static_assert(__is_same (decltype(c), int const));
+  };
+  f2_const_ref();
+
+  auto f3_const_ref = [&n, &c]<typename Self>(this Self const&){
+    static_assert(__is_same (decltype((n)), int&));
+    static_assert(__is_same (decltype(n), int));
+
+    static_assert(__is_same (decltype((c)), int const&));
+    static_assert(__is_same (decltype(c), int const));
+  };
+  f3_const_ref();
+
+  auto f4_const_ref = []<typename Self>(this Self const&){
+    static_assert(__is_same (decltype(n), int));
+    static_assert(__is_same (decltype((n)), int&));
+
+    static_assert(__is_same (decltype(c), int const));
+    static_assert(__is_same (decltype((c)), int const&));
+  };
+  f4_const_ref();
+}
+
+// dep1 uses the template parameter
+
+template<typename T = int>
+void dep1()
+{
+  T n = 0;
+  T const c = 0;
+  // value
+  auto f0_value = [=]<typename Self>(this Self){
+    static_assert(is_lvalue_ref<decltype((n))>,
+		  "decltype((n)) is not an lvalue ref");
+    static_assert(is_const_v<Self> == is_const_v<__remove_reference (decltype((n)))>, // { dg-bogus {static assertion failed: qualification of decltype\(\(n\)\) does not match qualification of Self} }
+		  "qualification of decltype((n)) does not match qualification of Self");
+    static_assert(__is_same (__remove_cvref (decltype((n))), int),
+		  "decltype((n)) is not an int");
+    static_assert(__is_same (decltype(n), int));
+
+    static_assert(is_lvalue_ref<decltype((c))>,
+		  "decltype((c)) is not an lvalue ref");
+    static_assert(is_const_v<__remove_reference (decltype((c)))>,
+		  "qualification of decltype((c)) is not const");
+    static_assert(__is_same (__remove_cvref (decltype((c))), int),
+		  "decltype((c)) is not an int");
+    static_assert(__is_same (decltype(c), int const));
+  };
+  f0_value();
+  f0_value.template operator()<decltype(f0_value) const>();
+
+  auto f1_value = [&]<typename Self>(this Self){
+    static_assert(__is_same (decltype((n)), int&));
+    static_assert(__is_same (decltype(n), int));
+
+    static_assert(__is_same (decltype((c)), int const&));
+    static_assert(__is_same (decltype(c), int const));
+  };
+  f1_value();
+  f1_value.template operator()<decltype(f1_value) const>();
+
+  auto f2_value = [n, c]<typename Self>(this Self){
+    static_assert(is_lvalue_ref<decltype((n))>,
+		  "decltype((n)) is not an lvalue ref");
+    static_assert(is_const_v<Self> == is_const_v<__remove_reference (decltype((n)))>, // { dg-bogus {static assertion failed: qualification of decltype\(\(n\)\) does not match qualification of Self} }
+		  "qualification of decltype((n)) does not match qualification of Self");
+    static_assert(__is_same (__remove_cvref (decltype((n))), int),
+		  "decltype((n)) is not an int");
+    static_assert(__is_same (decltype(n), int));
+
+    static_assert(is_lvalue_ref<decltype((c))>,
+		  "decltype((c)) is not an lvalue ref");
+    static_assert(is_const_v<__remove_reference (decltype((c)))>,
+		  "qualification of decltype((c)) is not const");
+    static_assert(__is_same (__remove_cvref (decltype((c))), int),
+		  "decltype((c)) is not an int");
+    static_assert(__is_same (decltype(c), int const));
+  };
+  f2_value();
+  f2_value.template operator()<decltype(f2_value) const>();
+
+  auto f3_value = [&n, &c]<typename Self>(this Self){
+    static_assert(__is_same (decltype((n)), int&));
+    static_assert(__is_same (decltype(n), int));
+
+    static_assert(__is_same (decltype((c)), int const&));
+    static_assert(__is_same (decltype(c), int const));
+  };
+  f3_value();
+  f3_value.template operator()<decltype(f3_value) const>();
+
+  auto f4_value = []<typename Self>(this Self){
+    static_assert(__is_same (decltype(n), int));
+    static_assert(__is_same (decltype((n)), int&));
+
+    static_assert(__is_same (decltype(c), int const));
+    static_assert(__is_same (decltype((c)), int const&));
+  };
+  f4_value();
+  f4_value.template operator()<decltype(f4_value) const>();
+
+  // ref
+  auto f0_ref = [=]<typename Self>(this Self&){
+    static_assert(is_lvalue_ref<decltype((n))>,
+		  "decltype((n)) is not an lvalue ref");
+    static_assert(is_const_v<Self> == is_const_v<__remove_reference (decltype((n)))>, // { dg-bogus {static assertion failed: qualification of decltype\(\(n\)\) does not match qualification of Self} }
+		  "qualification of decltype((n)) does not match qualification of Self");
+    static_assert(__is_same (__remove_cvref (decltype((n))), int),
+		  "decltype((n)) is not an int");
+    static_assert(__is_same (decltype(n), int));
+
+    static_assert(is_lvalue_ref<decltype((c))>,
+		  "decltype((c)) is not an lvalue ref");
+    static_assert(is_const_v<__remove_reference (decltype((c)))>,
+		  "qualification of decltype((c)) is not const");
+    static_assert(__is_same (__remove_cvref (decltype((c))), int),
+		  "decltype((c)) is not an int");
+    static_assert(__is_same (decltype(c), int const));
+  };
+  f0_ref();
+  f0_ref.template operator()<decltype(f0_ref) const>();
+  static_cast<decltype(f0_ref) const&>(f0_ref)();
+
+  auto f1_ref = [&]<typename Self>(this Self&){
+    static_assert(__is_same (decltype((n)), int&));
+    static_assert(__is_same (decltype(n), int));
+
+    static_assert(__is_same (decltype((c)), int const&));
+    static_assert(__is_same (decltype(c), int const));
+  };
+  f1_ref();
+  f1_ref.template operator()<decltype(f1_ref) const>();
+  static_cast<decltype(f1_ref) const&>(f1_ref)();
+
+  auto f2_ref = [n, c]<typename Self>(this Self&){
+    static_assert(is_lvalue_ref<decltype((n))>,
+		  "decltype((n)) is not an lvalue ref");
+    static_assert(is_const_v<Self> == is_const_v<__remove_reference (decltype((n)))>, // { dg-bogus {static assertion failed: qualification of decltype\(\(n\)\) does not match qualification of Self} }
+		  "qualification of decltype((n)) does not match qualification of Self");
+    static_assert(__is_same (__remove_cvref (decltype((n))), int),
+		  "decltype((n)) is not an int");
+    static_assert(__is_same (decltype(n), int));
+
+    static_assert(is_lvalue_ref<decltype((c))>,
+		  "decltype((c)) is not an lvalue ref");
+    static_assert(is_const_v<__remove_reference (decltype((c)))>,
+		  "qualification of decltype((c)) is not const");
+    static_assert(__is_same (__remove_cvref (decltype((c))), int),
+		  "decltype((c)) is not an int");
+    static_assert(__is_same (decltype(c), int const));
+  };
+  f2_ref();
+  f2_ref.template operator()<decltype(f2_ref) const>();
+  static_cast<decltype(f2_ref) const&>(f2_ref)();
+
+  auto f3_ref = [&n, &c]<typename Self>(this Self&){
+    static_assert(__is_same(decltype((n)), int&));
+    static_assert(__is_same (decltype(n), int));
+
+    static_assert(__is_same (decltype((c)), int const&));
+    static_assert(__is_same (decltype(c), int const));
+  };
+  f3_ref();
+  f3_ref.template operator()<decltype(f3_ref) const>();
+  static_cast<decltype(f3_ref) const&>(f3_ref)();
+
+  auto f4_ref = []<typename Self>(this Self&){
+    static_assert(__is_same (decltype(n), int));
+    static_assert(__is_same (decltype((n)), int&));
+
+    static_assert(__is_same (decltype(c), int const));
+    static_assert(__is_same (decltype((c)), int const&));
+  };
+  f4_ref();
+  f4_ref.template operator()<decltype(f4_ref) const>();
+  static_cast<decltype(f4_ref) const&>(f4_ref)();
+
+  // const value
+  auto f0_const_value = [=]<typename Self>(this Self const){
+    static_assert(is_lvalue_ref<decltype((n))>,
+		  "decltype((n)) is not an lvalue ref");
+    static_assert(is_const_v<__remove_reference (decltype((n)))>, // { dg-bogus {static assertion failed: qualification of decltype\(\(n\)\) does not match qualification of Self} }
+		  "qualification of decltype((n)) does not match qualification of Self");
+    static_assert(__is_same (__remove_cvref (decltype((n))), int),
+		  "decltype((n)) is not an int");
+    static_assert(__is_same (decltype(n), int));
+
+    static_assert(is_lvalue_ref<decltype((c))>,
+		  "decltype((c)) is not an lvalue ref");
+    static_assert(is_const_v<__remove_reference (decltype((c)))>,
+		  "qualification of decltype((c)) is not const");
+    static_assert(__is_same (__remove_cvref (decltype((c))), int),
+		  "decltype((c)) is not an int");
+    static_assert(__is_same (decltype(c), int const));
+  };
+  f0_const_value();
+
+  auto f1_const_value = [&]<typename Self>(this Self const){
+    static_assert(__is_same (decltype((n)), int&));
+    static_assert(__is_same (decltype(n), int));
+
+    static_assert(__is_same (decltype((c)), int const&));
+    static_assert(__is_same (decltype(c), int const));
+  };
+  f1_const_value();
+
+  auto f2_const_value = [n, c]<typename Self>(this Self const){
+    static_assert(is_lvalue_ref<decltype((n))>,
+		  "decltype((n)) is not an lvalue ref");
+    static_assert(is_const_v<__remove_reference (decltype((n)))>, // { dg-bogus {static assertion failed: qualification of decltype\(\(n\)\) does not match qualification of Self} }
+		  "qualification of decltype((n)) does not match qualification of Self");
+    static_assert(__is_same (__remove_cvref (decltype((n))), int),
+		  "decltype((n)) is not an int");
+    static_assert(__is_same (decltype(n), int));
+
+    static_assert(is_lvalue_ref<decltype((c))>,
+		  "decltype((c)) is not an lvalue ref");
+    static_assert(is_const_v<__remove_reference (decltype((c)))>,
+		  "qualification of decltype((c)) is not const");
+    static_assert(__is_same (__remove_cvref (decltype((c))), int),
+		  "decltype((c)) is not an int");
+    static_assert(__is_same (decltype(c), int const));
+  };
+  f2_const_value();
+
+  auto f3_const_value = [&n, &c]<typename Self>(this Self const){
+    static_assert(__is_same (decltype((n)), int&));
+    static_assert(__is_same (decltype(n), int));
+
+    static_assert(__is_same (decltype((c)), int const&));
+    static_assert(__is_same (decltype(c), int const));
+  };
+  f3_const_value();
+
+  auto f4_const_value = []<typename Self>(this Self const){
+    static_assert(__is_same (decltype(n), int));
+    static_assert(__is_same (decltype((n)), int&));
+
+    static_assert(__is_same (decltype(c), int const));
+    static_assert(__is_same (decltype((c)), int const&));
+  };
+  f4_const_value();
+
+  // const ref
+  auto f0_const_ref = [=]<typename Self>(this Self const&){
+    static_assert(is_lvalue_ref<decltype((n))>,
+		  "decltype((n)) is not an lvalue ref");
+    static_assert(is_const_v<__remove_reference (decltype((n)))>, // { dg-bogus {static assertion failed: qualification of decltype\(\(n\)\) does not match qualification of Self} }
+		  "qualification of decltype((n)) does not match qualification of Self");
+    static_assert(__is_same (__remove_cvref (decltype((n))), int),
+		  "decltype((n)) is not an int");
+    static_assert(__is_same (decltype(n), int));
+
+    static_assert(is_lvalue_ref<decltype((c))>,
+		  "decltype((c)) is not an lvalue ref");
+    static_assert(is_const_v<__remove_reference (decltype((c)))>,
+		  "qualification of decltype((c)) is not const");
+    static_assert(__is_same (__remove_cvref (decltype((c))), int),
+		  "decltype((c)) is not an int");
+    static_assert(__is_same (decltype(c), int const));
+  };
+  f0_const_ref();
+
+  auto f1_const_ref = [&]<typename Self>(this Self const&){
+    static_assert(__is_same (decltype((n)), int&));
+    static_assert(__is_same (decltype(n), int));
+
+    static_assert(__is_same (decltype((c)), int const&));
+    static_assert(__is_same (decltype(c), int const));
+  };
+  f1_const_ref();
+
+  auto f2_const_ref = [n, c]<typename Self>(this Self const&){
+    static_assert(is_lvalue_ref<decltype((n))>,
+		  "decltype((n)) is not an lvalue ref");
+    static_assert(is_const_v<__remove_reference (decltype((n)))>, // { dg-bogus {static assertion failed: qualification of decltype\(\(n\)\) does not match qualification of Self} }
+		  "qualification of decltype((n)) does not match qualification of Self");
+    static_assert(__is_same (__remove_cvref (decltype((n))), int),
+		  "decltype((n)) is not an int");
+    static_assert(__is_same (decltype(n), int));
+
+    static_assert(is_lvalue_ref<decltype((c))>,
+		  "decltype((c)) is not an lvalue ref");
+    static_assert(is_const_v<__remove_reference (decltype((c)))>,
+		  "qualification of decltype((c)) is not const");
+    static_assert(__is_same (__remove_cvref (decltype((c))), int),
+		  "decltype((c)) is not an int");
+    static_assert(__is_same (decltype(c), int const));
+  };
+  f2_const_ref();
+
+  auto f3_const_ref = [&n, &c]<typename Self>(this Self const&){
+    static_assert(__is_same (decltype((n)), int&));
+    static_assert(__is_same (decltype(n), int));
+
+    static_assert(__is_same (decltype((c)), int const&));
+    static_assert(__is_same (decltype(c), int const));
+  };
+  f3_const_ref();
+
+  auto f4_const_ref = []<typename Self>(this Self const&){
+    static_assert(__is_same (decltype(n), int));
+    static_assert(__is_same (decltype((n)), int&));
+
+    static_assert(__is_same (decltype(c), int const));
+    static_assert(__is_same (decltype((c)), int const&));
+  };
+  f4_const_ref();
+}
+
+void instantiate_dep()
+{
+  dep0();
+  dep1();
+}
+
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda7.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda7.C
new file mode 100644
index 00000000000..1e10fb26675
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda7.C
@@ -0,0 +1,20 @@
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+// diagnose mutation of lambda capture when called with a deduced as const explicit object parameter
+
+void test()
+{
+  auto f0 = [n = 5](this auto){ n = 10; }; // { dg-bogus {assignment of read-only variable} }
+  auto f1 = [n = 5](this auto const){ n = 10; }; // { dg-error {assignment of read-only variable} }
+  auto f2 = [n = 5](this auto&){ n = 10; };  // { dg-error {assignment of read-only variable} }
+  auto f3 = [n = 5](this auto const&){ n = 10; }; // { dg-error {assignment of read-only variable} }
+  auto f4 = [n = 5](this auto&&){ n = 10; };  // { dg-error {assignment of read-only variable} }
+
+  static_cast<decltype(f0) const&>(f0)();
+  f1();
+  static_cast<decltype(f2) const&>(f2)();
+  f3();
+  static_cast<decltype(f4) const&>(f4)();
+}
+
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda8.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda8.C
new file mode 100644
index 00000000000..a068941413a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda8.C
@@ -0,0 +1,87 @@
+// P0847R7
+// { dg-do run { target c++23 } }
+
+// lambda capture mutability with explicit object parameter
+
+void capture_by_value()
+{
+  static constexpr int magic = 42;
+  auto f0 = [n = 0](this auto self){
+    n += magic;
+    return n;
+  };
+  auto f1 = [n = 0](this auto& self){
+    n += magic;
+    return n;
+  };
+  auto f2 = [n = 0](this auto&& self){
+    n += magic;
+    return n;
+  };
+
+  // passed by value, should still return a value equal to magic regardless
+  // of how many times it is called
+  if (f0 () != magic)
+    __builtin_abort ();
+  if (f0 () != magic)
+    __builtin_abort ();
+  // passed by reference, the returned value should increase by magic
+  // each time it is called
+  if (f1 () != magic)
+    __builtin_abort ();
+  if (f1 () != magic + magic)
+    __builtin_abort ();
+  if (f2 () != magic)
+    __builtin_abort ();
+  if (f2 () != magic + magic)
+    __builtin_abort ();
+}
+
+void capture_by_ref()
+{
+  static constexpr int magic = 42;
+  int n0 = 0;
+  auto f0 = [&n0](this auto self){
+    n0 += magic;
+  };
+  int n1 = 0;
+  auto f1 = [&n1](this auto& self){
+    n1 += magic;
+  };
+  int n2 = 0;
+  auto f2 = [&n2](this auto&& self){
+    n2 += magic;
+  };
+  int n3 = 0;
+  auto f3 = [&n3](this auto const& self){
+    n3 += magic;
+  };
+
+  // all calls should mutate their capture, the capture is by reference
+  if (f0 (); n0 != magic)
+    __builtin_abort ();
+  if (f0 (); n0 != magic + magic)
+    __builtin_abort ();
+
+  if (f1 (); n1 != magic)
+    __builtin_abort ();
+  if (f1 (); n1 != magic + magic)
+    __builtin_abort ();
+
+  if (f2 (); n2 != magic)
+    __builtin_abort ();
+  if (f2 (); n2 != magic + magic)
+    __builtin_abort ();
+
+  if (f3 (); n3 != magic)
+    __builtin_abort ();
+  if (f3 (); n3 != magic + magic)
+    __builtin_abort ();
+}
+
+int main()
+{
+  capture_by_value ();
+  capture_by_ref ();
+}
+
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda9.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda9.C
new file mode 100644
index 00000000000..a808019e28e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda9.C
@@ -0,0 +1,46 @@
+// P0847R7
+// { dg-do run { target c++23 } }
+
+// calling captureless lambda call operator with unrelated explicit object parameter
+// through function pointer
+
+int main()
+{
+  auto f0 = [](this auto self) { return self; };
+  auto fp0_value     = static_cast<int(*)(int)        >(&decltype(f0)::operator());
+  auto fp0_lref      = static_cast<int(*)(int&)       >(&decltype(f0)::operator());
+  auto fp0_rref      = static_cast<int(*)(int&&)      >(&decltype(f0)::operator());
+  auto fp0_constlref = static_cast<int(*)(int const&) >(&decltype(f0)::operator());
+  auto fp0_constrref = static_cast<int(*)(int const&&)>(&decltype(f0)::operator());
+
+  auto f1 = [](this auto&& self) { return self; };
+  auto fp1_lref      = static_cast<int(*)(int&)       >(&decltype(f1)::operator());
+  auto fp1_rref      = static_cast<int(*)(int&&)      >(&decltype(f1)::operator());
+  auto fp1_constlref = static_cast<int(*)(int const&) >(&decltype(f1)::operator());
+  auto fp1_constrref = static_cast<int(*)(int const&&)>(&decltype(f1)::operator());
+
+  // both are needed for lvalue/rvalue overloads
+  #define MAGIC 42
+  int magic = MAGIC;
+
+  if (fp0_value (magic) != magic)
+    __builtin_abort ();
+  if (fp0_lref (magic) != magic)
+    __builtin_abort ();
+  if (fp0_rref (MAGIC) != magic)
+    __builtin_abort ();
+  if (fp0_constlref (magic) != magic)
+    __builtin_abort ();
+  if (fp0_constrref (MAGIC) != magic)
+    __builtin_abort ();
+
+  if (fp1_lref (magic) != magic)
+    __builtin_abort ();
+  if (fp1_rref (MAGIC) != magic)
+    __builtin_abort ();
+  if (fp1_constlref (magic) != magic)
+    __builtin_abort ();
+  if (fp1_constrref (MAGIC) != magic)
+    __builtin_abort ();
+}
+
-- 
2.43.0

Reply via email to