diff options
author | Marek Polacek <polacek@redhat.com> | 2020-09-18 16:57:34 -0400 |
---|---|---|
committer | Marek Polacek <polacek@redhat.com> | 2020-09-30 20:56:42 -0400 |
commit | cf7dae01734eea0dfb4c387e4cd40e1f9a682f56 (patch) | |
tree | 4c98b0601dc585df10374119507e2a15d623f7d0 | |
parent | 660bfe61d4045c7931a7c1c3a166d0a2cd199412 (diff) | |
download | gcc-cf7dae01734eea0dfb4c387e4cd40e1f9a682f56.zip gcc-cf7dae01734eea0dfb4c387e4cd40e1f9a682f56.tar.gz gcc-cf7dae01734eea0dfb4c387e4cd40e1f9a682f56.tar.bz2 |
c++: CTAD and explicit deduction guides for copy-list-init [PR90210]
This PR points out that we accept
template<typename T> struct tuple { tuple(T); }; // #1
template<typename T> explicit tuple(T t) -> tuple<T>; // #2
tuple t = { 1 };
despite the 'explicit' deduction guide in a copy-list-initialization
context. That's because in deduction_guides_for we first find the
user-defined deduction guide (#2), and then ctor_deduction_guides_for
creates artificial deduction guides: one from the tuple(T) constructor and
a copy guide. So we end up with these three guides:
(1) template<class T> tuple(T) -> tuple<T> [DECL_NONCONVERTING_P]
(2) template<class T> tuple(tuple<T>) -> tuple<T>
(3) template<class T> tuple(T) -> tuple<T>
Then, in do_class_deduction, we prune this set, and get rid of (1).
Then overload resolution selects (3) and we succeed.
But [over.match.list]p1 says "In copy-list-initialization, if an explicit
constructor is chosen, the initialization is ill-formed." It also goes
on to say that this differs from other situations where only converting
constructors are considered for copy-initialization. Therefore for
list-initialization we consider explicit constructors and complain if one
is chosen. E.g. convert_like_internal/ck_user can give an error.
So my logic runs that we should not prune the deduction_guides_for guides
in a copy-list-initialization context, and only complain if we actually
choose an explicit deduction guide. This matches clang++/EDG/msvc++.
gcc/cp/ChangeLog:
PR c++/90210
* pt.c (do_class_deduction): Don't prune explicit deduction guides
in copy-list-initialization. In copy-list-initialization, if an
explicit deduction guide was selected, give an error.
gcc/testsuite/ChangeLog:
PR c++/90210
* g++.dg/cpp1z/class-deduction73.C: New test.
-rw-r--r-- | gcc/cp/pt.c | 49 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/cpp1z/class-deduction73.C | 41 |
2 files changed, 79 insertions, 11 deletions
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 652b458..869477f 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -28977,6 +28977,7 @@ do_class_deduction (tree ptype, tree tmpl, tree init, tree type = TREE_TYPE (tmpl); bool try_list_ctor = false; + bool list_init_p = false; releasing_vec rv_args = NULL; vec<tree,va_gc> *&args = *&rv_args; @@ -28984,6 +28985,7 @@ do_class_deduction (tree ptype, tree tmpl, tree init, args = make_tree_vector (); else if (BRACE_ENCLOSED_INITIALIZER_P (init)) { + list_init_p = true; try_list_ctor = TYPE_HAS_LIST_CTOR (type); if (try_list_ctor && CONSTRUCTOR_NELTS (init) == 1) { @@ -29016,9 +29018,10 @@ do_class_deduction (tree ptype, tree tmpl, tree init, if (cands == error_mark_node) return error_mark_node; - /* Prune explicit deduction guides in copy-initialization context. */ + /* Prune explicit deduction guides in copy-initialization context (but + not copy-list-initialization). */ bool elided = false; - if (flags & LOOKUP_ONLYCONVERTING) + if (!list_init_p && (flags & LOOKUP_ONLYCONVERTING)) { for (lkp_iterator iter (cands); !elided && iter; ++iter) if (DECL_NONCONVERTING_P (STRIP_TEMPLATE (*iter))) @@ -29087,18 +29090,42 @@ do_class_deduction (tree ptype, tree tmpl, tree init, --cp_unevaluated_operand; } - if (call == error_mark_node - && (complain & tf_warning_or_error)) + if (call == error_mark_node) { - error ("class template argument deduction failed:"); + if (complain & tf_warning_or_error) + { + error ("class template argument deduction failed:"); - ++cp_unevaluated_operand; - call = build_new_function_call (cands, &args, complain | tf_decltype); - --cp_unevaluated_operand; + ++cp_unevaluated_operand; + call = build_new_function_call (cands, &args, + complain | tf_decltype); + --cp_unevaluated_operand; - if (elided) - inform (input_location, "explicit deduction guides not considered " - "for copy-initialization"); + if (elided) + inform (input_location, "explicit deduction guides not considered " + "for copy-initialization"); + } + return error_mark_node; + } + /* [over.match.list]/1: In copy-list-initialization, if an explicit + constructor is chosen, the initialization is ill-formed. */ + else if (flags & LOOKUP_ONLYCONVERTING) + { + tree fndecl = cp_get_callee_fndecl_nofold (call); + if (fndecl && DECL_NONCONVERTING_P (fndecl)) + { + if (complain & tf_warning_or_error) + { + // TODO: Pass down location from cp_finish_decl. + error ("class template argument deduction for %qT failed: " + "explicit deduction guide selected in " + "copy-list-initialization", type); + inform (DECL_SOURCE_LOCATION (fndecl), + "explicit deduction guide declared here"); + + } + return error_mark_node; + } } /* If CTAD succeeded but the type doesn't have any explicit deduction diff --git a/gcc/testsuite/g++.dg/cpp1z/class-deduction73.C b/gcc/testsuite/g++.dg/cpp1z/class-deduction73.C new file mode 100644 index 0000000..b37dded --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1z/class-deduction73.C @@ -0,0 +1,41 @@ +// PR c++/90210 +// { dg-do compile { target c++17 } } + +template<typename T> struct tuple { tuple(T); }; +template<typename T> explicit tuple(T t) -> tuple<T>; +tuple t = { 1 }; // { dg-error "explicit deduction guide selected" } +tuple t1 = tuple{ 1 }; +tuple t2{ 1 }; + +template<typename T> struct A { A(T, T); }; +template<typename T> explicit A(T, T) -> A<int>; +A a = {1, 1}; // { dg-error "explicit deduction guide selected" } +A a1 = A{1, 1}; +A a2{1, 1}; + +template<typename T, typename U> +struct B { + B(T, U); +}; +template<typename T, typename U> +B(T, U) -> B<T, typename U::type>; // SFINAEd-out +B b = { 1, 2 }; // OK +B b1 = B{ 1, 2 }; // OK +B b2{ 1, 2 }; // OK + +// Overriden implicit default constructor deduction guide: +template<typename T> +struct C { }; +explicit C() -> C<int>; +C c = {}; // { dg-error "explicit deduction guide selected" } +C c1 = C{}; +C c2{}; + +// Overriden copy guide: +template<typename T> +struct D { }; +template<typename T> explicit D(D<T>) -> D<T>; +D<int> d; +D d1 = {d}; // { dg-error "explicit deduction guide selected" } +D d2 = D{d}; +D d3{d}; |