diff mbox series

c++: Recursive unification with packs and explicit targs [PR94628]

Message ID 20200419165524.2005506-1-ppalka@redhat.com
State New
Headers show
Series c++: Recursive unification with packs and explicit targs [PR94628] | expand

Commit Message

Patrick Palka April 19, 2020, 4:55 p.m. UTC
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.

Passes 'make check-c++', does this look OK to commit after bootstrap/regtesting?

gcc/cp/ChangeLog:

	PR c++/94628
	* cp-tree.h (lss_policy::lss_inherit): New enumerator.
	* pt.c (local_specialization_stack::local_specialization_stack): Handle
	an lss_inherit 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/cp-tree.h                         |  2 +-
 gcc/cp/pt.c                              | 38 +++++++++++-------------
 gcc/testsuite/g++.dg/cpp0x/variadic179.C | 16 ++++++++++
 gcc/testsuite/g++.dg/cpp0x/variadic180.C | 25 ++++++++++++++++
 4 files changed, 59 insertions(+), 22 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp0x/variadic179.C
 create mode 100644 gcc/testsuite/g++.dg/cpp0x/variadic180.C

Comments

Jason Merrill April 20, 2020, 7:31 p.m. UTC | #1
On 4/19/20 12:55 PM, Patrick Palka wrote:
> 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.
> 
> Passes 'make check-c++', does this look OK to commit after bootstrap/regtesting?
> 
> gcc/cp/ChangeLog:
> 
> 	PR c++/94628
> 	* cp-tree.h (lss_policy::lss_inherit): New enumerator.
> 	* pt.c (local_specialization_stack::local_specialization_stack): Handle
> 	an lss_inherit 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/cp-tree.h                         |  2 +-
>   gcc/cp/pt.c                              | 38 +++++++++++-------------
>   gcc/testsuite/g++.dg/cpp0x/variadic179.C | 16 ++++++++++
>   gcc/testsuite/g++.dg/cpp0x/variadic180.C | 25 ++++++++++++++++
>   4 files changed, 59 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/cp-tree.h b/gcc/cp/cp-tree.h
> index 000badc21ac..5b3b9507474 100644
> --- a/gcc/cp/cp-tree.h
> +++ b/gcc/cp/cp-tree.h
> @@ -5424,7 +5424,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_inherit };
>   class local_specialization_stack
>   {
>   public:
> diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
> index 9e39f46a090..9cea663514d 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_inherit)
> +    ;

Maybe assert that saved is non-null?

lss_inherit suggests to me that we could add things and then pop back to 
the previous state, which is the actual meaning of lss_copy.  Maybe 
lss_nop or lss_same instead?

OK with those changes.

> +  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.  */
> @@ -12694,7 +12699,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;
>   
> @@ -12893,7 +12897,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_inherit);
> +
> +  if (unsubstituted_packs)
>       {
>         /* There were no real arguments, we're just replacing a parameter
>   	 pack with another version of itself. Substitute into the
> @@ -12910,16 +12922,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);
> @@ -12966,12 +12968,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/g++.dg/cpp0x/variadic179.C b/gcc/testsuite/g++.dg/cpp0x/variadic179.C
> new file mode 100644
> index 00000000000..f04d3f753ca
> --- /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<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
> index 00000000000..e8fcdd0a64d
> --- /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 given.
> +// { 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);
> +}
>
Patrick Palka April 20, 2020, 9:08 p.m. UTC | #2
On Mon, 20 Apr 2020, Jason Merrill wrote:
> On 4/19/20 12:55 PM, Patrick Palka wrote:
> > 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.
> > 
> > Passes 'make check-c++', does this look OK to commit after
> > bootstrap/regtesting?
> > 
> > gcc/cp/ChangeLog:
> > 
> > 	PR c++/94628
> > 	* cp-tree.h (lss_policy::lss_inherit): New enumerator.
> > 	* pt.c (local_specialization_stack::local_specialization_stack):
> > Handle
> > 	an lss_inherit 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/cp-tree.h                         |  2 +-
> >   gcc/cp/pt.c                              | 38 +++++++++++-------------
> >   gcc/testsuite/g++.dg/cpp0x/variadic179.C | 16 ++++++++++
> >   gcc/testsuite/g++.dg/cpp0x/variadic180.C | 25 ++++++++++++++++
> >   4 files changed, 59 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/cp-tree.h b/gcc/cp/cp-tree.h
> > index 000badc21ac..5b3b9507474 100644
> > --- a/gcc/cp/cp-tree.h
> > +++ b/gcc/cp/cp-tree.h
> > @@ -5424,7 +5424,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_inherit };
> >   class local_specialization_stack
> >   {
> >   public:
> > diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
> > index 9e39f46a090..9cea663514d 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_inherit)
> > +    ;
> 
> Maybe assert that saved is non-null?

Hmm, it looks like adding an assert to that effect breaks substituting
into the TYPE_ARG_TYPES of a variadic function template, because
local_specializations is not set up until we finish substituting into
the decl and start instantiating the body.

> 
> lss_inherit suggests to me that we could add things and then pop back to the
> previous state, which is the actual meaning of lss_copy.  Maybe lss_nop or
> lss_same instead?

Sounds good, I will commit the patch with lss_inherit changed to
lss_nop shortly (and without the assert I suppose).

> 
> OK with those changes.
> 
> > +  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.  */
> > @@ -12694,7 +12699,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;
> >   @@ -12893,7 +12897,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_inherit);
> > +
> > +  if (unsubstituted_packs)
> >       {
> >         /* There were no real arguments, we're just replacing a parameter
> >   	 pack with another version of itself. Substitute into the
> > @@ -12910,16 +12922,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);
> > @@ -12966,12 +12968,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/g++.dg/cpp0x/variadic179.C
> > b/gcc/testsuite/g++.dg/cpp0x/variadic179.C
> > new file mode 100644
> > index 00000000000..f04d3f753ca
> > --- /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<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
> > index 00000000000..e8fcdd0a64d
> > --- /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 given.
> > +// { 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);
> > +}
> > 
> 
>
diff mbox series

Patch

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 000badc21ac..5b3b9507474 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5424,7 +5424,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_inherit };
 class local_specialization_stack
 {
 public:
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 9e39f46a090..9cea663514d 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_inherit)
+    ;
+  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.  */
@@ -12694,7 +12699,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;
 
@@ -12893,7 +12897,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_inherit);
+
+  if (unsubstituted_packs)
     {
       /* There were no real arguments, we're just replacing a parameter
 	 pack with another version of itself. Substitute into the
@@ -12910,16 +12922,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);
@@ -12966,12 +12968,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/g++.dg/cpp0x/variadic179.C b/gcc/testsuite/g++.dg/cpp0x/variadic179.C
new file mode 100644
index 00000000000..f04d3f753ca
--- /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<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
index 00000000000..e8fcdd0a64d
--- /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 given.
+// { 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);
+}