c++: Recursive unification with packs and explicit targs [PR94628]
authorPatrick Palka <ppalka@redhat.com>
Mon, 20 Apr 2020 22:34:00 +0000 (18:34 -0400)
committerPatrick Palka <ppalka@redhat.com>
Mon, 20 Apr 2020 22:34:00 +0000 (18:34 -0400)
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
gcc/cp/cp-tree.h
gcc/cp/pt.c
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/cpp0x/variadic179.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp0x/variadic180.C [new file with mode: 0644]

index 52f199d90bf34c97e861f255cf1fca80c2d6551e..797984bcaa2922bd08921091409de9c33f40ef8a 100644 (file)
@@ -1,3 +1,15 @@
+2020-04-20  Patrick Palka  <ppalka@redhat.com>
+
+       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  <polacek@redhat.com>
 
        PR c++/94505 - bogus -Wparentheses warning with fold-expression.
index 63aaf615926799a956d1be3d62b39f80209381cf..0b62a775c1bf0dfc524b2682f5642a43f1cf772e 100644 (file)
@@ -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:
index 899df9a330cf67c63c42b3c2d2a57a448f3737cb..cd6392aca222a2f8c065933598faca87a04e431f 100644 (file)
@@ -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<tree, tree>;
   else
     local_specializations = new hash_map<tree, tree>(*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<tree, tree> *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<tree, tree>;
-    }
-
   /* 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
index 33f008b9af432b0773694cb9d31c76a744b41dd2..813f94c7de0b8b35c5cb2c92b03d5b3fe4ea1bbc 100644 (file)
@@ -1,3 +1,9 @@
+2020-04-20  Patrick Palka  <ppalka@redhat.com>
+
+       PR c++/94628
+       * g++.dg/cpp0x/variadic179.C: New test.
+       * g++.dg/cpp0x/variadic180.C: New test.
+
 2020-04-20  Marek Polacek  <polacek@redhat.com>
 
        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 (file)
index 0000000..f04d3f7
--- /dev/null
@@ -0,0 +1,16 @@
+// PR c++/94628
+// { dg-do compile { target c++11 } }
+
+int f(int, int);
+int f(int);
+
+template<class...Args>
+auto select(Args... args) -> decltype(f(args...))
+{
+  if (sizeof...(Args) > 1)
+    return select<char>(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 (file)
index 0000000..0dd0f88
--- /dev/null
@@ -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<class T>
+struct Container
+{ T f() const; };
+
+template<class T>
+T deref(const T& t)
+{ return t; }
+
+
+template <class T, class... Args>
+auto
+deref(const T& u, int r, Args... args)
+-> decltype(deref(u.f(), args...))
+{ return deref<decltype(u.f())>(u.f(), args...); }
+
+int main(void)
+{
+    Container<Container<int>> v;
+    deref(v,1,2);
+}