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?

-- >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
-- 
2.54.0

Reply via email to