diff mbox series

c++: variadic using-decl with parm pack in terminal name [PR102104]

Message ID 20221215161535.2731182-1-ppalka@redhat.com
State New
Headers show
Series c++: variadic using-decl with parm pack in terminal name [PR102104] | expand

Commit Message

Patrick Palka Dec. 15, 2022, 4:15 p.m. UTC
There's a curious corner case with variadic member using-decls: the
terminal name can also contain a parameter pack, and only through naming
a conversion function, e.g.

  using A<Ts>::operator Ts...;

We currently only handle parameter packs appearing in the qualifying
scope of a variadic using-decl; this patch adds support for the above
case as well, representing such a using-decl with two pack expansions,
one for the qualifying scope and one for the terminal name (despite
logically there being just one).  Then at instantiation time we manually
merge them.

Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look OK for
trunk?

	PR c++/102104
	PR c++/108090

gcc/cp/ChangeLog:

	* error.cc (dump_decl) <case USING_DECL>: Look through a
	pack expansion in the name as well.
	* parser.c (cp_parser_using_declaration): Handle a parameter
	pack appearing in the terminal name of a variadic using-decl.
	* pt.c (tsubst_decl) <case USING_DECL>: Likewise.  Combine the
	handling of variadic and non-variadic using-decls.

gcc/testsuite/ChangeLog:

	* g++.dg/cpp1z/using-variadic1.C: New test.
	* g++.dg/cpp1z/using-variadic1a.C: New test.
	* g++.dg/cpp1z/using-variadic1b.C: New test.
	* g++.dg/cpp1z/using-variadic1c.C: New test.
	* g++.dg/cpp1z/using-variadic2.C: New test.
	* g++.dg/cpp1z/using-variadic3.C: New test.
---
 gcc/cp/error.cc                               |  9 ++
 gcc/cp/parser.cc                              | 33 ++++++-
 gcc/cp/pt.cc                                  | 91 ++++++++++++++-----
 gcc/testsuite/g++.dg/cpp1z/using-variadic1.C  | 29 ++++++
 gcc/testsuite/g++.dg/cpp1z/using-variadic1a.C | 34 +++++++
 gcc/testsuite/g++.dg/cpp1z/using-variadic1b.C | 37 ++++++++
 gcc/testsuite/g++.dg/cpp1z/using-variadic1c.C | 33 +++++++
 gcc/testsuite/g++.dg/cpp1z/using-variadic2.C  | 24 +++++
 gcc/testsuite/g++.dg/cpp1z/using-variadic3.C  |  8 ++
 9 files changed, 272 insertions(+), 26 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp1z/using-variadic1.C
 create mode 100644 gcc/testsuite/g++.dg/cpp1z/using-variadic1a.C
 create mode 100644 gcc/testsuite/g++.dg/cpp1z/using-variadic1b.C
 create mode 100644 gcc/testsuite/g++.dg/cpp1z/using-variadic1c.C
 create mode 100644 gcc/testsuite/g++.dg/cpp1z/using-variadic2.C
 create mode 100644 gcc/testsuite/g++.dg/cpp1z/using-variadic3.C

Comments

Jason Merrill Dec. 15, 2022, 10:36 p.m. UTC | #1
On 12/15/22 11:15, Patrick Palka wrote:
> There's a curious corner case with variadic member using-decls: the
> terminal name can also contain a parameter pack, and only through naming
> a conversion function, e.g.
> 
>    using A<Ts>::operator Ts...;
> 
> We currently only handle parameter packs appearing in the qualifying
> scope of a variadic using-decl; this patch adds support for the above
> case as well, representing such a using-decl with two pack expansions,
> one for the qualifying scope and one for the terminal name (despite
> logically there being just one).  Then at instantiation time we manually
> merge them.
> 
> Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look OK for
> trunk?

OK.

> 	PR c++/102104
> 	PR c++/108090
> 
> gcc/cp/ChangeLog:
> 
> 	* error.cc (dump_decl) <case USING_DECL>: Look through a
> 	pack expansion in the name as well.
> 	* parser.c (cp_parser_using_declaration): Handle a parameter
> 	pack appearing in the terminal name of a variadic using-decl.
> 	* pt.c (tsubst_decl) <case USING_DECL>: Likewise.  Combine the
> 	handling of variadic and non-variadic using-decls.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* g++.dg/cpp1z/using-variadic1.C: New test.
> 	* g++.dg/cpp1z/using-variadic1a.C: New test.
> 	* g++.dg/cpp1z/using-variadic1b.C: New test.
> 	* g++.dg/cpp1z/using-variadic1c.C: New test.
> 	* g++.dg/cpp1z/using-variadic2.C: New test.
> 	* g++.dg/cpp1z/using-variadic3.C: New test.
> ---
>   gcc/cp/error.cc                               |  9 ++
>   gcc/cp/parser.cc                              | 33 ++++++-
>   gcc/cp/pt.cc                                  | 91 ++++++++++++++-----
>   gcc/testsuite/g++.dg/cpp1z/using-variadic1.C  | 29 ++++++
>   gcc/testsuite/g++.dg/cpp1z/using-variadic1a.C | 34 +++++++
>   gcc/testsuite/g++.dg/cpp1z/using-variadic1b.C | 37 ++++++++
>   gcc/testsuite/g++.dg/cpp1z/using-variadic1c.C | 33 +++++++
>   gcc/testsuite/g++.dg/cpp1z/using-variadic2.C  | 24 +++++
>   gcc/testsuite/g++.dg/cpp1z/using-variadic3.C  |  8 ++
>   9 files changed, 272 insertions(+), 26 deletions(-)
>   create mode 100644 gcc/testsuite/g++.dg/cpp1z/using-variadic1.C
>   create mode 100644 gcc/testsuite/g++.dg/cpp1z/using-variadic1a.C
>   create mode 100644 gcc/testsuite/g++.dg/cpp1z/using-variadic1b.C
>   create mode 100644 gcc/testsuite/g++.dg/cpp1z/using-variadic1c.C
>   create mode 100644 gcc/testsuite/g++.dg/cpp1z/using-variadic2.C
>   create mode 100644 gcc/testsuite/g++.dg/cpp1z/using-variadic3.C
> 
> diff --git a/gcc/cp/error.cc b/gcc/cp/error.cc
> index 12b28e8ee5b..e7f60335cc0 100644
> --- a/gcc/cp/error.cc
> +++ b/gcc/cp/error.cc
> @@ -1477,11 +1477,20 @@ dump_decl (cxx_pretty_printer *pp, tree t, int flags)
>   	if (!(flags & TFF_UNQUALIFIED_NAME))
>   	  {
>   	    tree scope = USING_DECL_SCOPE (t);
> +	    tree name = DECL_NAME (t);
>   	    if (PACK_EXPANSION_P (scope))
>   	      {
>   		scope = PACK_EXPANSION_PATTERN (scope);
>   		variadic = true;
>   	      }
> +	    if (identifier_p (name)
> +		&& IDENTIFIER_CONV_OP_P (name)
> +		&& PACK_EXPANSION_P (TREE_TYPE (name)))
> +	      {
> +		name = make_conv_op_name (PACK_EXPANSION_PATTERN
> +					  (TREE_TYPE (name)));
> +		variadic = true;
> +	      }
>   	    dump_type (pp, scope, flags);
>   	    pp_cxx_colon_colon (pp);
>   	  }
> diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
> index 03550308365..2e2d81c1316 100644
> --- a/gcc/cp/parser.cc
> +++ b/gcc/cp/parser.cc
> @@ -21705,7 +21705,38 @@ cp_parser_using_declaration (cp_parser* parser,
>   	pedwarn (ell->location, OPT_Wc__17_extensions,
>   		 "pack expansion in using-declaration only available "
>   		 "with %<-std=c++17%> or %<-std=gnu++17%>");
> -      qscope = make_pack_expansion (qscope);
> +
> +      /* A parameter pack can appear in the qualifying scope, and/or in the
> +	 terminal name (if naming a conversion function).  Logically they're
> +	 part of a single pack expansion of the overall USING_DECL, but we
> +	 express them as separate pack expansions within the USING_DECL since
> +	 we can't create a pack expansion over a USING_DECL.  */
> +      bool saw_parm_pack = false;
> +      if (uses_parameter_packs (qscope))
> +	{
> +	  qscope = make_pack_expansion (qscope);
> +	  saw_parm_pack = true;
> +	}
> +      /* It can also appear in the terminal name (if naming a conversion
> +	 function).  */
> +      if (identifier_p (identifier)
> +	  && IDENTIFIER_CONV_OP_P (identifier)
> +	  && uses_parameter_packs (TREE_TYPE (identifier)))
> +	{
> +	  identifier = make_conv_op_name (make_pack_expansion
> +					  (TREE_TYPE (identifier)));
> +	  saw_parm_pack = true;
> +	}
> +      if (!saw_parm_pack)
> +	{
> +	  /* Issue an error in terms using a SCOPE_REF that includes both
> +	     components.  */
> +	  tree name
> +	    = build_qualified_name (NULL_TREE, qscope, identifier, false);
> +	  make_pack_expansion (name);
> +	  gcc_assert (seen_error ());
> +	  qscope = identifier = error_mark_node;
> +	}
>       }
>   
>     /* The function we call to handle a using-declaration is different
> diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
> index 44058d30799..2fb7d6832ea 100644
> --- a/gcc/cp/pt.cc
> +++ b/gcc/cp/pt.cc
> @@ -14963,43 +14963,84 @@ tsubst_decl (tree t, tree args, tsubst_flags_t complain)
>         if (DECL_DEPENDENT_P (t)
>   	  || uses_template_parms (USING_DECL_SCOPE (t)))
>   	{
> +	  /* True iff this using-decl was written as a pack expansion
> +	     (and a pack appeared in its scope or name).  If a pack
> +	     appeared in both, we expand the packs separately and
> +	     manually merge them.  */
> +	  bool variadic_p = false;
> +
>   	  tree scope = USING_DECL_SCOPE (t);
> -	  tree name = tsubst_copy (DECL_NAME (t), args, complain, in_decl);
>   	  if (PACK_EXPANSION_P (scope))
>   	    {
> -	      tree vec = tsubst_pack_expansion (scope, args, complain, in_decl);
> -	      int len = TREE_VEC_LENGTH (vec);
> -	      r = make_tree_vec (len);
> -	      for (int i = 0; i < len; ++i)
> +	      scope = tsubst_pack_expansion (scope, args, complain, in_decl);
> +	      variadic_p = true;
> +	    }
> +	  else
> +	    scope = tsubst_copy (scope, args, complain, in_decl);
> +
> +	  tree name = DECL_NAME (t);
> +	  if (IDENTIFIER_CONV_OP_P (name)
> +	      && PACK_EXPANSION_P (TREE_TYPE (name)))
> +	    {
> +	      name = tsubst_pack_expansion (TREE_TYPE (name), args,
> +					    complain, in_decl);
> +	      if (name == error_mark_node)
>   		{
> -		  tree escope = TREE_VEC_ELT (vec, i);
> -		  tree elt = do_class_using_decl (escope, name);
> -		  if (!elt)
> -		    {
> -		      r = error_mark_node;
> -		      break;
> -		    }
> -		  else
> -		    {
> -		      TREE_PROTECTED (elt) = TREE_PROTECTED (t);
> -		      TREE_PRIVATE (elt) = TREE_PRIVATE (t);
> -		    }
> -		  TREE_VEC_ELT (r, i) = elt;
> +		  r = error_mark_node;
> +		  break;
>   		}
> +	      for (int i = 0; i < TREE_VEC_LENGTH (name); i++)
> +		TREE_VEC_ELT (name, i)
> +		  = make_conv_op_name (TREE_VEC_ELT (name, i));
> +	      variadic_p = true;
>   	    }
>   	  else
> +	    name = tsubst_copy (name, args, complain, in_decl);
> +
> +	  int len;
> +	  if (!variadic_p)
> +	    len = 1;
> +	  else if (TREE_CODE (scope) == TREE_VEC
> +		   && TREE_CODE (name) == TREE_VEC)
>   	    {
> -	      tree inst_scope = tsubst_copy (USING_DECL_SCOPE (t), args,
> -					     complain, in_decl);
> -	      r = do_class_using_decl (inst_scope, name);
> -	      if (!r)
> -		r = error_mark_node;
> +	      if (TREE_VEC_LENGTH (scope) != TREE_VEC_LENGTH (name))
> +		{
> +		  error ("mismatched argument pack lengths");
> +		  r = error_mark_node;
> +		  break;
> +		}
> +	      len = TREE_VEC_LENGTH (scope);
> +	    }
> +	  else if (TREE_CODE (scope) == TREE_VEC)
> +	    len = TREE_VEC_LENGTH (scope);
> +	  else /* TREE_CODE (name) == TREE_VEC  */
> +	    len = TREE_VEC_LENGTH (name);
> +
> +	  r = make_tree_vec (len);
> +	  for (int i = 0; i < len; ++i)
> +	    {
> +	      tree escope = (TREE_CODE (scope) == TREE_VEC
> +			     ? TREE_VEC_ELT (scope, i)
> +			     : scope);
> +	      tree ename = (TREE_CODE (name) == TREE_VEC
> +			    ? TREE_VEC_ELT (name, i)
> +			    : name);
> +	      tree elt = do_class_using_decl (escope, ename);
> +	      if (!elt)
> +		{
> +		  r = error_mark_node;
> +		  break;
> +		}
>   	      else
>   		{
> -		  TREE_PROTECTED (r) = TREE_PROTECTED (t);
> -		  TREE_PRIVATE (r) = TREE_PRIVATE (t);
> +		  TREE_PROTECTED (elt) = TREE_PROTECTED (t);
> +		  TREE_PRIVATE (elt) = TREE_PRIVATE (t);
>   		}
> +	      TREE_VEC_ELT (r, i) = elt;
>   	    }
> +
> +	  if (!variadic_p && r != error_mark_node)
> +	    r = TREE_VEC_ELT (r, 0);
>   	}
>         else
>   	{
> diff --git a/gcc/testsuite/g++.dg/cpp1z/using-variadic1.C b/gcc/testsuite/g++.dg/cpp1z/using-variadic1.C
> new file mode 100644
> index 00000000000..7a8bcbbe372
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp1z/using-variadic1.C
> @@ -0,0 +1,29 @@
> +// PR c++/102104
> +// { dg-do compile { target c++17 } }
> +
> +struct A {
> +  using target_type = bool*;
> +  operator bool*();
> +};
> +
> +struct B {
> +  using target_type = long*;
> +  operator long*();
> +};
> +
> +template<typename... Bases>
> +struct cls : private Bases... {
> +  using Bases::operator typename Bases::target_type...;
> +};
> +
> +cls<A, B> v1;
> +bool* a1 = v1;
> +long* b1 = v1;
> +
> +cls<B> v2;
> +bool* a2 = v2; // { dg-error "cannot convert" }
> +long* b2 = v2;
> +
> +cls<A> v3;
> +bool* a3 = v3;
> +long* b3 = v3; // { dg-error "cannot convert" }
> diff --git a/gcc/testsuite/g++.dg/cpp1z/using-variadic1a.C b/gcc/testsuite/g++.dg/cpp1z/using-variadic1a.C
> new file mode 100644
> index 00000000000..0393cab6ace
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp1z/using-variadic1a.C
> @@ -0,0 +1,34 @@
> +// PR c++/102104
> +// A version of using-variadic1.C where the qualifying scope and the
> +// terminal name of the using-declaration use different parameter packs.
> +// { dg-do compile { target c++17 } }
> +
> +struct A {
> +  using target_type = bool*;
> +  operator bool*();
> +};
> +
> +struct B {
> +  using target_type = long*;
> +  operator long*();
> +};
> +
> +template<typename... Bases>
> +struct cls {
> +  template<class... Ts>
> +  struct nested : private Bases... {
> +    using Bases::operator typename Ts::target_type...;
> +  };
> +};
> +
> +cls<A, B>::nested<A, B> v1;
> +bool* a1 = v1;
> +long* b1 = v1;
> +
> +cls<B>::nested<B> v2;
> +bool* a2 = v2; // { dg-error "cannot convert" }
> +long* b2 = v2;
> +
> +cls<A>::nested<A> v3;
> +bool* a3 = v3;
> +long* b3 = v3; // { dg-error "cannot convert" }
> diff --git a/gcc/testsuite/g++.dg/cpp1z/using-variadic1b.C b/gcc/testsuite/g++.dg/cpp1z/using-variadic1b.C
> new file mode 100644
> index 00000000000..fd3a41718b6
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp1z/using-variadic1b.C
> @@ -0,0 +1,37 @@
> +// PR c++/102104
> +// A version of using-variadic1.C where only the qualifying scope
> +// uses a parameter pack.
> +// { dg-do compile { target c++17 } }
> +
> +struct A {
> +  using target_type = bool*;
> +};
> +
> +struct B {
> +  using target_type = long*;
> +};
> +
> +struct C {
> +  operator bool*();
> +  operator long*();
> +};
> +
> +template<typename Base>
> +struct cls {
> +  template<class... Ts>
> +  struct nested : private Base {
> +    using Base::operator typename Ts::target_type...;
> +  };
> +};
> +
> +cls<C>::nested<A, B> v1;
> +bool* a1 = v1;
> +long* b1 = v1;
> +
> +cls<C>::nested<B> v2;
> +bool* a2 = v2; // { dg-error "inaccessible|not an accessible" }
> +long* b2 = v2;
> +
> +cls<C>::nested<A> v3;
> +bool* a3 = v3;
> +long* b3 = v3; // { dg-error "inaccessible|not an accessible" }
> diff --git a/gcc/testsuite/g++.dg/cpp1z/using-variadic1c.C b/gcc/testsuite/g++.dg/cpp1z/using-variadic1c.C
> new file mode 100644
> index 00000000000..aa86b28fd2e
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp1z/using-variadic1c.C
> @@ -0,0 +1,33 @@
> +// PR c++/102104
> +// A version of of using-variadic1.C where only the terminal name
> +// uses a parameter pack.
> +// { dg-do compile { target c++17 } }
> +
> +struct A {
> +  operator bool*();
> +};
> +
> +struct B {
> +  operator bool*();
> +};
> +
> +struct C {
> +  using target_type = bool*;
> +};
> +
> +template<typename... Bases>
> +struct cls {
> +  template<class T>
> +  struct nested : private Bases... {
> +    using Bases::operator typename T::target_type...;
> +  };
> +};
> +
> +cls<A, B>::nested<C> v1;
> +bool* a1 = v1; // { dg-error "ambiguous" }
> +
> +cls<A>::nested<C> v2;
> +bool* a2 = v2;
> +
> +cls<B>::nested<C> v3;
> +bool* a3 = v3;
> diff --git a/gcc/testsuite/g++.dg/cpp1z/using-variadic2.C b/gcc/testsuite/g++.dg/cpp1z/using-variadic2.C
> new file mode 100644
> index 00000000000..1d68ceece8a
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp1z/using-variadic2.C
> @@ -0,0 +1,24 @@
> +// PR c++/102104
> +// A version of using-variadic1a.C where the argument packs have
> +// different lengths.
> +// { dg-do compile { target c++17 } }
> +
> +struct A {
> +  using target_type = bool*;
> +  operator bool*();
> +};
> +
> +struct B {
> +  using target_type = long*;
> +  operator long*();
> +};
> +
> +template<typename... Bases>
> +struct cls {
> +  template<class... Ts>
> +  struct nested : private Bases... {
> +    using Bases::operator typename Ts::target_type...; // { dg-error "lengths" }
> +  };
> +};
> +
> +cls<A>::nested<A, B> v1; // { dg-message "required from here" }
> diff --git a/gcc/testsuite/g++.dg/cpp1z/using-variadic3.C b/gcc/testsuite/g++.dg/cpp1z/using-variadic3.C
> new file mode 100644
> index 00000000000..4e1d6894e56
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp1z/using-variadic3.C
> @@ -0,0 +1,8 @@
> +// PR c++/108090
> +// { dg-do compile { target c++17 } }
> +
> +template<typename T> struct As { operator T(); };
> +template<typename ...T> struct AsAll : As<T>... {
> +  using As<T>::operator T...;
> +};
> +AsAll<int, float, char> x;
diff mbox series

Patch

diff --git a/gcc/cp/error.cc b/gcc/cp/error.cc
index 12b28e8ee5b..e7f60335cc0 100644
--- a/gcc/cp/error.cc
+++ b/gcc/cp/error.cc
@@ -1477,11 +1477,20 @@  dump_decl (cxx_pretty_printer *pp, tree t, int flags)
 	if (!(flags & TFF_UNQUALIFIED_NAME))
 	  {
 	    tree scope = USING_DECL_SCOPE (t);
+	    tree name = DECL_NAME (t);
 	    if (PACK_EXPANSION_P (scope))
 	      {
 		scope = PACK_EXPANSION_PATTERN (scope);
 		variadic = true;
 	      }
+	    if (identifier_p (name)
+		&& IDENTIFIER_CONV_OP_P (name)
+		&& PACK_EXPANSION_P (TREE_TYPE (name)))
+	      {
+		name = make_conv_op_name (PACK_EXPANSION_PATTERN
+					  (TREE_TYPE (name)));
+		variadic = true;
+	      }
 	    dump_type (pp, scope, flags);
 	    pp_cxx_colon_colon (pp);
 	  }
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index 03550308365..2e2d81c1316 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -21705,7 +21705,38 @@  cp_parser_using_declaration (cp_parser* parser,
 	pedwarn (ell->location, OPT_Wc__17_extensions,
 		 "pack expansion in using-declaration only available "
 		 "with %<-std=c++17%> or %<-std=gnu++17%>");
-      qscope = make_pack_expansion (qscope);
+
+      /* A parameter pack can appear in the qualifying scope, and/or in the
+	 terminal name (if naming a conversion function).  Logically they're
+	 part of a single pack expansion of the overall USING_DECL, but we
+	 express them as separate pack expansions within the USING_DECL since
+	 we can't create a pack expansion over a USING_DECL.  */
+      bool saw_parm_pack = false;
+      if (uses_parameter_packs (qscope))
+	{
+	  qscope = make_pack_expansion (qscope);
+	  saw_parm_pack = true;
+	}
+      /* It can also appear in the terminal name (if naming a conversion
+	 function).  */
+      if (identifier_p (identifier)
+	  && IDENTIFIER_CONV_OP_P (identifier)
+	  && uses_parameter_packs (TREE_TYPE (identifier)))
+	{
+	  identifier = make_conv_op_name (make_pack_expansion
+					  (TREE_TYPE (identifier)));
+	  saw_parm_pack = true;
+	}
+      if (!saw_parm_pack)
+	{
+	  /* Issue an error in terms using a SCOPE_REF that includes both
+	     components.  */
+	  tree name
+	    = build_qualified_name (NULL_TREE, qscope, identifier, false);
+	  make_pack_expansion (name);
+	  gcc_assert (seen_error ());
+	  qscope = identifier = error_mark_node;
+	}
     }
 
   /* The function we call to handle a using-declaration is different
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index 44058d30799..2fb7d6832ea 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -14963,43 +14963,84 @@  tsubst_decl (tree t, tree args, tsubst_flags_t complain)
       if (DECL_DEPENDENT_P (t)
 	  || uses_template_parms (USING_DECL_SCOPE (t)))
 	{
+	  /* True iff this using-decl was written as a pack expansion
+	     (and a pack appeared in its scope or name).  If a pack
+	     appeared in both, we expand the packs separately and
+	     manually merge them.  */
+	  bool variadic_p = false;
+
 	  tree scope = USING_DECL_SCOPE (t);
-	  tree name = tsubst_copy (DECL_NAME (t), args, complain, in_decl);
 	  if (PACK_EXPANSION_P (scope))
 	    {
-	      tree vec = tsubst_pack_expansion (scope, args, complain, in_decl);
-	      int len = TREE_VEC_LENGTH (vec);
-	      r = make_tree_vec (len);
-	      for (int i = 0; i < len; ++i)
+	      scope = tsubst_pack_expansion (scope, args, complain, in_decl);
+	      variadic_p = true;
+	    }
+	  else
+	    scope = tsubst_copy (scope, args, complain, in_decl);
+
+	  tree name = DECL_NAME (t);
+	  if (IDENTIFIER_CONV_OP_P (name)
+	      && PACK_EXPANSION_P (TREE_TYPE (name)))
+	    {
+	      name = tsubst_pack_expansion (TREE_TYPE (name), args,
+					    complain, in_decl);
+	      if (name == error_mark_node)
 		{
-		  tree escope = TREE_VEC_ELT (vec, i);
-		  tree elt = do_class_using_decl (escope, name);
-		  if (!elt)
-		    {
-		      r = error_mark_node;
-		      break;
-		    }
-		  else
-		    {
-		      TREE_PROTECTED (elt) = TREE_PROTECTED (t);
-		      TREE_PRIVATE (elt) = TREE_PRIVATE (t);
-		    }
-		  TREE_VEC_ELT (r, i) = elt;
+		  r = error_mark_node;
+		  break;
 		}
+	      for (int i = 0; i < TREE_VEC_LENGTH (name); i++)
+		TREE_VEC_ELT (name, i)
+		  = make_conv_op_name (TREE_VEC_ELT (name, i));
+	      variadic_p = true;
 	    }
 	  else
+	    name = tsubst_copy (name, args, complain, in_decl);
+
+	  int len;
+	  if (!variadic_p)
+	    len = 1;
+	  else if (TREE_CODE (scope) == TREE_VEC
+		   && TREE_CODE (name) == TREE_VEC)
 	    {
-	      tree inst_scope = tsubst_copy (USING_DECL_SCOPE (t), args,
-					     complain, in_decl);
-	      r = do_class_using_decl (inst_scope, name);
-	      if (!r)
-		r = error_mark_node;
+	      if (TREE_VEC_LENGTH (scope) != TREE_VEC_LENGTH (name))
+		{
+		  error ("mismatched argument pack lengths");
+		  r = error_mark_node;
+		  break;
+		}
+	      len = TREE_VEC_LENGTH (scope);
+	    }
+	  else if (TREE_CODE (scope) == TREE_VEC)
+	    len = TREE_VEC_LENGTH (scope);
+	  else /* TREE_CODE (name) == TREE_VEC  */
+	    len = TREE_VEC_LENGTH (name);
+
+	  r = make_tree_vec (len);
+	  for (int i = 0; i < len; ++i)
+	    {
+	      tree escope = (TREE_CODE (scope) == TREE_VEC
+			     ? TREE_VEC_ELT (scope, i)
+			     : scope);
+	      tree ename = (TREE_CODE (name) == TREE_VEC
+			    ? TREE_VEC_ELT (name, i)
+			    : name);
+	      tree elt = do_class_using_decl (escope, ename);
+	      if (!elt)
+		{
+		  r = error_mark_node;
+		  break;
+		}
 	      else
 		{
-		  TREE_PROTECTED (r) = TREE_PROTECTED (t);
-		  TREE_PRIVATE (r) = TREE_PRIVATE (t);
+		  TREE_PROTECTED (elt) = TREE_PROTECTED (t);
+		  TREE_PRIVATE (elt) = TREE_PRIVATE (t);
 		}
+	      TREE_VEC_ELT (r, i) = elt;
 	    }
+
+	  if (!variadic_p && r != error_mark_node)
+	    r = TREE_VEC_ELT (r, 0);
 	}
       else
 	{
diff --git a/gcc/testsuite/g++.dg/cpp1z/using-variadic1.C b/gcc/testsuite/g++.dg/cpp1z/using-variadic1.C
new file mode 100644
index 00000000000..7a8bcbbe372
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/using-variadic1.C
@@ -0,0 +1,29 @@ 
+// PR c++/102104
+// { dg-do compile { target c++17 } }
+
+struct A {
+  using target_type = bool*;
+  operator bool*();
+};
+
+struct B {
+  using target_type = long*;
+  operator long*();
+};
+
+template<typename... Bases>
+struct cls : private Bases... {
+  using Bases::operator typename Bases::target_type...;
+};
+
+cls<A, B> v1;
+bool* a1 = v1;
+long* b1 = v1;
+
+cls<B> v2;
+bool* a2 = v2; // { dg-error "cannot convert" }
+long* b2 = v2;
+
+cls<A> v3;
+bool* a3 = v3;
+long* b3 = v3; // { dg-error "cannot convert" }
diff --git a/gcc/testsuite/g++.dg/cpp1z/using-variadic1a.C b/gcc/testsuite/g++.dg/cpp1z/using-variadic1a.C
new file mode 100644
index 00000000000..0393cab6ace
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/using-variadic1a.C
@@ -0,0 +1,34 @@ 
+// PR c++/102104
+// A version of using-variadic1.C where the qualifying scope and the
+// terminal name of the using-declaration use different parameter packs.
+// { dg-do compile { target c++17 } }
+
+struct A {
+  using target_type = bool*;
+  operator bool*();
+};
+
+struct B {
+  using target_type = long*;
+  operator long*();
+};
+
+template<typename... Bases>
+struct cls {
+  template<class... Ts>
+  struct nested : private Bases... {
+    using Bases::operator typename Ts::target_type...;
+  };
+};
+
+cls<A, B>::nested<A, B> v1;
+bool* a1 = v1;
+long* b1 = v1;
+
+cls<B>::nested<B> v2;
+bool* a2 = v2; // { dg-error "cannot convert" }
+long* b2 = v2;
+
+cls<A>::nested<A> v3;
+bool* a3 = v3;
+long* b3 = v3; // { dg-error "cannot convert" }
diff --git a/gcc/testsuite/g++.dg/cpp1z/using-variadic1b.C b/gcc/testsuite/g++.dg/cpp1z/using-variadic1b.C
new file mode 100644
index 00000000000..fd3a41718b6
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/using-variadic1b.C
@@ -0,0 +1,37 @@ 
+// PR c++/102104
+// A version of using-variadic1.C where only the qualifying scope
+// uses a parameter pack.
+// { dg-do compile { target c++17 } }
+
+struct A {
+  using target_type = bool*;
+};
+
+struct B {
+  using target_type = long*;
+};
+
+struct C {
+  operator bool*();
+  operator long*();
+};
+
+template<typename Base>
+struct cls {
+  template<class... Ts>
+  struct nested : private Base {
+    using Base::operator typename Ts::target_type...;
+  };
+};
+
+cls<C>::nested<A, B> v1;
+bool* a1 = v1;
+long* b1 = v1;
+
+cls<C>::nested<B> v2;
+bool* a2 = v2; // { dg-error "inaccessible|not an accessible" }
+long* b2 = v2;
+
+cls<C>::nested<A> v3;
+bool* a3 = v3;
+long* b3 = v3; // { dg-error "inaccessible|not an accessible" }
diff --git a/gcc/testsuite/g++.dg/cpp1z/using-variadic1c.C b/gcc/testsuite/g++.dg/cpp1z/using-variadic1c.C
new file mode 100644
index 00000000000..aa86b28fd2e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/using-variadic1c.C
@@ -0,0 +1,33 @@ 
+// PR c++/102104
+// A version of of using-variadic1.C where only the terminal name
+// uses a parameter pack.
+// { dg-do compile { target c++17 } }
+
+struct A {
+  operator bool*();
+};
+
+struct B {
+  operator bool*();
+};
+
+struct C {
+  using target_type = bool*;
+};
+
+template<typename... Bases>
+struct cls {
+  template<class T>
+  struct nested : private Bases... {
+    using Bases::operator typename T::target_type...;
+  };
+};
+
+cls<A, B>::nested<C> v1;
+bool* a1 = v1; // { dg-error "ambiguous" }
+
+cls<A>::nested<C> v2;
+bool* a2 = v2;
+
+cls<B>::nested<C> v3;
+bool* a3 = v3;
diff --git a/gcc/testsuite/g++.dg/cpp1z/using-variadic2.C b/gcc/testsuite/g++.dg/cpp1z/using-variadic2.C
new file mode 100644
index 00000000000..1d68ceece8a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/using-variadic2.C
@@ -0,0 +1,24 @@ 
+// PR c++/102104
+// A version of using-variadic1a.C where the argument packs have
+// different lengths.
+// { dg-do compile { target c++17 } }
+
+struct A {
+  using target_type = bool*;
+  operator bool*();
+};
+
+struct B {
+  using target_type = long*;
+  operator long*();
+};
+
+template<typename... Bases>
+struct cls {
+  template<class... Ts>
+  struct nested : private Bases... {
+    using Bases::operator typename Ts::target_type...; // { dg-error "lengths" }
+  };
+};
+
+cls<A>::nested<A, B> v1; // { dg-message "required from here" }
diff --git a/gcc/testsuite/g++.dg/cpp1z/using-variadic3.C b/gcc/testsuite/g++.dg/cpp1z/using-variadic3.C
new file mode 100644
index 00000000000..4e1d6894e56
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/using-variadic3.C
@@ -0,0 +1,8 @@ 
+// PR c++/108090
+// { dg-do compile { target c++17 } }
+
+template<typename T> struct As { operator T(); };
+template<typename ...T> struct AsAll : As<T>... {
+  using As<T>::operator T...;
+};
+AsAll<int, float, char> x;