From a3a4f6be0c7ac1536c4d1def14217840b04dd488 Mon Sep 17 00:00:00 2001 From: Patrick Palka Date: Mon, 20 Apr 2020 18:34:00 -0400 Subject: [PATCH] c++: Recursive unification with packs and explicit targs [PR94628] This PR seems to be similar to PR c++/43382, except that the recursive call to the variadic function with trailing return type in this testcase is additionally given some explicit template arguments. In the first testcase below, when resolving the recursive call to 'select', fn_type_unification first substitutes in the call's explicit template arguments before doing unification, and so during this substitution the template argument pack for Args is incomplete. Since the pack is incomplete, the substitution of 'args...' in the trailing return type decltype(f(args...)) is handled by the unsubstituted_packs case of tsubst_pack_expansion. But the handling of this case happens _before_ we reset local_specializations, and so the substitution ends up reusing the old binding for 'args' from local_specializations rather than building a new one. This patch fixes this issue by setting up local_specializations sooner in tsubst_pack_expansion, before the handling of the unsubstituted_packs case. It also adds a new policy to local_specialization_stack so that we could use the class here to conditionally replace local_specializations. gcc/cp/ChangeLog: PR c++/94628 * cp-tree.h (lss_policy::lss_nop): New enumerator. * pt.c (local_specialization_stack::local_specialization_stack): Handle an lss_nop policy. (local_specialization_stack::~local_specialization_stack): Likewise. (tsubst_pack_expansion): Use a local_specialization_stack instead of manually saving and restoring local_specializations. Conditionally replace local_specializations sooner, before the handling of the unsubstituted_packs case. gcc/testsuite/ChangeLog: PR c++/94628 * g++.dg/cpp0x/variadic179.C: New test. * g++.dg/cpp0x/variadic180.C: New test. --- gcc/cp/ChangeLog | 12 ++++++++++ gcc/cp/cp-tree.h | 2 +- gcc/cp/pt.c | 38 ++++++++++++++------------------ gcc/testsuite/ChangeLog | 6 +++++ gcc/testsuite/g++.dg/cpp0x/variadic179.C | 16 ++++++++++++++ gcc/testsuite/g++.dg/cpp0x/variadic180.C | 25 +++++++++++++++++++++ 6 files changed, 77 insertions(+), 22 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp0x/variadic179.C create mode 100644 gcc/testsuite/g++.dg/cpp0x/variadic180.C diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 52f199d..797984b 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,15 @@ +2020-04-20 Patrick Palka + + PR c++/94628 + * cp-tree.h (lss_policy::lss_nop): New enumerator. + * pt.c (local_specialization_stack::local_specialization_stack): Handle + an lss_nop policy. + (local_specialization_stack::~local_specialization_stack): Likewise. + (tsubst_pack_expansion): Use a local_specialization_stack instead of + manually saving and restoring local_specializations. Conditionally + replace local_specializations sooner, before the handling of the + unsubstituted_packs case. + 2020-04-20 Marek Polacek PR c++/94505 - bogus -Wparentheses warning with fold-expression. diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 63aaf61..0b62a77 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -5421,7 +5421,7 @@ enum unification_kind_t { // An RAII class used to create a new pointer map for local // specializations. When the stack goes out of scope, the // previous pointer map is restored. -enum lss_policy { lss_blank, lss_copy }; +enum lss_policy { lss_blank, lss_copy, lss_nop }; class local_specialization_stack { public: diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 899df9a..cd6392a 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -83,7 +83,9 @@ static tree cur_stmt_expr; local_specialization_stack::local_specialization_stack (lss_policy policy) : saved (local_specializations) { - if (policy == lss_blank || !saved) + if (policy == lss_nop) + ; + else if (policy == lss_blank || !saved) local_specializations = new hash_map; else local_specializations = new hash_map(*saved); @@ -91,8 +93,11 @@ local_specialization_stack::local_specialization_stack (lss_policy policy) local_specialization_stack::~local_specialization_stack () { - delete local_specializations; - local_specializations = saved; + if (local_specializations != saved) + { + delete local_specializations; + local_specializations = saved; + } } /* True if we've recursed into fn_type_unification too many times. */ @@ -12718,7 +12723,6 @@ tsubst_pack_expansion (tree t, tree args, tsubst_flags_t complain, bool unsubstituted_fn_pack = false; int i, len = -1; tree result; - hash_map *saved_local_specializations = NULL; bool need_local_specializations = false; int levels; @@ -12917,7 +12921,15 @@ tsubst_pack_expansion (tree t, tree args, tsubst_flags_t complain, = build_extra_args (pattern, args, complain); return t; } - else if (unsubstituted_packs) + + /* If NEED_LOCAL_SPECIALIZATIONS then we're in a late-specified return + type, so create our own local specializations map; the current map is + either NULL or (in the case of recursive unification) might have + bindings that we don't want to use or alter. */ + local_specialization_stack lss (need_local_specializations + ? lss_blank : lss_nop); + + if (unsubstituted_packs) { /* There were no real arguments, we're just replacing a parameter pack with another version of itself. Substitute into the @@ -12934,16 +12946,6 @@ tsubst_pack_expansion (tree t, tree args, tsubst_flags_t complain, gcc_assert (len >= 0); - if (need_local_specializations) - { - /* We're in a late-specified return type, so create our own local - specializations map; the current map is either NULL or (in the - case of recursive unification) might have bindings that we don't - want to use or alter. */ - saved_local_specializations = local_specializations; - local_specializations = new hash_map; - } - /* For each argument in each argument pack, substitute into the pattern. */ result = make_tree_vec (len); @@ -12990,12 +12992,6 @@ tsubst_pack_expansion (tree t, tree args, tsubst_flags_t complain, } } - if (need_local_specializations) - { - delete local_specializations; - local_specializations = saved_local_specializations; - } - /* If the dependent pack arguments were such that we end up with only a single pack expansion again, there's no need to keep it in a TREE_VEC. */ if (len == 1 && TREE_CODE (result) == TREE_VEC diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 33f008b..813f94c 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,9 @@ +2020-04-20 Patrick Palka + + PR c++/94628 + * g++.dg/cpp0x/variadic179.C: New test. + * g++.dg/cpp0x/variadic180.C: New test. + 2020-04-20 Marek Polacek PR c++/94505 - bogus -Wparentheses warning with fold-expression. diff --git a/gcc/testsuite/g++.dg/cpp0x/variadic179.C b/gcc/testsuite/g++.dg/cpp0x/variadic179.C new file mode 100644 index 0000000..f04d3f7 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/variadic179.C @@ -0,0 +1,16 @@ +// PR c++/94628 +// { dg-do compile { target c++11 } } + +int f(int, int); +int f(int); + +template +auto select(Args... args) -> decltype(f(args...)) +{ + if (sizeof...(Args) > 1) + return select(7); + else + return 0; +} + +int a = select(0, 1); diff --git a/gcc/testsuite/g++.dg/cpp0x/variadic180.C b/gcc/testsuite/g++.dg/cpp0x/variadic180.C new file mode 100644 index 0000000..0dd0f88 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/variadic180.C @@ -0,0 +1,25 @@ +// PR c++/94628 +// A variant of variadic101.C where the recursive call to deref +// has its first template argument explicitly provided. +// { dg-do compile { target c++11 } } + +template +struct Container +{ T f() const; }; + +template +T deref(const T& t) +{ return t; } + + +template +auto +deref(const T& u, int r, Args... args) +-> decltype(deref(u.f(), args...)) +{ return deref(u.f(), args...); } + +int main(void) +{ + Container> v; + deref(v,1,2); +} -- 2.7.4