diff mbox series

c++: CTAD and explicit deduction guides for copy-list-init [PR90210]

Message ID 20200919213336.2260320-1-polacek@redhat.com
State New
Headers show
Series c++: CTAD and explicit deduction guides for copy-list-init [PR90210] | expand

Commit Message

Marek Polacek Sept. 19, 2020, 9:33 p.m. UTC
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++.

Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?

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.
---
 gcc/cp/pt.c                                   | 49 ++++++++++++++-----
 .../g++.dg/cpp1z/class-deduction73.C          | 41 ++++++++++++++++
 2 files changed, 79 insertions(+), 11 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp1z/class-deduction73.C


base-commit: 4a5ff2b56bfea0b3e154a15e809c5c42dc3b9e9f

Comments

Marek Polacek Sept. 29, 2020, 8:25 p.m. UTC | #1
Ping.

On Sat, Sep 19, 2020 at 05:33:36PM -0400, Marek Polacek via Gcc-patches wrote:
> 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++.
> 
> Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?
> 
> 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.
> ---
>  gcc/cp/pt.c                                   | 49 ++++++++++++++-----
>  .../g++.dg/cpp1z/class-deduction73.C          | 41 ++++++++++++++++
>  2 files changed, 79 insertions(+), 11 deletions(-)
>  create mode 100644 gcc/testsuite/g++.dg/cpp1z/class-deduction73.C
> 
> diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
> index cfe5ff4a94f..9bcb743dc1d 100644
> --- a/gcc/cp/pt.c
> +++ b/gcc/cp/pt.c
> @@ -28929,6 +28929,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;
> @@ -28936,6 +28937,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)
>  	{
> @@ -28967,9 +28969,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)))
> @@ -29038,18 +29041,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;
> +	}
>      }
>  
>    return cp_build_qualified_type (TREE_TYPE (call), cp_type_quals (ptype));
> 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 00000000000..b37ddedd1bf
> --- /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};
> 
> base-commit: 4a5ff2b56bfea0b3e154a15e809c5c42dc3b9e9f
> -- 
> 2.26.2
> 

Marek
Jason Merrill Sept. 30, 2020, 9:19 p.m. UTC | #2
On 9/19/20 5:33 PM, Marek Polacek wrote:
> 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++.
> 
> Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?

OK.

> 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.
> ---
>   gcc/cp/pt.c                                   | 49 ++++++++++++++-----
>   .../g++.dg/cpp1z/class-deduction73.C          | 41 ++++++++++++++++
>   2 files changed, 79 insertions(+), 11 deletions(-)
>   create mode 100644 gcc/testsuite/g++.dg/cpp1z/class-deduction73.C
> 
> diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
> index cfe5ff4a94f..9bcb743dc1d 100644
> --- a/gcc/cp/pt.c
> +++ b/gcc/cp/pt.c
> @@ -28929,6 +28929,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;
> @@ -28936,6 +28937,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)
>   	{
> @@ -28967,9 +28969,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)))
> @@ -29038,18 +29041,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;
> +	}
>       }
>   
>     return cp_build_qualified_type (TREE_TYPE (call), cp_type_quals (ptype));
> 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 00000000000..b37ddedd1bf
> --- /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};
> 
> base-commit: 4a5ff2b56bfea0b3e154a15e809c5c42dc3b9e9f
>
diff mbox series

Patch

diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index cfe5ff4a94f..9bcb743dc1d 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -28929,6 +28929,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;
@@ -28936,6 +28937,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)
 	{
@@ -28967,9 +28969,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)))
@@ -29038,18 +29041,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;
+	}
     }
 
   return cp_build_qualified_type (TREE_TYPE (call), cp_type_quals (ptype));
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 00000000000..b37ddedd1bf
--- /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};