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;
