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;