On 6/9/26 12:45 PM, Marek Polacek wrote:
On Wed, May 20, 2026 at 02:07:55PM -0400, Jason Merrill wrote:
On 5/20/26 12:38 PM, Marek Polacek wrote:
Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk/16.2?

-- >8 --
This patch extends the 123700 fix to detect more cases:
a TYPENAME_TYPE with a LAMBDA_EXPR in it, and a DECLTYPE_TYPE
whose operand is a CALL_EXPR whose CALL_EXPR_FN is a LAMBDA_EXPR.

It still doesn't completely fix the problem: when the LAMBDA_EXPR
is more nested as e.g. in 110961:

using Typ = std::conditional_t<detail::ExtentsLike<Lst>, decltype([]<auto... 
Idx>(std::index_sequence<Idx...>){}(std::make_index_sequence<sizeof...(Ts)-1>{})), Tup>;

we won't find it since we don't and can't use walk_tree.  But this
patch is pretty simple and fixes (at least) 4 PRs.

Yes, but how long are we going to keep adding more special cases until we
decide to fix the general problem?  You already found another PR that isn't
fixed by this new special case.

I assume the slowness problem is from rechecking every time we want to use
the type; let's move toward setting a flag on the TYPE_DECL as the comment
suggests.

Fair enough.  This patch adds such a flag.

Bootstrapped/regtested on x86_64-pc-linux-gnu and ppc64le-pc-linux-gnu,
ok for trunk?

OK, thanks.

-- >8 --
This patch introduces a new TYPE_DECL flag so that dependent_opaque_alias_p
doesn't have to peruse the tree every time it's called to see if there is
a LAMBDA_EXPR, in which case the alias should be opaque.  Using walk_tree
in dependent_opaque_alias_p was too expensive, and the special cases we
had there were fooled by more deeply nested lambdas.

Compiling range-v3's zip.cpp, perf stat shows:
this patch: 28.826446158 seconds time elapsed
trunk:      29.425441889 seconds time elapsed
so this patch makes the situation marginally better.

        PR c++/121287
        PR c++/125212
        PR c++/105667
        PR c++/121597
        PR c++/110961

gcc/cp/ChangeLog:

        * cp-tree.h (TYPE_DECL_OPAQUE_ALIAS_P): Define.
        (any_lambdas_p): Declare.
        * decl.cc (grokdeclarator): Set TYPE_DECL_OPAQUE_ALIAS_P.
        * pt.cc (dependent_opaque_alias_p): Refine to check
        TYPE_DECL_OPAQUE_ALIAS_P.
        (tsubst_decl) <case TYPE_DECL>: Set TYPE_DECL_OPAQUE_ALIAS_P.
        (any_lambdas_p): New, factored out of...
        (regenerate_decl_from_template): ...this.  Call it.

gcc/testsuite/ChangeLog:

        * g++.dg/cpp26/lambda-targ1.C: New test.
        * g++.dg/cpp2a/lambda-targ26.C: New test.
        * g++.dg/cpp2a/lambda-targ27.C: New test.
        * g++.dg/cpp2a/lambda-targ28.C: New test.
        * g++.dg/cpp2a/lambda-targ29.C: New test.
        * g++.dg/cpp2a/lambda-targ30.C: New test.
        * g++.dg/cpp2a/lambda-targ31.C: New test.
        * g++.dg/cpp2a/lambda-targ32.C: New test.
---
  gcc/cp/cp-tree.h                           |  8 +++
  gcc/cp/decl.cc                             |  4 ++
  gcc/cp/pt.cc                               | 65 ++++++++++------------
  gcc/testsuite/g++.dg/cpp26/lambda-targ1.C  | 12 ++++
  gcc/testsuite/g++.dg/cpp2a/lambda-targ26.C | 32 +++++++++++
  gcc/testsuite/g++.dg/cpp2a/lambda-targ27.C | 22 ++++++++
  gcc/testsuite/g++.dg/cpp2a/lambda-targ28.C | 16 ++++++
  gcc/testsuite/g++.dg/cpp2a/lambda-targ29.C | 12 ++++
  gcc/testsuite/g++.dg/cpp2a/lambda-targ30.C | 22 ++++++++
  gcc/testsuite/g++.dg/cpp2a/lambda-targ31.C | 21 +++++++
  gcc/testsuite/g++.dg/cpp2a/lambda-targ32.C | 21 +++++++
  11 files changed, 199 insertions(+), 36 deletions(-)
  create mode 100644 gcc/testsuite/g++.dg/cpp26/lambda-targ1.C
  create mode 100644 gcc/testsuite/g++.dg/cpp2a/lambda-targ26.C
  create mode 100644 gcc/testsuite/g++.dg/cpp2a/lambda-targ27.C
  create mode 100644 gcc/testsuite/g++.dg/cpp2a/lambda-targ28.C
  create mode 100644 gcc/testsuite/g++.dg/cpp2a/lambda-targ29.C
  create mode 100644 gcc/testsuite/g++.dg/cpp2a/lambda-targ30.C
  create mode 100644 gcc/testsuite/g++.dg/cpp2a/lambda-targ31.C
  create mode 100644 gcc/testsuite/g++.dg/cpp2a/lambda-targ32.C

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 8807d80a21a..6df271d5e35 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -461,6 +461,7 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX];
        SPLICE_EXPR_EXPRESSION_P (in SPLICE_EXPR)
        OLD_PARM_DECL_P (in PARM_DECL)
        COMPONENT_REF_SPLICE_P (in COMPONENT_REF)
+      TYPE_DECL_OPAQUE_ALIAS_P (in TYPE_DECL)
     1: IDENTIFIER_KIND_BIT_1 (in IDENTIFIER_NODE)
        TI_PENDING_TEMPLATE_FLAG.
        TEMPLATE_PARMS_FOR_INLINE.
@@ -4079,6 +4080,12 @@ struct GTY(()) lang_decl {
     && TYPE_DECL_FOR_LINKAGE_PURPOSES_P (NODE) \
     && DECL_IMPLICIT_TYPEDEF_P (NODE))
+/* Nonzero for TYPE_DECL means that it represents an opaque alias; that is,
+   there is a LAMBDA_EXPR involved in it.  This flag is used to implement
+   dependent_opaque_alias_p.  */
+#define TYPE_DECL_OPAQUE_ALIAS_P(NODE) \
+  TREE_LANG_FLAG_0 (TYPE_DECL_CHECK (NODE))
+
  /* If non-NULL for a VAR_DECL, FUNCTION_DECL, TYPE_DECL, TEMPLATE_DECL,
     or CONCEPT_DECL, the entity is either a template specialization (if
     DECL_USE_TEMPLATE is nonzero) or the abstract instance of the
@@ -8314,6 +8321,7 @@ extern bool any_value_dependent_elements_p      
(const_tree);
  extern bool dependent_template_arg_p          (tree);
  extern bool dependent_omp_for_p                       (tree, tree, tree, 
tree, tree);
  extern tree resolve_typename_type             (tree, bool);
+extern bool any_lambdas_p                      (tree);
  extern tree template_for_substitution         (tree);
  extern bool reregister_specialization         (tree, tree, tree);
  extern tree instantiate_non_dependent_expr    (tree, tsubst_flags_t = 
tf_error);
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index bec89effd4f..173554c1a30 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -16159,6 +16159,10 @@ grokdeclarator (const cp_declarator *declarator,
             `using analias = atype;'.  */
        TYPE_DECL_ALIAS_P (decl) = 1;
+ /* We use a flag so that dependent_opaque_alias_p doesn't have to
+        recompute the answer every single time.  */
+      TYPE_DECL_OPAQUE_ALIAS_P (decl) = any_lambdas_p (type);
+
        return decl;
      }
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index 891e89f1d76..535c7574724 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -6924,35 +6924,18 @@ 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
-                                              (TYPE_NAME (t)))
-             /* Treat a dependent decltype(lambda) alias as opaque so that we
-                don't prematurely strip it when used as a template argument.
-                Otherwise substitution into each occurrence of the (stripped)
-                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))))
-             /* 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))))));
+         /* Treat a dependent decltype(lambda) alias as opaque so that we
+            don't prematurely strip it when used as a template argument.
+            Otherwise substitution into each occurrence of the (stripped)
+            alias would incorrectly yield a distinct lambda type.  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.  */
+         && (TYPE_DECL_OPAQUE_ALIAS_P (TYPE_NAME (t))
+             || any_dependent_type_attributes_p
+                 (DECL_ATTRIBUTES (TYPE_NAME (t)))));
  }
/* Return the number of innermost template parameters in TMPL. */
@@ -16483,6 +16466,9 @@ tsubst_decl (tree t, tree args, tsubst_flags_t complain,
        /* Preserve a typedef that names a type.  */
        if (is_typedef_decl (r) && type != error_mark_node)
          {
+           /* Now that we've substituted the type, it may not be opaque
+              anymore.  */
+           TYPE_DECL_OPAQUE_ALIAS_P (r) = any_lambdas_p (type);
            DECL_ORIGINAL_TYPE (r) = NULL_TREE;
            set_underlying_type (r);
@@ -28325,6 +28311,21 @@ do_type_instantiation (tree t, tree storage, tsubst_flags_t complain)
        }
  }
+/* Return true if T contains any LAMBDA_EXPRs. */
+
+bool
+any_lambdas_p (tree t)
+{
+  walk_tree_fn find_lambda = [](tree *tp, int *, void *)
+    {
+      if (TREE_CODE (*tp) == LAMBDA_EXPR)
+       return *tp;
+      return NULL_TREE;
+    };
+
+  return !!cp_walk_tree_without_duplicates (&t, find_lambda, nullptr);
+}
+
  /* Given a function DECL, which is a specialization of TMPL, modify
     DECL to be a re-instantiation of TMPL with the same template
     arguments.  TMPL should be the template into which tsubst'ing
@@ -28368,15 +28369,7 @@ regenerate_decl_from_template (tree decl, tree tmpl, 
tree args)
/* A template with a lambda in the signature also changes type if
         regenerated (PR119401).  */
-      walk_tree_fn find_lambda
-       = [](tree *tp, int *, void *)
-       {
-         if (TREE_CODE (*tp) == LAMBDA_EXPR)
-           return *tp;
-         return NULL_TREE;
-       };
-      if (cp_walk_tree_without_duplicates
-         (&TREE_TYPE (tmpl), find_lambda, nullptr))
+      if (any_lambdas_p (TREE_TYPE (tmpl)))
        goto done;
/* Use the source location of the definition. */
diff --git a/gcc/testsuite/g++.dg/cpp26/lambda-targ1.C 
b/gcc/testsuite/g++.dg/cpp26/lambda-targ1.C
new file mode 100644
index 00000000000..8e12766e27c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp26/lambda-targ1.C
@@ -0,0 +1,12 @@
+// PR c++/121287
+// { dg-do compile { target c++26 } }
+// { dg-additional-options "-freflection" }
+
+template <typename _Iter>
+concept random_access_iterator = requires(_Iter __n) { __n; };
+template <typename> struct default_accessor {};
+template <random_access_iterator I> struct iterator_accessor {
+  using element_type = [:[] { return ^^I; }():];
+  operator default_accessor<const element_type>();
+};
+default_accessor<const int> _ = iterator_accessor<int>();
diff --git a/gcc/testsuite/g++.dg/cpp2a/lambda-targ26.C 
b/gcc/testsuite/g++.dg/cpp2a/lambda-targ26.C
new file mode 100644
index 00000000000..ccea0a83b75
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/lambda-targ26.C
@@ -0,0 +1,32 @@
+// PR c++/125212
+// { dg-do compile { target c++20 } }
+
+template <auto dummyVal>
+struct MyStruct
+{
+    template<class Callable>
+    static constexpr auto Invoke(Callable callable)
+    {
+        return callable.template operator() <decltype(val)> ();
+    }
+
+    static constexpr int val = 0;
+
+    template <auto callable>
+    using Alias = MyStruct<dummyVal>;
+};
+
+template <typename T>
+constexpr auto IceFunction()
+{
+    using Alias1 = T::template Alias<[]<typename>() {}>;
+    constexpr auto lambda = []<typename>() {};
+    using Alias2 = T::template Alias<lambda>;
+    using Alias = Alias1;
+    return Alias::Invoke([]<typename>{ return MyStruct<Alias::val>{}; });
+}
+
+int main()
+{
+    IceFunction<MyStruct<1>>();
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/lambda-targ27.C 
b/gcc/testsuite/g++.dg/cpp2a/lambda-targ27.C
new file mode 100644
index 00000000000..29a2e874e5d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/lambda-targ27.C
@@ -0,0 +1,22 @@
+// PR c++/105667
+// { dg-do compile { target c++20 } }
+
+template<auto f>
+struct h {typedef int type;};
+
+template<class Types>
+struct t{};
+
+template<int Ts>
+struct crash {
+    using Types = typename h<[]() {}>::type;
+    template<int tt>
+    static void f()
+    {
+        t<Types> b;
+    };
+};
+
+int main() {
+    crash<0>::f<1>();
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/lambda-targ28.C 
b/gcc/testsuite/g++.dg/cpp2a/lambda-targ28.C
new file mode 100644
index 00000000000..9a381e39834
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/lambda-targ28.C
@@ -0,0 +1,16 @@
+// PR c++/105667
+// { dg-do compile { target c++20 } }
+
+template<typename>
+struct get { };
+
+template<int>
+struct thing {
+    using T = decltype([](auto) { });
+
+    static constexpr auto value = [](auto) {
+        return get<T>();
+    }(0);
+};
+
+thing<0> X;
diff --git a/gcc/testsuite/g++.dg/cpp2a/lambda-targ29.C 
b/gcc/testsuite/g++.dg/cpp2a/lambda-targ29.C
new file mode 100644
index 00000000000..8a781f1534f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/lambda-targ29.C
@@ -0,0 +1,12 @@
+// PR c++/105667
+// { dg-do compile { target c++20 } }
+
+template <auto A = []<class B>(B){}>
+struct C {
+  using D = void;
+};
+
+template <class...>
+using E = C<>::D;
+
+using F = E<>;
diff --git a/gcc/testsuite/g++.dg/cpp2a/lambda-targ30.C 
b/gcc/testsuite/g++.dg/cpp2a/lambda-targ30.C
new file mode 100644
index 00000000000..d89d0cac3f0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/lambda-targ30.C
@@ -0,0 +1,22 @@
+// PR c++/105667
+// { dg-do compile { target c++20 } }
+
+struct class1
+{
+       virtual void a_function() = 0;
+};
+
+template<auto my_lambda = []<typename T>() {}>
+class class2 {};
+
+template<typename Touter>
+struct class3 : public class1 {
+       void a_function()
+       {
+               class2<> x;
+       }
+};
+
+struct class4 : public class3<class4> {
+       class4() {}
+};
diff --git a/gcc/testsuite/g++.dg/cpp2a/lambda-targ31.C 
b/gcc/testsuite/g++.dg/cpp2a/lambda-targ31.C
new file mode 100644
index 00000000000..b61bec29be0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/lambda-targ31.C
@@ -0,0 +1,21 @@
+// PR c++/121597
+// { dg-do compile { target c++20 } }
+
+template<int, class> struct Tmpl {};
+template<typename T> struct Wrap {};
+
+template<typename = void>
+void foo()
+{
+  using Type = decltype([]<typename = void>{
+    return Tmpl<0, char>{};
+  }());
+
+  []<typename = void>{
+    using X = decltype([]<auto N, class T>(Tmpl<N, T>&&)
+      {}.template operator()<0>(Type{}));
+    return Wrap<X>{};
+  }();
+};
+
+template void foo<>();
diff --git a/gcc/testsuite/g++.dg/cpp2a/lambda-targ32.C 
b/gcc/testsuite/g++.dg/cpp2a/lambda-targ32.C
new file mode 100644
index 00000000000..8a46faeefb2
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/lambda-targ32.C
@@ -0,0 +1,21 @@
+// PR c++/121287
+// { dg-do compile { target c++20 } }
+
+template <typename _Tp, _Tp...> struct integer_sequence {};
+template <long... _Idx>
+using index_sequence = integer_sequence<unsigned long, _Idx...>;
+template <typename> struct array {};
+
+template <typename = void>
+auto
+f ()
+{
+  using Res = decltype([]<unsigned long Idx>(index_sequence<Idx>) { 
}(index_sequence<3>{}));
+  return []<long...>(index_sequence<>) { array<Res>{}; }({});
+}
+
+void
+g ()
+{
+  f ();
+}

base-commit: 2415f291466dc93182b64e6f069dd0015862e70f

Reply via email to