When gcc checks the constraint { t.x } -> M<[]<A>(){}> and substitutes
its template parameters, generic lambda []<A>(){} gets more levels than
its enclosing templates have.  tsubst_template_decl then ICEs in
template_parms_to_args.

Drop the extra levels before substituting, like do_class_deduction
already does for CTAD, and move that code into a shared helper.

        PR c++/102047

gcc/cp/ChangeLog:

        * pt.cc (outer_template_args_for_substitution): New helper to discard
        argument levels beyond the enclosing template context of a template.
        (tsubst_lambda_expr): Call it for generic lambdas after adding
        deferred arguments.
        (do_class_deduction): Use the helper.

gcc/testsuite/ChangeLog:

        * g++.dg/cpp2a/concepts-lambda-pr102047.C: New test.

Signed-off-by: Mishmish Dev <[email protected]>
---
Bootstrapped and regtested on x86_64-pc-linux-gnu (Debian 13), with
--enable-checking=yes.  The new test passes in C++20/23/26.  No failures
in libstdc++.  There were some unrelated failures during my testing (guality,
a few i386 and vectorizer scan tests, and an AVX512 execution test on a CPU
without AVX512), which also failed identically on pristine trunk.

OK for trunk and backports up to gcc-14?

 gcc/cp/pt.cc                                  | 26 ++++++++++++++++---
 .../g++.dg/cpp2a/concepts-lambda-pr102047.C   | 20 ++++++++++++++
 2 files changed, 42 insertions(+), 4 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-lambda-pr102047.C

diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index 26bc7c21db3..0711306b846 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -5251,6 +5251,22 @@ outer_template_args (const_tree decl)
   return strip_innermost_template_args (args, 1);
 }
 
+/* Return the arguments from ARGS for TMPL's enclosing templates, leaving
+   out parameters for later substitution.  */
+
+static tree
+outer_template_args_for_substitution (tree tmpl, tree args)
+{
+  int args_depth = TMPL_ARGS_DEPTH (args);
+  int parms_depth = TMPL_PARMS_DEPTH (DECL_TEMPLATE_PARMS (tmpl));
+  int want = std::min (args_depth, parms_depth - 1);
+  /* CTAD's caller excludes want == 0; a generic lambda can have no
+     enclosing template arguments.  */
+  return want
+        ? strip_innermost_template_args (args, args_depth - want)
+        : NULL_TREE;
+}
+
 /* Update the declared TYPE by doing any lookups which were thought to be
    dependent, but are not now that we know the SCOPE of the declarator.  */
 
@@ -21251,6 +21267,10 @@ tsubst_lambda_expr (tree t, tree args, tsubst_flags_t 
complain, tree in_decl)
   in_decl = oldfn;
 
   args = add_extra_args (LAMBDA_EXPR_EXTRA_ARGS (t), args, complain, in_decl);
+  if (generic_lambda_fn_p (oldfn))
+    args = outer_template_args_for_substitution (DECL_TI_TEMPLATE (oldfn),
+                                                args);
+
   if (processing_template_decl
       && (!in_template_context || (complain & tf_partial)
          || LAMBDA_EXPR_EXTRA_ARGS (t)))
@@ -32885,15 +32905,13 @@ do_class_deduction (tree ptype, tree tmpl, tree init, 
tree outer_targs,
 
   if (outer_targs)
     {
-      int args_depth = TMPL_ARGS_DEPTH (outer_targs);
       int parms_depth = TMPL_PARMS_DEPTH (DECL_TEMPLATE_PARMS (tmpl));
       if (parms_depth > 1)
        {
          /* Substitute outer arguments into this CTAD template from the
             current instantiation.  */
-         int want = std::min (args_depth, parms_depth - 1);
-         outer_targs = strip_innermost_template_args (outer_targs,
-                                                      args_depth - want);
+         outer_targs = outer_template_args_for_substitution (tmpl,
+                                                             outer_targs);
          tmpl = tsubst (tmpl, outer_targs, complain, NULL_TREE);
          if (tmpl == error_mark_node)
            return error_mark_node;
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-lambda-pr102047.C 
b/gcc/testsuite/g++.dg/cpp2a/concepts-lambda-pr102047.C
new file mode 100644
index 00000000000..b49944157b6
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-lambda-pr102047.C
@@ -0,0 +1,20 @@
+// PR c++/102047
+// { dg-do compile { target c++20 } }
+
+template<class T, auto QuotedConcept>
+concept constrained_by = requires {
+  QuotedConcept.template operator()<T>();
+};
+
+template<class T, class U> concept same_as = __is_same(T, U);
+
+template<class T, class U>
+concept reads = requires(T t) {
+  // we can pass around constraints in "shark quotes" - by placing them
+  // into a generic lambda template parameter and requires clause
+  { t.read() } -> constrained_by<[]<same_as<U>>{}>;
+};
+
+struct Reader { int read(); };
+static_assert(reads<Reader, int>);
+static_assert(!reads<Reader, double>);

base-commit: cd7a85774a992c4759ebd03a26ce03cbb934e4d9
-- 
2.55.0

Reply via email to