https://gcc.gnu.org/g:d063d7dd7d460ccc426579cc792013fe657f7b89
commit r16-8504-gd063d7dd7d460ccc426579cc792013fe657f7b89 Author: Patrick Palka <[email protected]> Date: Tue Apr 7 12:28:44 2026 -0400 c++: template-id typedef with lambda targ [PR123700] A typedef to a template-id with a lambda targ must not be stripped by strip_typedefs because it can cause the lambda to leak into a deeper template context, which breaks substituting into the lambda later. For example in the below testcases zero<inner> gets stripped to zero<Type<[](auto) {}>>, and we end up substituting through this lambda targ twice (with args {double} then {int}) even though the lambda is from a depth-one template context. This patch extends the existing lambda typedef handling in dependent_opaque_alias_p (added by r15-3694 for decltype(lambda)) to also recognize such template-id typedefs. This should recognize the vast majority of such constructs in practice (though the lambda could also be a subexpression within a template argument which this won't recognize). PR c++/123700 gcc/cp/ChangeLog: * pt.cc (dependent_opaque_alias_p): Return true for a template-id typedef with a lambda targ. gcc/testsuite/ChangeLog: * g++.dg/cpp2a/lambda-targ24.C: New test. * g++.dg/cpp2a/lambda-targ24a.C: New test. Reviewed-by: Jason Merrill <[email protected]> Diff: --- gcc/cp/pt.cc | 20 +++++++++++++++++++- gcc/testsuite/g++.dg/cpp2a/lambda-targ24.C | 12 ++++++++++++ gcc/testsuite/g++.dg/cpp2a/lambda-targ24a.C | 14 ++++++++++++++ 3 files changed, 45 insertions(+), 1 deletion(-) diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc index 1aa86ee5c66c..70418e05f915 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -6872,6 +6872,14 @@ dependent_alias_template_spec_p (const_tree t, bool transparent_typedefs) bool dependent_opaque_alias_p (const_tree t) { + auto any_lambda_targ_p = [] (tree args) + { + for (tree arg : tree_vec_range (args)) + if (TREE_CODE (arg) == LAMBDA_EXPR) + return true; + return false; + }; + return (TYPE_P (t) && typedef_variant_p (t) && (any_dependent_type_attributes_p (DECL_ATTRIBUTES @@ -6882,7 +6890,17 @@ dependent_opaque_alias_p (const_tree t) alias would incorrectly yield a distinct lambda type. */ || (TREE_CODE (t) == DECLTYPE_TYPE && TREE_CODE (DECLTYPE_TYPE_EXPR (t)) == LAMBDA_EXPR - && !typedef_variant_p (DECL_ORIGINAL_TYPE (TYPE_NAME (t)))))); + && !typedef_variant_p (DECL_ORIGINAL_TYPE (TYPE_NAME (t)))) + /* Also treat an alias to A<lambda> as opaque so that it doesn't + "leak" into a deeper template context which would cause us to + over substitute into the lambda. */ + /* FIXME These lambda checks don't recognize deeply nested lambda + subexpressions, and we can't use walk_tree here because it's + slow. Maybe a tree flag indicating typedef opaqueness? */ + || (TYPE_TEMPLATE_INFO (t) + && PRIMARY_TEMPLATE_P (TYPE_TI_TEMPLATE (t)) + && any_lambda_targ_p (INNERMOST_TEMPLATE_ARGS + (TYPE_TI_ARGS (t)))))); } /* Return the number of innermost template parameters in TMPL. */ diff --git a/gcc/testsuite/g++.dg/cpp2a/lambda-targ24.C b/gcc/testsuite/g++.dg/cpp2a/lambda-targ24.C new file mode 100644 index 000000000000..6b071ca2ada9 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/lambda-targ24.C @@ -0,0 +1,12 @@ +// PR c++/123700 +// { dg-do compile { target c++20 } } + +template<class> constexpr int zero = 0; +template<auto> struct Type; + +int outer(auto) { + using inner = Type<[](auto) {}>; + return [](auto) { return zero<inner>; }(0); +} + +int main() { outer(0.0); } diff --git a/gcc/testsuite/g++.dg/cpp2a/lambda-targ24a.C b/gcc/testsuite/g++.dg/cpp2a/lambda-targ24a.C new file mode 100644 index 000000000000..0f883bd9f1a4 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/lambda-targ24a.C @@ -0,0 +1,14 @@ +// PR c++/123700 +// { dg-do compile { target c++20 } } +// A version of lambda-targ24.C where zero is a class instead of variable +// template. + +template<class> struct zero { }; +template<auto> struct Type; + +auto outer(auto) { + using inner = Type<[](auto) {}>; + return [](auto) { return zero<inner>{}; }(0); +} + +int main() { outer(0.0); }
