Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?

-- >8 --
This patch is an attempt to implement P2036R3 along with P2579R0, fixing
build breakages caused by P2036R3.

The simplest example is:

  auto counter1 = [j=0]() mutable -> decltype(j) {
      return j++;
  };

which currently doesn't compile because the 'j' in the capture isn't
visible in the trailing return type.  With these proposals, the 'j'
will be in a lambda scope which spans the trailing return type, so
this test will compile.

This oughtn't be difficult but decltype and other issues made this patch
much more challenging.

We have to push the explicit captures before going into _lambda_declarator_opt
because that is what parses the trailing return type.  Yet we can't build
any captures until after _lambda_body -> start_lambda_function which
creates the lambda's operator(), without which we can't build a proxy,
but _lambda_body happens only after parsing the declarator.  This patch
works around it by creating a fake operator() in make_dummy_lambda_op.

Another thing is that in "-> decltype(j)" we don't have the right
current_function_decl yet, so I've added the in_lambda_declarator_p flag
to be used in finish_decltype_type so that we know this decltype appertains
to a lambda -- then current_lambda_expr should give us the right lambda,
which has another new flag tracking whether mutable was seen.

I don't think this patch changes behavior for the tests in
"capture-default with [=]" as the paper promises; clang++ behaves the
same as gcc with this patch.

        PR c++/102610

gcc/cp/ChangeLog:

        * coroutines.cc (cp_coroutine_transform::build_ramp_function): Pass
        false to finish_decltype_type.
        * cp-tree.def: Document DECLTYPE_IN_LAMBDA_DECLARATOR_P.
        * cp-tree.h (LAMBDA_EXPR_MUTABLE_SEEN_P): Define.
        (DECLTYPE_IN_LAMBDA_DECLARATOR_P): Define.
        (make_dummy_lambda_op): Declare.
        (finish_decltype_type): Adjust.
        (push_capture_proxies): Declare.
        * lambda.cc (build_capture_proxy): No longer static.  If we do not have
        the operator() yet, build up a fake one.
        (maybe_add_lambda_conv_op): Pass false to finish_decltype_type.
        (push_capture_proxies): New.
        (start_lambda_function): Use it.
        * name-lookup.cc (check_local_shadow): Adjust for P2036.
        (cp_binding_level_descriptor): Add lambda-scope.
        (begin_scope) <case sk_lambda>: New case.
        * name-lookup.h (enum scope_kind): Add sk_lambda.
        (struct cp_binding_level): Widen kind.
        * parser.cc (cp_parser_lambda_expression): Create a new (lambda) scope
        after the lambda-introducer.  Push the capture proxies.
        (cp_parser_lambda_declarator_opt): Override
        parser->in_lambda_declarator_p.  Set LAMBDA_EXPR_MUTABLE_SEEN_P.
        (make_dummy_lambda_op): New.
        (cp_parser_decltype): Pass parser->in_lambda_declarator_p to
        finish_decltype_type.
        * parser.h (struct cp_parser): Add in_lambda_declarator_p.
        * pt.cc (tsubst): Adjust the call to finish_decltype_type.
        (tsubst_lambda_expr): Begin/end a lambda scope.  Push the capture
        proxies.
        (maybe_aggr_guide): Adjust the call to finish_decltype_type.
        (do_auto_deduction): Likewise.
        * semantics.cc (automatic_var_p): New.
        (finish_decltype_type): New parameter.  Use it to grab the correct
        lambda and qualifiers.
        * tree.cc (strip_typedefs): Adjust the call to finish_decltype_type.

gcc/testsuite/ChangeLog:

        * g++.dg/cpp0x/lambda/lambda-decltype3.C: Remove xfail.
        * g++.dg/warn/Wshadow-19.C: Add -Wpedantic.  Adjust a dg-warning.
        * g++.dg/warn/Wshadow-6.C: Adjust expected diagnostics.
        * g++.dg/cpp23/lambda-scope1.C: New test.
        * g++.dg/cpp23/lambda-scope2.C: New test.
        * g++.dg/cpp23/lambda-scope3.C: New test.
        * g++.dg/cpp23/lambda-scope4.C: New test.
        * g++.dg/cpp23/lambda-scope4b.C: New test.
        * g++.dg/cpp23/lambda-scope5.C: New test.
        * g++.dg/cpp23/lambda-scope6.C: New test.
        * g++.dg/cpp23/lambda-scope7.C: New test.
---
 gcc/cp/coroutines.cc                          |   1 +
 gcc/cp/cp-tree.def                            |  10 +-
 gcc/cp/cp-tree.h                              |  19 +-
 gcc/cp/lambda.cc                              |  34 ++-
 gcc/cp/name-lookup.cc                         |  12 +-
 gcc/cp/name-lookup.h                          |   3 +-
 gcc/cp/parser.cc                              |  44 ++++
 gcc/cp/parser.h                               |   4 +
 gcc/cp/pt.cc                                  |  19 +-
 gcc/cp/semantics.cc                           |  56 +++--
 gcc/cp/tree.cc                                |   1 +
 .../g++.dg/cpp0x/lambda/lambda-decltype3.C    |   2 +-
 gcc/testsuite/g++.dg/cpp23/lambda-scope1.C    | 102 ++++++++
 gcc/testsuite/g++.dg/cpp23/lambda-scope2.C    | 217 ++++++++++++++++++
 gcc/testsuite/g++.dg/cpp23/lambda-scope3.C    |  44 ++++
 gcc/testsuite/g++.dg/cpp23/lambda-scope4.C    |  41 ++++
 gcc/testsuite/g++.dg/cpp23/lambda-scope4b.C   |  42 ++++
 gcc/testsuite/g++.dg/cpp23/lambda-scope5.C    |  22 ++
 gcc/testsuite/g++.dg/cpp23/lambda-scope6.C    |  20 ++
 gcc/testsuite/g++.dg/cpp23/lambda-scope7.C    |  20 ++
 gcc/testsuite/g++.dg/warn/Wshadow-19.C        |   4 +-
 gcc/testsuite/g++.dg/warn/Wshadow-6.C         |   8 +-
 22 files changed, 682 insertions(+), 43 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp23/lambda-scope1.C
 create mode 100644 gcc/testsuite/g++.dg/cpp23/lambda-scope2.C
 create mode 100644 gcc/testsuite/g++.dg/cpp23/lambda-scope3.C
 create mode 100644 gcc/testsuite/g++.dg/cpp23/lambda-scope4.C
 create mode 100644 gcc/testsuite/g++.dg/cpp23/lambda-scope4b.C
 create mode 100644 gcc/testsuite/g++.dg/cpp23/lambda-scope5.C
 create mode 100644 gcc/testsuite/g++.dg/cpp23/lambda-scope6.C
 create mode 100644 gcc/testsuite/g++.dg/cpp23/lambda-scope7.C

diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc
index 52cc186fbfa..609c870ea91 100644
--- a/gcc/cp/coroutines.cc
+++ b/gcc/cp/coroutines.cc
@@ -5262,6 +5262,7 @@ cp_coroutine_transform::build_ramp_function ()
      When we use a local to hold this, it is decltype(auto).  */
   tree gro_type
     = finish_decltype_type (get_ro, /*id_expression_or_member_access_p*/false,
+                           /*in_lambda_declarator_p=*/false,
                            tf_warning_or_error);
   if (VOID_TYPE_P (gro_type) && !void_ramp_p)
     {
diff --git a/gcc/cp/cp-tree.def b/gcc/cp/cp-tree.def
index bb5aaf983fe..73cceb70703 100644
--- a/gcc/cp/cp-tree.def
+++ b/gcc/cp/cp-tree.def
@@ -448,7 +448,7 @@ DEFTREECODE (TRAIT_EXPR, "trait_expr", tcc_exceptional, 0)
 /* Represents a templated trait that yields a type.  */
 DEFTREECODE (TRAIT_TYPE, "trait_type", tcc_type, 0)
 
-/* A lambda expression.  This is a C++0x extension.
+/* A lambda expression.  This is a C++11 extension.
    LAMBDA_EXPR_DEFAULT_CAPTURE_MODE is an enum for the default, which may be
    none.
    LAMBDA_EXPR_CAPTURE_LIST holds the capture-list, including `this'.
@@ -457,13 +457,15 @@ DEFTREECODE (TRAIT_TYPE, "trait_type", tcc_type, 0)
    be pushed once scope returns to the lambda.  */
 DEFTREECODE (LAMBDA_EXPR, "lambda_expr", tcc_exceptional, 0)
 
-/* The declared type of an expression.  This is a C++0x extension.
+/* The declared type of an expression.  This is a C++11 extension.
    DECLTYPE_TYPE_EXPR is the expression whose type we are computing.
    DECLTYPE_TYPE_ID_EXPR_OR_MEMBER_ACCESS_P states whether the
    expression was parsed as an id-expression or a member access
-   expression. When false, it was parsed as a full expression.
+   expression.  When false, it was parsed as a full expression.
    DECLTYPE_FOR_LAMBDA_CAPTURE is set if we want lambda capture semantics.
-   DECLTYPE_FOR_LAMBDA_RETURN is set if we want lambda return deduction.  */
+   DECLTYPE_FOR_LAMBDA_RETURN is set if we want lambda return deduction.
+   DECLTYPE_IN_LAMBDA_DECLARATOR_P is set if the decltype is in a lambda
+   declarator after the lambda's parameters.  */
 DEFTREECODE (DECLTYPE_TYPE, "decltype_type", tcc_type, 0)
 
 /* A type designated by one of the bases type traits.
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 1b893e23543..6c09834aab2 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -472,6 +472,8 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX];
       BIND_EXPR_VEC_DTOR (in BIND_EXPR)
       ATOMIC_CONSTR_EXPR_FROM_CONCEPT_P (in ATOMIC_CONSTR)
       STATIC_INIT_DECOMP_BASE_P (in the TREE_LIST for {static,tls}_aggregates)
+      LAMBDA_EXPR_MUTABLE_SEEN_P (in LAMBDA_EXPR)
+      DECLTYPE_IN_LAMBDA_DECLARATOR_P (in DECLTYPE_TYPE)
    2: IDENTIFIER_KIND_BIT_2 (in IDENTIFIER_NODE)
       ICS_THIS_FLAG (in _CONV)
       DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (in VAR_DECL)
@@ -1543,6 +1545,11 @@ enum cp_lambda_default_capture_mode_type {
 #define LAMBDA_EXPR_THIS_CAPTURE(NODE) \
   (((struct tree_lambda_expr *)LAMBDA_EXPR_CHECK (NODE))->this_capture)
 
+/* True iff we saw the mutable keyword while parsing the lambda declarator.
+   This is used for decltype in the trailing return type.  */
+#define LAMBDA_EXPR_MUTABLE_SEEN_P(NODE) \
+  TREE_LANG_FLAG_1 (LAMBDA_EXPR_CHECK (NODE))
+
 /* True iff uses of a const variable capture were optimized away.  */
 #define LAMBDA_EXPR_CAPTURE_OPTIMIZED(NODE) \
   TREE_LANG_FLAG_2 (LAMBDA_EXPR_CHECK (NODE))
@@ -5063,6 +5070,13 @@ get_vec_init_expr (tree t)
 #define DECLTYPE_FOR_REF_CAPTURE(NODE) \
   TREE_LANG_FLAG_3 (DECLTYPE_TYPE_CHECK (NODE))
 
+/* When the decltype is in a lambda declarator after the lambda's parameters,
+   as in
+     auto l = [j=0]() mutable -> decltype(j) { ... }
+   In that case, current_lambda_expr should give us the lambda.  */
+#define DECLTYPE_IN_LAMBDA_DECLARATOR_P(NODE) \
+  TREE_LANG_FLAG_1 (DECLTYPE_TYPE_CHECK (NODE))
+
 /* Nonzero for VAR_DECL and FUNCTION_DECL node means that `extern' was
    specified in its declaration.  This can also be set for an
    erroneously declared PARM_DECL.  */
@@ -7681,6 +7695,7 @@ extern location_t defparse_location (tree);
 extern void maybe_show_extern_c_location (void);
 extern bool literal_integer_zerop (const_tree);
 extern tree attr_chainon (tree, tree);
+extern tree make_dummy_lambda_op ();
 
 /* in pt.cc */
 extern tree canonical_type_parameter           (tree);
@@ -8161,7 +8176,8 @@ extern bool cxx_omp_create_clause_info            (tree, 
tree, bool, bool,
 extern tree baselink_for_fns                    (tree);
 extern void finish_static_assert                (tree, tree, location_t,
                                                 bool, bool);
-extern tree finish_decltype_type                (tree, bool, tsubst_flags_t);
+extern tree finish_decltype_type                (tree, bool, bool,
+                                                tsubst_flags_t);
 extern tree fold_builtin_is_corresponding_member (location_t, int, tree *);
 extern tree fold_builtin_is_pointer_inverconvertible_with_class (location_t, 
int, tree *);
 extern tree finish_trait_expr                  (location_t, enum 
cp_trait_kind, tree, tree);
@@ -8208,6 +8224,7 @@ extern void record_lambda_scope                   (tree 
lambda);
 extern void record_lambda_scope_discriminator  (tree lambda);
 extern void record_lambda_scope_sig_discriminator (tree lambda, tree fn);
 extern tree start_lambda_function              (tree fn, tree lambda_expr);
+extern void push_capture_proxies               (tree);
 extern void finish_lambda_function             (tree body);
 extern bool regenerated_lambda_fn_p            (tree);
 extern tree lambda_regenerating_args           (tree);
diff --git a/gcc/cp/lambda.cc b/gcc/cp/lambda.cc
index 182cffaf8d4..117b39b9012 100644
--- a/gcc/cp/lambda.cc
+++ b/gcc/cp/lambda.cc
@@ -409,9 +409,10 @@ lambda_proxy_type (tree ref)
 
 /* MEMBER is a capture field in a lambda closure class.  Now that we're
    inside the operator(), build a placeholder var for future lookups and
-   debugging.  */
+   debugging.  Except that if we're called from push_capture_proxies, we
+   do not have the operator() yet, and have to build up a fake one.  */
 
-static tree
+tree
 build_capture_proxy (tree member, tree init)
 {
   tree var, object, fn, closure, name, lam, type;
@@ -422,6 +423,9 @@ build_capture_proxy (tree member, tree init)
   closure = DECL_CONTEXT (member);
   fn = lambda_function (closure);
   lam = CLASSTYPE_LAMBDA_EXPR (closure);
+  const bool early_p = (fn == NULL_TREE);
+  if (early_p)
+    fn = make_dummy_lambda_op ();
 
   object = DECL_ARGUMENTS (fn);
   /* The proxy variable forwards to the capture field.  */
@@ -503,11 +507,19 @@ build_capture_proxy (tree member, tree init)
 
   if (name == this_identifier)
     {
+      if (early_p)
+       return var;
       gcc_assert (LAMBDA_EXPR_THIS_CAPTURE (lam) == member);
       LAMBDA_EXPR_THIS_CAPTURE (lam) = var;
     }
 
-  if (fn == current_function_decl)
+  if (early_p)
+    {
+      gcc_checking_assert (current_binding_level->kind == sk_lambda);
+      /* insert_capture_proxy below wouldn't push into the lambda scope.  */
+      pushdecl (var);
+    }
+  else if (fn == current_function_decl)
     insert_capture_proxy (var);
   else
     vec_safe_push (LAMBDA_EXPR_PENDING_PROXIES (lam), var);
@@ -1283,7 +1295,7 @@ maybe_add_lambda_conv_op (tree type)
        {
          fn_result = finish_decltype_type
            (decltype_call, /*id_expression_or_member_access_p=*/false,
-            tf_warning_or_error);
+            /*in_lambda_declarator_p=*/false, tf_warning_or_error);
        }
     }
   else if (thisarg)
@@ -1776,6 +1788,16 @@ record_lambda_scope_sig_discriminator (tree lambda, tree 
fn)
   LAMBDA_EXPR_SCOPE_SIG_DISCRIMINATOR (lambda) = sig->count++;
 }
 
+/* Push the proxies for any explicit captures in LAMBDA_EXPR.  */
+
+void
+push_capture_proxies (tree lambda_expr)
+{
+  for (tree cap = LAMBDA_EXPR_CAPTURE_LIST (lambda_expr); cap;
+       cap = TREE_CHAIN (cap))
+    build_capture_proxy (TREE_PURPOSE (cap), TREE_VALUE (cap));
+}
+
 tree
 start_lambda_function (tree fco, tree lambda_expr)
 {
@@ -1788,9 +1810,7 @@ start_lambda_function (tree fco, tree lambda_expr)
   tree body = begin_function_body ();
 
   /* Push the proxies for any explicit captures.  */
-  for (tree cap = LAMBDA_EXPR_CAPTURE_LIST (lambda_expr); cap;
-       cap = TREE_CHAIN (cap))
-    build_capture_proxy (TREE_PURPOSE (cap), TREE_VALUE (cap));
+  push_capture_proxies (lambda_expr);
 
   return body;
 }
diff --git a/gcc/cp/name-lookup.cc b/gcc/cp/name-lookup.cc
index 9aa7c16f64b..456b632aa74 100644
--- a/gcc/cp/name-lookup.cc
+++ b/gcc/cp/name-lookup.cc
@@ -3351,8 +3351,12 @@ check_local_shadow (tree decl)
        }
       /* Don't complain if it's from an enclosing function.  */
       else if (DECL_CONTEXT (old) == current_function_decl
-              && TREE_CODE (decl) != PARM_DECL
-              && TREE_CODE (old) == PARM_DECL)
+              && ((TREE_CODE (decl) != PARM_DECL
+                   && TREE_CODE (old) == PARM_DECL)
+                  || (is_capture_proxy (old)
+                      && current_lambda_expr ()
+                      && DECL_CONTEXT (old)
+                         == lambda_function (current_lambda_expr ()))))
        {
          /* Go to where the parms should be and see if we find
             them there.  */
@@ -4635,7 +4639,8 @@ cp_binding_level_descriptor (cp_binding_level *scope)
     "template-parameter-scope",
     "template-explicit-spec-scope",
     "transaction-scope",
-    "openmp-scope"
+    "openmp-scope",
+    "lambda-scope"
   };
   static_assert (ARRAY_SIZE (scope_kind_names) == sk_count,
                 "must keep names aligned with scope_kind enum");
@@ -4726,6 +4731,7 @@ begin_scope (scope_kind kind, tree entity)
     case sk_transaction:
     case sk_omp:
     case sk_stmt_expr:
+    case sk_lambda:
       scope->keep = keep_next_level_flag;
       break;
 
diff --git a/gcc/cp/name-lookup.h b/gcc/cp/name-lookup.h
index 2fa736bd046..a47989e1374 100644
--- a/gcc/cp/name-lookup.h
+++ b/gcc/cp/name-lookup.h
@@ -214,6 +214,7 @@ enum scope_kind {
                        "template <>", this scope is always empty.  */
   sk_transaction,    /* A synchronized or atomic statement.  */
   sk_omp,           /* An OpenMP structured block.  */
+  sk_lambda,        /* A lambda scope.  */
   sk_count          /* Number of scope_kind enumerations.  */
 };
 
@@ -287,7 +288,7 @@ struct GTY(()) cp_binding_level {
   /* The kind of scope that this object represents.  However, a
       SK_TEMPLATE_SPEC scope is represented with KIND set to
       SK_TEMPLATE_PARMS and EXPLICIT_SPEC_P set to true.  */
-  ENUM_BITFIELD (scope_kind) kind : 4;
+  ENUM_BITFIELD (scope_kind) kind : 5;
 
   /* True if this scope is an SK_TEMPLATE_SPEC scope.  This field is
       only valid if KIND == SK_TEMPLATE_PARMS.  */
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index 239e6f9a556..d6d9994f715 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -11807,6 +11807,9 @@ cp_parser_lambda_expression (cp_parser* parser)
     if (type == error_mark_node)
       return error_mark_node;
 
+    /* A lambda scope starts immediately after the lambda-introducer of E
+       and extends to the end of the compound-statement of E.  */
+    begin_scope (sk_lambda, NULL_TREE);
     record_lambda_scope (lambda_expr);
     record_lambda_scope_discriminator (lambda_expr);
 
@@ -11857,6 +11860,10 @@ cp_parser_lambda_expression (cp_parser* parser)
     if (cp_parser_start_tentative_firewall (parser))
       start = token;
 
+    /* Inject the captures for the sake of the possible
+       trailing-return-type -- we have to be able to look them up.  */
+    push_capture_proxies (lambda_expr);
+
     ok &= cp_parser_lambda_declarator_opt (parser, lambda_expr);
 
     if (ok && cp_parser_error_occurred (parser))
@@ -11877,6 +11884,8 @@ cp_parser_lambda_expression (cp_parser* parser)
     if (ok)
       maybe_add_lambda_conv_op (type);
 
+    /* Leave the lambda scope.  */
+    pop_bindings_and_leave_scope ();
     finish_struct (type, /*attributes=*/NULL_TREE);
 
     in_consteval_if_p = save_in_consteval_if_p;
@@ -12333,6 +12342,8 @@ 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;
 
+  auto oild = make_temp_override (parser->in_lambda_declarator_p, true);
+
   /* [expr.prim.lambda.general]
      lambda-specifier:
        consteval, constexpr, mutable, static
@@ -12424,6 +12435,7 @@ cp_parser_lambda_declarator_opt (cp_parser* parser, 
tree lambda_expr)
   else if (lambda_specs.storage_class == sc_mutable)
     {
       quals = TYPE_UNQUALIFIED;
+      LAMBDA_EXPR_MUTABLE_SEEN_P (lambda_expr) = true;
     }
   else if (lambda_specs.storage_class == sc_static)
     {
@@ -12577,6 +12589,37 @@ cp_parser_lambda_declarator_opt (cp_parser* parser, 
tree lambda_expr)
   }
 }
 
+/* Create a fake operator() for a lambda.  We do this so that we can
+   build_capture_proxy even before start_lambda_function.  */
+
+tree
+make_dummy_lambda_op ()
+{
+  cp_decl_specifier_seq return_type_specs;
+  cp_cv_quals quals = TYPE_UNQUALIFIED;
+
+  clear_decl_specs (&return_type_specs);
+  return_type_specs.type = make_auto ();
+
+  void *p = obstack_alloc (&declarator_obstack, 0);
+
+  cp_declarator *declarator = make_id_declarator (NULL_TREE,
+                                                 call_op_identifier,
+                                                 sfk_none,
+                                                 input_location);
+
+  declarator = make_call_declarator (declarator, void_list_node, quals,
+                                    VIRT_SPEC_UNSPECIFIED,
+                                    REF_QUAL_NONE, NULL_TREE,
+                                    NULL_TREE, NULL_TREE, NULL_TREE,
+                                    NULL_TREE, UNKNOWN_LOCATION);
+
+  tree fco = grokmethod (&return_type_specs, declarator, NULL_TREE);
+  obstack_free (&declarator_obstack, p);
+
+  return fco;
+}
+
 /* Parse the body of a lambda expression, which is simply
 
    compound-statement
@@ -17992,6 +18035,7 @@ cp_parser_decltype (cp_parser *parser)
     expr = make_decltype_auto ();
   else
     expr = finish_decltype_type (expr, id_expression_or_member_access_p,
+                                parser->in_lambda_declarator_p,
                                 tf_warning_or_error);
 
   /* Replace the decltype with a CPP_DECLTYPE so we don't need to parse
diff --git a/gcc/cp/parser.h b/gcc/cp/parser.h
index f9ed80123c4..b3ad4f9a842 100644
--- a/gcc/cp/parser.h
+++ b/gcc/cp/parser.h
@@ -439,6 +439,10 @@ struct GTY(()) cp_parser {
      return type. */
   bool in_result_type_constraint_p;
 
+  /* True if we find ourselves in a lambda declarator after the lambda's
+     parameters.  */
+  bool in_lambda_declarator_p;
+
   /* True if a constrained-type-specifier is not allowed in this
      context e.g., because they could never be deduced.  */
   int prevent_constrained_type_specifiers;
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index 3362a6f8f9c..f459007672a 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -17363,7 +17363,9 @@ tsubst (tree t, tree args, tsubst_flags_t complain, 
tree in_decl)
                 it produces an expression, it's not an id-expression or
                 member access.  */
              id = false;
-           type = finish_decltype_type (type, id, complain);
+           type = finish_decltype_type (type, id,
+                                        DECLTYPE_IN_LAMBDA_DECLARATOR_P (t),
+                                        complain);
          }
        return cp_build_qualified_type (type,
                                        cp_type_quals (t)
@@ -20430,6 +20432,8 @@ tsubst_lambda_expr (tree t, tree args, tsubst_flags_t 
complain, tree in_decl)
       return error_mark_node;
     }
 
+  begin_scope (sk_lambda, NULL_TREE);
+
   if (LAMBDA_EXPR_EXTRA_SCOPE (t))
     record_lambda_scope (r);
   if (TYPE_NAMESPACE_SCOPE_P (TREE_TYPE (t)))
@@ -20453,6 +20457,10 @@ tsubst_lambda_expr (tree t, tree args, tsubst_flags_t 
complain, tree in_decl)
 
   tree fntype = static_fn_type (oldfn);
 
+  /* Like in cp_parser_lambda_expression, we need to bring the captures
+     into the lambda scope.  */
+  push_capture_proxies (r);
+
   tree saved_ctp = current_template_parms;
   if (oldtmpl)
     {
@@ -20568,6 +20576,7 @@ tsubst_lambda_expr (tree t, tree args, tsubst_flags_t 
complain, tree in_decl)
     }
 
 out:
+  pop_bindings_and_leave_scope ();
   finish_struct (type, /*attr*/NULL_TREE);
 
   insert_pending_capture_proxies ();
@@ -31001,7 +31010,9 @@ maybe_aggr_guide (tree tmpl, tree init, vec<tree,va_gc> 
*args)
          field = next_aggregate_field (field);
          if (!field)
            return NULL_TREE;
-         tree ftype = finish_decltype_type (field, true, complain);
+         tree ftype = finish_decltype_type (field, true,
+                                            /*in_lambda_declarator_p=*/false,
+                                            complain);
          parms = tree_cons (NULL_TREE, ftype, parms);
        }
     }
@@ -31972,7 +31983,9 @@ do_auto_deduction (tree type, tree init, tree auto_node,
   else if (AUTO_IS_DECLTYPE (auto_node))
     {
       const bool id = unparenthesized_id_or_class_member_access_p (init);
-      tree deduced = finish_decltype_type (init, id, complain);
+      tree deduced = finish_decltype_type (init, id,
+                                          /*in_lambda_declarator_p=*/false,
+                                          complain);
       deduced = canonicalize_type_argument (deduced, complain);
       if (deduced == error_mark_node)
        return error_mark_node;
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 28baf7b3172..2191104017e 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -4507,6 +4507,14 @@ outer_automatic_var_p (tree decl)
          && !TREE_STATIC (decl));
 }
 
+/* Returns true iff DECL is an automatic variable.  */
+
+static bool
+automatic_var_p (tree decl)
+{
+  return VAR_P (decl) && !TREE_STATIC (decl);
+}
+
 /* DECL satisfies outer_automatic_var_p.  Possibly complain about it or
    rewrite it for lambda capture.
 
@@ -12689,11 +12697,12 @@ finish_static_assert (tree condition, tree message, 
location_t location,
 
    ID_EXPRESSION_OR_MEMBER_ACCESS_P is true when EXPR was parsed as an
    id-expression or a class member access, FALSE when it was parsed as
-   a full expression.  */
+   a full expression.  IN_LAMBDA_DECLARATOR_P is true if we are in
+   a lambda declarator after the lambda's parameters.  */
 
 tree
 finish_decltype_type (tree expr, bool id_expression_or_member_access_p,
-                     tsubst_flags_t complain)
+                     bool in_lambda_declarator_p, tsubst_flags_t complain)
 {
   tree type = NULL_TREE;
 
@@ -12725,6 +12734,7 @@ finish_decltype_type (tree expr, bool 
id_expression_or_member_access_p,
       DECLTYPE_TYPE_EXPR (type) = expr;
       DECLTYPE_TYPE_ID_EXPR_OR_MEMBER_ACCESS_P (type)
         = id_expression_or_member_access_p;
+      DECLTYPE_IN_LAMBDA_DECLARATOR_P (type) = in_lambda_declarator_p;
       SET_TYPE_STRUCTURAL_EQUALITY (type);
 
       return type;
@@ -12885,9 +12895,11 @@ finish_decltype_type (tree expr, bool 
id_expression_or_member_access_p,
     }
   else
     {
-      if (outer_automatic_var_p (STRIP_REFERENCE_REF (expr))
-         && current_function_decl
-         && LAMBDA_FUNCTION_P (current_function_decl))
+      if ((outer_automatic_var_p (STRIP_REFERENCE_REF (expr))
+          && current_function_decl
+          && LAMBDA_FUNCTION_P (current_function_decl))
+         || (automatic_var_p (STRIP_REFERENCE_REF (expr))
+             && in_lambda_declarator_p))
        {
          /* [expr.prim.id.unqual]/3: If naming the entity from outside of an
             unevaluated operand within S would refer to an entity captured by
@@ -12905,7 +12917,10 @@ finish_decltype_type (tree expr, bool 
id_expression_or_member_access_p,
             And we don't handle nested lambdas properly, where we need to
             consider the outer lambdas as well (PR112926). */
          tree decl = STRIP_REFERENCE_REF (expr);
-         tree lam = CLASSTYPE_LAMBDA_EXPR (DECL_CONTEXT 
(current_function_decl));
+         tree lam
+           = (in_lambda_declarator_p
+              ? current_lambda_expr ()
+              : CLASSTYPE_LAMBDA_EXPR (DECL_CONTEXT (current_function_decl)));
          tree cap = lookup_name (DECL_NAME (decl), LOOK_where::BLOCK,
                                  LOOK_want::HIDDEN_LAMBDA);
 
@@ -12921,17 +12936,24 @@ finish_decltype_type (tree expr, bool 
id_expression_or_member_access_p,
 
          if (type && !TYPE_REF_P (type))
            {
-             tree obtype = TREE_TYPE (DECL_ARGUMENTS (current_function_decl));
-             if (WILDCARD_TYPE_P (non_reference (obtype)))
-               /* We don't know what the eventual obtype quals will be.  */
-               goto dependent;
-             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));
+             int quals;
+             if (in_lambda_declarator_p)
+               quals = (LAMBDA_EXPR_MUTABLE_SEEN_P (lam)
+                        ? TYPE_UNQUALIFIED : TYPE_QUAL_CONST);
+             else
+               {
+                 tree obtype = TREE_TYPE (DECL_ARGUMENTS 
(current_function_decl));
+                 if (WILDCARD_TYPE_P (non_reference (obtype)))
+                   /* We don't know what the eventual obtype quals will be.  */
+                   goto dependent;
+                 auto direct_type = [](tree t){
+                     if (INDIRECT_TYPE_P (t))
+                       return TREE_TYPE (t);
+                     return t;
+                  };
+                 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/cp/tree.cc b/gcc/cp/tree.cc
index 5863b6878f0..dd4ae9896e1 100644
--- a/gcc/cp/tree.cc
+++ b/gcc/cp/tree.cc
@@ -1833,6 +1833,7 @@ strip_typedefs (tree t, bool *remove_attributes /* = NULL 
*/,
        result = (finish_decltype_type
                  (result,
                   DECLTYPE_TYPE_ID_EXPR_OR_MEMBER_ACCESS_P (t),
+                  DECLTYPE_IN_LAMBDA_DECLARATOR_P (t),
                   tf_none));
       break;
     case TRAIT_TYPE:
diff --git a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-decltype3.C 
b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-decltype3.C
index 2e06e496140..6ba0dabc37d 100644
--- a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-decltype3.C
+++ b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-decltype3.C
@@ -11,7 +11,7 @@ void f() {
     decltype((x)) y2 = y1;      // y2 has type float const&
     decltype(r) r1 = y1;        // r1 has type float&
     decltype((r)) r2 = y2;      // r2 has type float const&
-    return y2;                  // { dg-bogus "'float&' to 'const float'" "" { 
xfail *-*-* } }
+    return y2;                  // { dg-bogus "'float&' to 'const float'" }
   };
 
   [=](decltype((x)) y) {
diff --git a/gcc/testsuite/g++.dg/cpp23/lambda-scope1.C 
b/gcc/testsuite/g++.dg/cpp23/lambda-scope1.C
new file mode 100644
index 00000000000..5a5f5c5067b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/lambda-scope1.C
@@ -0,0 +1,102 @@
+// P2036R3 - Change scope of lambda trailing-return-type
+// PR c++/102610
+// { dg-do compile { target c++23 } }
+
+template <typename T, typename U>
+constexpr bool is_same = false;
+
+template <typename T>
+constexpr bool is_same<T, T> = true;
+
+struct S {
+  void foo () {
+    auto counter1 = [j=0]() mutable -> decltype(j) {
+      return j++;
+    };
+  }
+};
+
+// [expr.prim.id.unqual]/3.2
+void
+f ()
+{
+  float x, &r = x;
+
+  [=]() -> decltype((x)) {      // lambda returns float const& because this 
lambda is not mutable and
+                                // x is an lvalue
+    decltype(x) y1;             // y1 has type float
+    decltype((x)) y2 = y1;      // y2 has type float const&
+    decltype(r) r1 = y1;        // r1 has type float&
+    decltype((r)) r2 = y2;      // r2 has type float const&
+    return y2;
+  };
+
+  [=](decltype((x)) y) {
+    decltype((x)) z = x;        // OK, y has type float&, z has type float 
const&
+    static_assert(is_same<float&, decltype((y))>);
+    static_assert(is_same<const float&, decltype((z))>);
+  };
+
+  [=] {
+    [](decltype((x)) y) {     // OK, lambda takes a parameter of type float 
const&
+    };
+
+    [x=1](decltype((x)) y) {
+      decltype((x)) z = x;      // OK, y has type int&, z has type int const&
+      // FIXME We don't handle nested lambdas yet?
+      //static_assert(is_same<int&, decltype((y))>);
+      static_assert(is_same<const int&, decltype((z))>);
+    };
+  };
+
+  [x=1](decltype((x)) y) {
+    decltype((x)) z = x;
+    static_assert(is_same<int&, decltype((y))>);
+    static_assert(is_same<const int&, decltype((z))>);
+  };
+}
+
+void
+ok ()
+{
+  auto counter1 = [j=0]() mutable -> decltype(j) {
+    static_assert(is_same<int&, decltype((j))>);
+    return j++;
+  };
+
+  auto l = [j=0]() -> decltype(j) {
+    static_assert(is_same<const int&, decltype((j))>);
+    return j;
+  };
+
+  int y;
+  [=] -> decltype((y)) {
+    return y;
+  };
+}
+
+void
+bad ()
+{
+  [x=1](int x){};  // { dg-error "declared as a capture" }
+  [x=1]{ int x; };  // { dg-error "shadows a parameter" }
+
+  auto f = [i = 5] () { int i; return 0; }; // { dg-error "shadows a 
parameter" }
+  auto f2 = [i = 5] <int N> () { int i; return 0; };  // { dg-error "shadows a 
parameter" }
+
+  // [expr.prim.lambda.capture]/5
+  int x = 0;
+  auto g = [x](int x) { return 0; };  // { dg-error "declared as a capture" }
+  auto h = [y = 0]<typename y>(y) { return 0; };  // { dg-error "shadows 
template parameter" }
+
+  auto l2 = [i = 0, j = i]() -> decltype(i) { // { dg-error "not declared in 
this scope" }
+    return i;
+  };
+
+}
+
+void
+foo ()
+{
+  int x = [x](int y[sizeof x]){return sizeof x;}(0);
+}
diff --git a/gcc/testsuite/g++.dg/cpp23/lambda-scope2.C 
b/gcc/testsuite/g++.dg/cpp23/lambda-scope2.C
new file mode 100644
index 00000000000..6b55e5fe513
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/lambda-scope2.C
@@ -0,0 +1,217 @@
+// P2036R3 - Change scope of lambda trailing-return-type
+// PR c++/102610
+// { dg-do compile { target c++23 } }
+// From LLVM's test/SemaCXX/lambda-capture-type-deduction.cpp
+
+template <typename T, typename U>
+constexpr bool is_same = false;
+
+template <typename T>
+constexpr bool is_same<T, T> = true;
+
+void
+f ()
+{
+  int y;
+
+  static_assert(is_same<const int &,
+                       decltype([x = 1] -> decltype((x)) { return x; }())>);
+
+  static_assert(is_same<int &,
+                        decltype([x = 1] mutable -> decltype((x)) { return x; 
}())>);
+
+  static_assert(is_same<const int &,
+                        decltype([=] -> decltype((y)) { return y; }())>);
+
+  static_assert(is_same<int &,
+                        decltype([=] mutable -> decltype((y)) { return y; 
}())>);
+
+  // Clang++ rejects this one, though the only difference is the extra (),
+  // and without the () the result is correct, as demonstrated above.
+  static_assert(is_same<const int &,
+                        decltype([=]() -> decltype((y)) { return y; }())>);
+
+  static_assert(is_same<int &,
+                        decltype([=]() mutable -> decltype((y)) { return y; 
}())>);
+
+  static_assert(is_same<const int &,
+                       decltype([y] -> decltype((y)) { return y; }())>);
+
+  static_assert(is_same<int &,
+                        decltype([y] mutable -> decltype((y)) { return y; 
}())>);
+
+
+  auto ref = [&x = y](
+                 decltype([&](decltype(x)) { return 0; }) y) {
+    return x;
+  };
+}
+
+void
+nested ()
+{
+  int x, y, z;
+  (void)[&](
+      decltype([&](
+                   decltype([=](
+                                decltype([&](
+                                             decltype([&](decltype(x)) {})) 
{})) {})) {})){};
+
+  (void)[&](
+      decltype([&](
+                   decltype([&](
+                                decltype([&](
+                                             decltype([&](decltype(y)) {})) 
{})) {})) {})){};
+
+  (void)[=](
+      decltype([=](
+                   decltype([=](
+                                decltype([=](
+                                             decltype([&]<decltype(z)> {})) 
{})) {})) {})){};
+}
+
+void
+test_noexcept ()
+{
+  int y;
+
+  static_assert(noexcept([x = 1] noexcept(is_same<const int &, decltype((x))>) 
{}()));
+  static_assert(noexcept([x = 1] mutable noexcept(is_same<int &, 
decltype((x))>) {}()));
+  static_assert(noexcept([y] noexcept(is_same<const int &, decltype((y))>) 
{}()));
+  static_assert(noexcept([y] mutable noexcept(is_same<int &, decltype((y))>) 
{}()));
+  static_assert(noexcept([=] noexcept(is_same<const int &, decltype((y))>) 
{}()));
+  static_assert(noexcept([=] mutable noexcept(is_same<int &, decltype((y))>) 
{}()));
+  static_assert(noexcept([&] noexcept(is_same<int &, decltype((y))>) {}()));
+  static_assert(noexcept([&] mutable noexcept(is_same<int &, decltype((y))>) 
{}()));
+}
+
+void
+check_params ()
+{
+  int i = 0;
+  int &j = i;
+
+  [=](decltype((j)) jp, decltype((i)) ip) {
+    static_assert(is_same<const int&, decltype((j))>);
+    static_assert(is_same<const int &, decltype((i))>);
+    static_assert(is_same<int &, decltype((jp))>);
+    static_assert(is_same<int &, decltype((ip))>);
+  };
+
+  [=](decltype((j)) jp, decltype((i)) ip) mutable {
+    static_assert(is_same<int &, decltype((j))>);
+    static_assert(is_same<int &, decltype((i))>);
+    static_assert(is_same<int &, decltype((jp))>);
+    static_assert(is_same<int &, decltype((ip))>);
+    static_assert(is_same<int &, decltype(jp)>);
+    static_assert(is_same<int &, decltype(ip)>);
+  };
+
+  [a = 0](decltype((a)) ap) mutable {
+    static_assert(is_same<int &, decltype((a))>);
+    static_assert(is_same<int, decltype(a)>);
+    static_assert(is_same<int &, decltype(ap)>);
+    decltype(a) x;
+    decltype((a)) y = x;
+    static_assert(is_same<int &, decltype(y)>);
+  };
+
+  [a = 0](decltype((a)) ap) {
+    static_assert(is_same<const int &, decltype((a))>);
+    static_assert(is_same<int, decltype(a)>);
+    static_assert(is_same<int&, decltype((ap))>);
+    decltype(a) x;
+    decltype((a)) y = x;
+    static_assert(is_same<const int &, decltype(y)>);
+  };
+
+  int a;
+  [a](decltype((a)) ap) mutable {
+    static_assert(is_same<int &, decltype((a))>);
+    static_assert(is_same<int, decltype(a)>);
+    static_assert(is_same<int &, decltype(ap)>);
+    decltype(a) x;
+    decltype((a)) y = x;
+    static_assert(is_same<int &, decltype(y)>);
+  };
+
+  [a](decltype((a)) ap) {
+    static_assert(is_same<const int &, decltype((a))>);
+    static_assert(is_same<int, decltype(a)>);
+    static_assert(is_same<int&, decltype((ap))>);
+    decltype(a) x;
+    decltype((a)) y = x;
+    static_assert(is_same<const int &, decltype(y)>);
+  };
+}
+
+template <typename T>
+void
+check_params_tpl ()
+{
+  T i = 0;
+  T &j = i;
+  (void)[=](decltype((j)) jp, decltype((i)) ip) {
+    static_assert(is_same<const int&, decltype((j))>);
+    static_assert(is_same<const int &, decltype((i))>);
+    // In these two, clang++ produces 'const int&'.  Why, when it's
+    // the same as in check_params, just not a template?
+    static_assert(is_same<int &, decltype((jp))>);
+    static_assert(is_same<int &, decltype((ip))>);
+  };
+
+  (void)[=](decltype((j)) jp, decltype((i)) ip) mutable {
+    static_assert(is_same<int &, decltype((j))>);
+    static_assert(is_same<int &, decltype((i))>);
+    static_assert(is_same<int &, decltype((jp))>);
+    static_assert(is_same<int &, decltype((ip))>);
+    static_assert(is_same<int &, decltype(jp)>);
+    static_assert(is_same<int &, decltype(ip)>);
+  };
+
+  (void)[a = 0](decltype((a)) ap) mutable {
+    static_assert(is_same<int &, decltype((a))>);
+    static_assert(is_same<int, decltype(a)>);
+    static_assert(is_same<int &, decltype(ap)>);
+  };
+  (void)[a = 0](decltype((a)) ap) {
+    static_assert(is_same<const int &, decltype((a))>);
+    static_assert(is_same<int, decltype(a)>);
+    static_assert(is_same<int&, decltype((ap))>);
+  };
+}
+
+template<typename T>
+void
+test_requires ()
+{
+  int x;
+
+  [x = 1]() requires is_same<const int &, decltype((x))> {} ();
+  [x = 1]() mutable requires is_same<int &, decltype((x))> {}
+  ();
+  [x]() requires is_same<const int &, decltype((x))> {} ();
+  [x]() mutable requires is_same<int &, decltype((x))> {}
+  ();
+  [=]() requires is_same<const int &, decltype((x))> {} ();
+  [=]() mutable requires is_same<int &, decltype((x))> {}
+  ();
+  [&]() requires is_same<int &, decltype((x))> {}
+  ();
+  [&]() mutable requires is_same<int &, decltype((x))> {}
+  ();
+  [&x]() requires is_same<int &, decltype((x))> {}
+  ();
+  [&x]() mutable requires is_same<int &, decltype((x))> {}
+  ();
+
+  [x = 1]() requires is_same<const int &, decltype((x))> {} ();
+  [x = 1]() mutable requires is_same<int &, decltype((x))> {} ();
+}
+
+void
+use ()
+{
+  test_requires<int>();
+  check_params_tpl<int>();
+}
diff --git a/gcc/testsuite/g++.dg/cpp23/lambda-scope3.C 
b/gcc/testsuite/g++.dg/cpp23/lambda-scope3.C
new file mode 100644
index 00000000000..697fdaae095
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/lambda-scope3.C
@@ -0,0 +1,44 @@
+// P2036R3 - Change scope of lambda trailing-return-type
+// PR c++/102610
+// { dg-do compile { target c++17 } }
+
+template <typename T>
+inline constexpr auto
+equal1 (T &&t)
+{
+  return [t = 3](const auto& obj) -> decltype(obj == t)
+    {
+      return obj == t;
+    };
+}
+
+template <typename T>
+inline constexpr auto
+equal2 (T &&t)
+{
+  return [t = t](const auto& obj) -> decltype(obj == t)
+    {
+      return obj == t;
+    };
+}
+
+template <typename T>
+inline constexpr auto
+equal3 (T &&t)
+{
+  return [t = 4](const auto& obj) -> decltype(obj == t)
+    {
+      return obj == t;
+    };
+}
+
+void
+g ()
+{
+  constexpr auto l1 = equal1 (5);
+  static_assert (l1 (3));
+  constexpr auto l2 = equal2 (3);
+  static_assert (l2 (3));
+  constexpr auto l3 = equal3 (2);
+  static_assert (l3 (4));
+}
diff --git a/gcc/testsuite/g++.dg/cpp23/lambda-scope4.C 
b/gcc/testsuite/g++.dg/cpp23/lambda-scope4.C
new file mode 100644
index 00000000000..9442db3f956
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/lambda-scope4.C
@@ -0,0 +1,41 @@
+// P2036R3 - Change scope of lambda trailing-return-type
+// PR c++/102610
+// { dg-do compile { target c++17 } }
+
+struct integral_constant {
+  using type = integral_constant;
+};
+template <bool> using __bool_constant = integral_constant;
+template <typename _Fn, typename>
+struct is_invocable : __bool_constant<true> {};
+int forward() { return 42; }
+template <typename...> class tuple;
+struct plus {
+  template <typename _Tp, typename _Up>
+  constexpr auto operator()(_Tp __t, _Up __u) {
+    return __t > __u;
+  }
+};
+constexpr auto equal() {
+  int t = 0;
+  return [t = 3](auto obj) -> decltype(obj == t) { return t; };
+}
+template <typename> struct is_tuple_invocable;
+template <typename... Ts> struct is_tuple_invocable<tuple<Ts...>> {
+  using type = typename is_invocable<Ts...>::type;
+};
+namespace detail {
+template <typename F, typename Tail, typename... T>
+constexpr auto compose(__bool_constant<true>, F f, Tail tail, T... objs) {
+  return f(tail(objs...));
+}
+} // namespace detail
+template <typename F, typename... Fs> constexpr auto compose(F f, Fs... fs) {
+  return [f, tail(fs...)](auto... objs) {
+    auto unitail =
+        typename is_tuple_invocable<tuple<decltype(objs)...>>::type{};
+    return detail::compose(unitail, f, tail, objs...);
+  };
+}
+template <auto> constexpr auto eq = equal();
+static_assert(compose(eq<3>, plus{})(1, 2));
diff --git a/gcc/testsuite/g++.dg/cpp23/lambda-scope4b.C 
b/gcc/testsuite/g++.dg/cpp23/lambda-scope4b.C
new file mode 100644
index 00000000000..6a9e6ec5e9d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/lambda-scope4b.C
@@ -0,0 +1,42 @@
+// P2036R3 - Change scope of lambda trailing-return-type
+// PR c++/102610
+// { dg-do compile { target c++17 } }
+
+struct integral_constant {
+  using type = integral_constant;
+};
+template <bool> using __bool_constant = integral_constant;
+template <typename _Fn, typename>
+struct is_invocable : __bool_constant<true> {};
+int forward() { return 42; }
+template <typename...> class tuple;
+struct plus {
+  template <typename _Tp, typename _Up>
+  constexpr auto operator()(_Tp __t, _Up __u) {
+    return __t > __u;
+  }
+};
+constexpr auto equal() {
+  int t = 0;
+  return [t = 3](auto obj) -> decltype(obj == t) { return t; };
+}
+template <typename> struct is_tuple_invocable;
+template <typename... Ts> struct is_tuple_invocable<tuple<Ts...>> {
+  using type = typename is_invocable<Ts...>::type;
+};
+namespace detail {
+template <typename F, typename Tail, typename... T>
+constexpr auto compose(__bool_constant<true>, F f, Tail tail, T... objs) {
+  return f(tail(objs...));
+}
+} // namespace detail
+template <typename F, typename... Fs>
+constexpr auto compose(F f, Fs... fs) {
+  return [f, tail(fs...)](auto... objs) -> decltype (detail::compose(typename 
is_tuple_invocable<tuple<decltype(objs)...>>::type{}, f, tail, objs...)) {
+    auto unitail =
+        typename is_tuple_invocable<tuple<decltype(objs)...>>::type{};
+    return detail::compose(unitail, f, tail, objs...);
+  };
+}
+template <auto> constexpr auto eq = equal();
+static_assert(compose(eq<3>, plus{})(1, 2));
diff --git a/gcc/testsuite/g++.dg/cpp23/lambda-scope5.C 
b/gcc/testsuite/g++.dg/cpp23/lambda-scope5.C
new file mode 100644
index 00000000000..48b5ef34c9e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/lambda-scope5.C
@@ -0,0 +1,22 @@
+// P2036R3 - Change scope of lambda trailing-return-type
+// PR c++/102610
+// { dg-do compile { target c++17 } }
+
+constexpr auto equal() {
+  int t = 0;
+  return [t](auto obj) { return obj; };
+}
+template <typename F>
+constexpr int bar (F) {
+  return 42;
+}
+
+template <typename F>
+constexpr auto compose(F f)
+{
+  return [f=f](int i) -> decltype(bar (f)) {
+      return 42;
+  };
+}
+template <auto> constexpr auto eq = equal();
+static_assert(compose(eq<3>)(1));
diff --git a/gcc/testsuite/g++.dg/cpp23/lambda-scope6.C 
b/gcc/testsuite/g++.dg/cpp23/lambda-scope6.C
new file mode 100644
index 00000000000..720c1ec6cba
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/lambda-scope6.C
@@ -0,0 +1,20 @@
+// P2036R3 - Change scope of lambda trailing-return-type
+// PR c++/102610
+// { dg-do compile { target c++23 } }
+
+void
+g ()
+{
+  /* It looks like this shouldn't work but [expr.prim.lambda.closure]/6
+     says "Otherwise, it is a non-static member function or member function
+     template that is declared const if and only if the lambda-expression's
+     parameter-declaration-clause is not followed by mutable and the
+     lambda-declarator does not contain an explicit object parameter."  */
+  auto counter = [j=0](this auto const& self) -> decltype(j) {
+      return j++;
+  };
+
+  auto counter2 = [j=0](this auto& self) -> decltype(j) {
+      return j++;
+  };
+}
diff --git a/gcc/testsuite/g++.dg/cpp23/lambda-scope7.C 
b/gcc/testsuite/g++.dg/cpp23/lambda-scope7.C
new file mode 100644
index 00000000000..6db39bd8692
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/lambda-scope7.C
@@ -0,0 +1,20 @@
+// P2036R3 - Change scope of lambda trailing-return-type
+// PR c++/102610
+// { dg-do compile { target c++14_only } }
+
+template <typename T>
+inline constexpr auto
+equal1 (T &&t)
+{
+  return [t = 3](const auto& obj) -> decltype(obj == t)
+    {
+      return obj == t;
+    };
+}
+
+void
+g ()
+{
+  constexpr auto l1 = equal1 (5); // { dg-error "not literal" }
+  static_assert (l1 (3), "");    // { dg-error "non-constant|non-.constexpr." }
+}
diff --git a/gcc/testsuite/g++.dg/warn/Wshadow-19.C 
b/gcc/testsuite/g++.dg/warn/Wshadow-19.C
index 030aefd153d..d0b8dba8549 100644
--- a/gcc/testsuite/g++.dg/warn/Wshadow-19.C
+++ b/gcc/testsuite/g++.dg/warn/Wshadow-19.C
@@ -1,5 +1,5 @@
 // { dg-do compile }
-// { dg-options "-Wshadow" }
+// { dg-options "-Wshadow -Wpedantic" }
 
 void
 foo (int x)
@@ -10,7 +10,7 @@ foo (int x)
     extern int y;                              // { dg-warning "declaration of 
'y' shadows a previous local" }
   }
 #if __cplusplus >= 201102L
-  auto fn = [x] () { extern int x; return 0; };        // { dg-warning 
"declaration of 'x' shadows a lambda capture" "" { target c++11 } }
+  auto fn = [x] () { extern int x; return 0; };    // { dg-warning 
"declaration of 'int x' shadows a parameter" "" { target c++11 } }
 #endif
 }
 
diff --git a/gcc/testsuite/g++.dg/warn/Wshadow-6.C 
b/gcc/testsuite/g++.dg/warn/Wshadow-6.C
index 1d8d21b9b6f..7b44d52781a 100644
--- a/gcc/testsuite/g++.dg/warn/Wshadow-6.C
+++ b/gcc/testsuite/g++.dg/warn/Wshadow-6.C
@@ -33,8 +33,8 @@ void f2(struct S i, int j) {
 
 void f3(int i) {
  [=]{
-   int j = i;                  // { dg-message "shadowed declaration" }
-   int i;                      // { dg-warning "shadows a lambda capture" }
+   int j = i;                  // { dg-message "previously declared here" }
+   int i;                      // { dg-error "shadows a parameter" }
    i = 1;
  };
 }
@@ -42,8 +42,8 @@ void f3(int i) {
 template <class T>
 void f4(int i) {
  [=]{
-   int j = i;                  // { dg-message "shadowed declaration" }
-   int i;                      // { dg-warning "shadows a " }
+   int j = i;                  // { dg-message "previously declared here" }
+   int i;                      // { dg-error "shadows a parameter" }
    i = 1;
  };
 }

base-commit: f555ee597ac4fa522da798e9c6a966903a5b7113
-- 
2.50.0

Reply via email to