]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c++: template-id typedef with lambda targ [PR123700]
authorPatrick Palka <ppalka@redhat.com>
Tue, 7 Apr 2026 16:28:44 +0000 (12:28 -0400)
committerPatrick Palka <ppalka@redhat.com>
Tue, 7 Apr 2026 16:28:44 +0000 (12:28 -0400)
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 <jason@redhat.com>
gcc/cp/pt.cc
gcc/testsuite/g++.dg/cpp2a/lambda-targ24.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/lambda-targ24a.C [new file with mode: 0644]

index 1aa86ee5c66c5f0a529d5bec16586a5eab98e117..70418e05f9150476b66f3ac52e31bbfb7616d855 100644 (file)
@@ -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 (file)
index 0000000..6b071ca
--- /dev/null
@@ -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 (file)
index 0000000..0f883bd
--- /dev/null
@@ -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); }