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