https://gcc.gnu.org/g:811cc466193d7efcfcc07d2081b1284d97fc91a1
commit r17-1455-g811cc466193d7efcfcc07d2081b1284d97fc91a1 Author: Marek Polacek <[email protected]> Date: Tue May 19 19:03:33 2026 -0400 c++: recognize more aliases with lambda targs [PR125212] 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. Reviewed-by: Jason Merrill <[email protected]> Diff: --- 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(-) diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 8807d80a21a3..6df271d5e359 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 bec89effd4f9..173554c1a307 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 891e89f1d763..535c75747242 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 000000000000..8e12766e27cb --- /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 000000000000..ccea0a83b75d --- /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 000000000000..29a2e874e5d3 --- /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 000000000000..9a381e398346 --- /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 000000000000..8a781f1534f1 --- /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 000000000000..d89d0cac3f03 --- /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 000000000000..b61bec29be07 --- /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 000000000000..8a46faeefb25 --- /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 (); +}
