https://gcc.gnu.org/g:94438ca82792063abf05823326695af25ab02d17
commit r15-9325-g94438ca82792063abf05823326695af25ab02d17 Author: Jason Merrill <ja...@redhat.com> Date: Tue Apr 8 15:53:34 2025 -0400 c++: lambda in concept [PR118698] When normalizing is_foo for <T>, we get to normalizing callable<decltype(...),T> for <T,foo>, which means substituting <T,foo> into <decltype(...),T>. Since r14-9938, because in_template_context is false we return the lambda unchanged, just with LAMBDA_EXPR_EXTRA_ARGS set, so the closure type still refers to the is_specialization_of tparms in its CLASSTYPE_TEMPLATE_INFO. So then in normalize_atom caching find_template_parameters walks over the parameter mapping; any_template_parm_r walks into the TREE_TYPE of a LAMBDA_EXPR without considering EXTRA_ARGS and finds a template parm from the wrong parameter list. But since r15-3530 we expect to set tf_partial when substituting with dependent arguments, so we should set that when normalizing. And then tf_partial causes TREE_STATIC to be set on the EXTRA_ARGS, meaning that those args will replace all the template parms in the rest of the lambda, so we can walk just the EXTRA_ARGS and ignore the rest. PR c++/118698 gcc/cp/ChangeLog: * constraint.cc (struct norm_info): Add tf_partial. * pt.cc (any_template_parm_r): Handle LAMBDA_EXPR_EXTRA_ARGS. gcc/testsuite/ChangeLog: * g++.dg/cpp2a/concepts-lambda22.C: New test. Diff: --- gcc/cp/constraint.cc | 2 +- gcc/cp/pt.cc | 12 ++++++++++++ gcc/testsuite/g++.dg/cpp2a/concepts-lambda22.C | 21 +++++++++++++++++++++ 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc index a9caba8e2cc7..2f1678ce4ff9 100644 --- a/gcc/cp/constraint.cc +++ b/gcc/cp/constraint.cc @@ -354,7 +354,7 @@ struct norm_info : subst_info /* Construct a top-level context for DECL. */ norm_info (tree in_decl, bool diag) - : subst_info (tf_warning_or_error, in_decl), + : subst_info (tf_warning_or_error|tf_partial, in_decl), generate_diagnostics (diag) { if (in_decl) diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc index 8f35fa702a21..0e120c4040ed 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -11117,6 +11117,18 @@ any_template_parm_r (tree t, void *data) case LAMBDA_EXPR: { + /* TREE_STATIC on LAMBDA_EXPR_EXTRA_ARGS means a full set of + arguments, so we can just look there; they will replace + any template parms in the rest of the LAMBDA_EXPR. */ + if (tree args = LAMBDA_EXPR_EXTRA_ARGS (t)) + { + WALK_SUBTREE (args); + /* Without TREE_STATIC the args are just outer levels, so we'd + still need to look through the lambda for just inner + parameters. Hopefully that's not necessary. */ + gcc_checking_assert (TREE_STATIC (args)); + return 0; + } /* Look in the parms and body. */ tree fn = lambda_function (t); WALK_SUBTREE (TREE_TYPE (fn)); diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-lambda22.C b/gcc/testsuite/g++.dg/cpp2a/concepts-lambda22.C new file mode 100644 index 000000000000..2437b7e06a94 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-lambda22.C @@ -0,0 +1,21 @@ +// PR c++/118698 +// { dg-do compile { target c++20 } } + +template <typename T> struct foo {}; +template <typename T> struct bar {}; + +template <class T> T&& declval (); + +template <typename T, typename U> +concept callable = requires { declval<T>()(declval<U>()); }; + +template <typename T, template <typename...> typename U> +concept is_specialization_of = callable<decltype([]<typename... Args>( U<Args...> const& ) { }),T>; + +static_assert( is_specialization_of<foo<int>,foo> == true ); +static_assert( is_specialization_of<foo<int>,bar> == false ); + +template <typename T> concept is_foo = is_specialization_of<T,foo>; + +static_assert( is_foo<foo<int>> ); +static_assert( is_foo<bar<int>> == false );