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