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); }

Reply via email to