diff mbox series

c++: Implement -Wctad-maybe-unsupported.

Message ID 20200919213420.2260468-1-polacek@redhat.com
State New
Headers show
Series c++: Implement -Wctad-maybe-unsupported. | expand

Commit Message

Marek Polacek Sept. 19, 2020, 9:34 p.m. UTC
I noticed that clang++ has this CTAD warning and thought that it might
be useful to have it.  From clang++: "Some style guides want to allow
using CTAD only on types that "opt-in"; i.e. on types that are designed
to support it and not just types that *happen* to work with it."

So this warning warns when CTAD deduced a type, but the type does not
define any deduction guides.  In that case CTAD worked only because the
compiler synthesized the implicit deduction guides.  That might not be
intended.

It can be suppressed by adding a deduction guide that will never be
considered:

  struct allow_ctad_t;
  template <typename T> struct S { S(T) {} };
  S(allow_ctad_t) -> S<void>;

This warning is off by default.

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

gcc/c-family/ChangeLog:

	* c.opt (Wctad-maybe-unsupported): New option.

gcc/cp/ChangeLog:

	* pt.c (deduction_guides_for): Add a bool parameter.  Set it.
	(do_class_deduction): Warn when CTAD succeeds but the type doesn't
	have any explicit deduction guides.

gcc/ChangeLog:

	* doc/invoke.texi: Document -Wctad-maybe-unsupported.

gcc/testsuite/ChangeLog:

	* g++.dg/warn/Wctad-maybe-unsupported.C: New test.
---
 gcc/c-family/c.opt                            |  5 ++
 gcc/cp/pt.c                                   | 22 ++++-
 gcc/doc/invoke.texi                           | 20 ++++-
 .../g++.dg/warn/Wctad-maybe-unsupported.C     | 88 +++++++++++++++++++
 4 files changed, 130 insertions(+), 5 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/warn/Wctad-maybe-unsupported.C


base-commit: c66c004ad610f4f02d5f065fe29c4c10f05ae13f

Comments

Jason Merrill Sept. 21, 2020, 5:04 a.m. UTC | #1
On 9/19/20 5:34 PM, Marek Polacek wrote:
> I noticed that clang++ has this CTAD warning and thought that it might
> be useful to have it.  From clang++: "Some style guides want to allow
> using CTAD only on types that "opt-in"; i.e. on types that are designed
> to support it and not just types that *happen* to work with it."

That's a weird name for the warning, but I guess if that's what clang 
calls it then we shouldn't change it.

> So this warning warns when CTAD deduced a type, but the type does not
> define any deduction guides.  In that case CTAD worked only because the
> compiler synthesized the implicit deduction guides.  That might not be
> intended.
> 
> It can be suppressed by adding a deduction guide that will never be
> considered:
> 
>    struct allow_ctad_t;
>    template <typename T> struct S { S(T) {} };
>    S(allow_ctad_t) -> S<void>;
> 
> This warning is off by default.

> Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?
> 
> gcc/c-family/ChangeLog:
> 
> 	* c.opt (Wctad-maybe-unsupported): New option.
> 
> gcc/cp/ChangeLog:
> 
> 	* pt.c (deduction_guides_for): Add a bool parameter.  Set it.
> 	(do_class_deduction): Warn when CTAD succeeds but the type doesn't
> 	have any explicit deduction guides.
> 
> gcc/ChangeLog:
> 
> 	* doc/invoke.texi: Document -Wctad-maybe-unsupported.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* g++.dg/warn/Wctad-maybe-unsupported.C: New test.
> ---
>   gcc/c-family/c.opt                            |  5 ++
>   gcc/cp/pt.c                                   | 22 ++++-
>   gcc/doc/invoke.texi                           | 20 ++++-
>   .../g++.dg/warn/Wctad-maybe-unsupported.C     | 88 +++++++++++++++++++
>   4 files changed, 130 insertions(+), 5 deletions(-)
>   create mode 100644 gcc/testsuite/g++.dg/warn/Wctad-maybe-unsupported.C
> 
> diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
> index c1d8fd336e8..fef4f09f72e 100644
> --- a/gcc/c-family/c.opt
> +++ b/gcc/c-family/c.opt
> @@ -467,6 +467,11 @@ Wcpp
>   C ObjC C++ ObjC++ CppReason(CPP_W_WARNING_DIRECTIVE)
>   ; Documented in common.opt
>   
> +Wctad-maybe-unsupported
> +C++ ObjC++ Var(warn_ctad_maybe_unsupported) Warning
> +Warn when performing class template argument deduction on a type with no
> +deduction guides.
> +
>   Wctor-dtor-privacy
>   C++ ObjC++ Var(warn_ctor_dtor_privacy) Warning
>   Warn when all constructors and destructors are private.
> diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
> index fe45de8d796..177b762883d 100644
> --- a/gcc/cp/pt.c
> +++ b/gcc/cp/pt.c
> @@ -28830,17 +28830,19 @@ static GTY((deletable)) hash_map<tree, tree_pair_p> *dguide_cache;
>   
>   /* Return the non-aggregate deduction guides for deducible template TMPL.  The
>      aggregate candidate is added separately because it depends on the
> -   initializer.  */
> +   initializer.  Set ANY_DGUIDES_P if we find a non-implicit deduction
> +   guide.  */
>   
>   static tree
> -deduction_guides_for (tree tmpl, tsubst_flags_t complain)
> +deduction_guides_for (tree tmpl, bool &any_dguides_p, tsubst_flags_t complain)
>   {
>     tree guides = NULL_TREE;
>     if (DECL_ALIAS_TEMPLATE_P (tmpl))
>       {
>         tree under = DECL_ORIGINAL_TYPE (DECL_TEMPLATE_RESULT (tmpl));
>         tree tinfo = get_template_info (under);
> -      guides = deduction_guides_for (TI_TEMPLATE (tinfo), complain);
> +      guides = deduction_guides_for (TI_TEMPLATE (tinfo), any_dguides_p,
> +				     complain);
>       }
>     else
>       {
> @@ -28849,6 +28851,8 @@ deduction_guides_for (tree tmpl, tsubst_flags_t complain)
>   				      LOOK_want::NORMAL, /*complain*/false);
>         if (guides == error_mark_node)
>   	guides = NULL_TREE;
> +      else
> +	any_dguides_p = true;
>       }
>   
>     /* Cache the deduction guides for a template.  We also remember the result of
> @@ -28974,7 +28978,8 @@ do_class_deduction (tree ptype, tree tmpl, tree init,
>     if (args == NULL)
>       return error_mark_node;
>   
> -  tree cands = deduction_guides_for (tmpl, complain);
> +  bool any_dguides_p = false;
> +  tree cands = deduction_guides_for (tmpl, any_dguides_p, complain);
>     if (cands == error_mark_node)
>       return error_mark_node;
>   
> @@ -29063,6 +29068,15 @@ do_class_deduction (tree ptype, tree tmpl, tree init,
>   		"for copy-initialization");
>       }
>   
> +  /* If CTAD succeeded but the type doesn't have any explicit deduction
> +     guides, this deduction might not be what the user intended.  */
> +  if (call != error_mark_node
> +      && !any_dguides_p
> +      && warning (OPT_Wctad_maybe_unsupported,
> +		  "%qT may not intend to support class template argument "
> +		  "deduction", type))
> +    inform (input_location, "add a deduction guide to suppress this warning");

I think you want to avoid warning for types defined in a system header 
without -Wsystem-headers.

>     return cp_build_qualified_type (TREE_TYPE (call), cp_type_quals (ptype));
>   }
>   
> diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
> index 8086e27aefb..74864535f53 100644
> --- a/gcc/doc/invoke.texi
> +++ b/gcc/doc/invoke.texi
> @@ -236,7 +236,8 @@ in the following sections.
>   -Wabi-tag  -Wcatch-value  -Wcatch-value=@var{n} @gol
>   -Wno-class-conversion  -Wclass-memaccess @gol
>   -Wcomma-subscript  -Wconditionally-supported @gol
> --Wno-conversion-null  -Wctor-dtor-privacy  -Wno-delete-incomplete @gol
> +-Wno-conversion-null  -Wctad-maybe-unsupported @gol
> +-Wctor-dtor-privacy  -Wno-delete-incomplete @gol
>   -Wdelete-non-virtual-dtor  -Wdeprecated-copy  -Wdeprecated-copy-dtor @gol
>   -Weffc++  -Wextra-semi  -Wno-inaccessible-base @gol
>   -Wno-inherited-variadic-ctor  -Wno-init-list-lifetime @gol
> @@ -3304,6 +3305,23 @@ void f(int *a, int b, int c) @{
>   
>   Enabled by default with @option{-std=c++20}.
>   
> +@item -Wctad-maybe-unsupported @r{(C++ and Objective-C++ only)}
> +@opindex Wctad-maybe-unsupported
> +@opindex Wno-ctad-maybe-unsupported
> +Warn when performing class template argument deduction on a type with no
> +deduction guides.  This warning will point out cases where CTAD succeeded
    ^explicitly written
> +only because the compiler synthesized the implicit deduction guides, which
> +might not be what the programmer intended.  This warning can be suppressed
> +with the following pattern:
> +
> +@smallexample
> +struct allow_ctad_t;
> +template <typename T> struct S @{
> +  S(T) @{ @}
> +@};
> +S(allow_ctad_t) -> S<void>; // will never be considered
> +@end smallexample

This should mention the style guide motivation, and clarify that the 
suppression doesn't require the "allow_ctad_t" name.

>   @item -Wctor-dtor-privacy @r{(C++ and Objective-C++ only)}
>   @opindex Wctor-dtor-privacy
>   @opindex Wno-ctor-dtor-privacy
> diff --git a/gcc/testsuite/g++.dg/warn/Wctad-maybe-unsupported.C b/gcc/testsuite/g++.dg/warn/Wctad-maybe-unsupported.C
> new file mode 100644
> index 00000000000..903e6f1f340
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/warn/Wctad-maybe-unsupported.C
> @@ -0,0 +1,88 @@
> +// Test -Wctad-maybe-unsupported.
> +// { dg-do compile { target c++17 } }
> +// { dg-options "-Wctad-maybe-unsupported" }
> +
> +template <typename T> struct Empty { };
> +
> +template <typename T>
> +struct A {
> +  A(T); // generates 'template<class T> A(T)-> A<T>'
> +  A(T, int); // generates 'template<class T> A(T, int)-> A<T>'
> +};
> +
> +// These only succeed because of the implicit guide.  That may be
> +// undesired.
> +A a1(42); // { dg-warning "may not intend to support class template argument deduction" }
> +A a2{42}; // { dg-warning "may not intend to support class template argument deduction" }
> +A a3 = {42}; // { dg-warning "may not intend to support class template argument deduction" }
> +
> +template <typename T>
> +struct B {
> +  B(T);
> +  B(T, int);
> +};
> +template <typename T> B(T, int) -> B<Empty<T>>;
> +
> +B b1(42);
> +B b2{42};
> +B b3 = {42};
> +
> +// Motivating examples from Stephan Lavavej's 2018 CppCon talk.
> +template <class T, class U>
> +struct Pair {
> +  T first;
> +  U second;
> +  explicit Pair(const T &t, const U &u) {}
> +};
> +// deduces to Pair<int, char[12]>
> +Pair p1(42, "hello world"); // { dg-warning "may not intend to support class template argument deduction" }
> +Pair p1b{42, "hello world"}; // { dg-warning "may not intend to support class template argument deduction" }
> +
> +template <class T, class U>
> +struct Pair2 {
> +  T first;
> +  U second;
> +  explicit Pair2(T t, U u) {}
> +};
> +// deduces to Pair2<int, const char*>
> +Pair2 p2(42, "hello world"); // { dg-warning "may not intend to support class template argument deduction" }
> +Pair2 p2b{42, "hello world"}; // { dg-warning "may not intend to support class template argument deduction" }
> +
> +template <class T, class U>
> +struct Pair3 {
> +  T first;
> +  U second;
> +  explicit Pair3(T const& t, U const& u) {}
> +};
> +template<class T1, class T2>
> +Pair3(T1, T2) -> Pair3<T1, T2>;
> + // deduces to Pair3<int, const char*>
> +Pair3 p3(42, "hello world");
> +static_assert(__is_same(decltype(p3), Pair3<int, const char*>));
> +
> +// Test that explicit guides suppress the warning even if they
> +// aren't used as candidates.
> +template <typename T>
> +struct C {
> +  C(T) { }
> +};
> +template <typename T>
> +explicit C(C<T> const&) -> C<void>;
> +C<int> c{42};
> +C c2 = c;
> +static_assert(__is_same(decltype(c2), C<int>));
> +
> +// Clang's suppression test.
> +struct allow_ctad_t {
> +  allow_ctad_t() = delete;
> +};
> +
> +template <typename T>
> +struct S {
> +  S(T) {}
> +};
> +S(allow_ctad_t) -> S<void>;
> +S s("abc");
> +S s2{"abc"};
> +static_assert(__is_same(decltype(s), S<const char *>));
> +static_assert(__is_same(decltype(s2), S<const char *>));
> 
> base-commit: c66c004ad610f4f02d5f065fe29c4c10f05ae13f
>
Marek Polacek Sept. 21, 2020, 7:57 p.m. UTC | #2
On Mon, Sep 21, 2020 at 01:04:27AM -0400, Jason Merrill via Gcc-patches wrote:
> On 9/19/20 5:34 PM, Marek Polacek wrote:
> > I noticed that clang++ has this CTAD warning and thought that it might
> > be useful to have it.  From clang++: "Some style guides want to allow
> > using CTAD only on types that "opt-in"; i.e. on types that are designed
> > to support it and not just types that *happen* to work with it."
> 
> That's a weird name for the warning, but I guess if that's what clang calls
> it then we shouldn't change it.

Yes.  Naming is hard, but this seem like a particularly bad name.  I think
-Wctad-maybe-unintended would have been better.  But diverging would be worse
for users.

> > +  /* If CTAD succeeded but the type doesn't have any explicit deduction
> > +     guides, this deduction might not be what the user intended.  */
> > +  if (call != error_mark_node
> > +      && !any_dguides_p
> > +      && warning (OPT_Wctad_maybe_unsupported,
> > +		  "%qT may not intend to support class template argument "
> > +		  "deduction", type))
> > +    inform (input_location, "add a deduction guide to suppress this warning");
> 
> I think you want to avoid warning for types defined in a system header
> without -Wsystem-headers.

Ack, fixed.

> > +@item -Wctad-maybe-unsupported @r{(C++ and Objective-C++ only)}
> > +@opindex Wctad-maybe-unsupported
> > +@opindex Wno-ctad-maybe-unsupported
> > +Warn when performing class template argument deduction on a type with no
> > +deduction guides.  This warning will point out cases where CTAD succeeded
>    ^explicitly written
> > +only because the compiler synthesized the implicit deduction guides, which
> > +might not be what the programmer intended.  This warning can be suppressed
> > +with the following pattern:
> > +
> > +@smallexample
> > +struct allow_ctad_t;
> > +template <typename T> struct S @{
> > +  S(T) @{ @}
> > +@};
> > +S(allow_ctad_t) -> S<void>; // will never be considered
> > +@end smallexample
> 
> This should mention the style guide motivation, and clarify that the
> suppression doesn't require the "allow_ctad_t" name.

Fixed both things.

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

-- >8 --
I noticed that clang++ has this CTAD warning and thought that it might
be useful to have it.  From clang++: "Some style guides want to allow
using CTAD only on types that "opt-in"; i.e. on types that are designed
to support it and not just types that *happen* to work with it."

So this warning warns when CTAD deduced a type, but the type does not
define any deduction guides.  In that case CTAD worked only because the
compiler synthesized the implicit deduction guides.  That might not be
intended.

It can be suppressed by adding a deduction guide that will never be
considered:

  struct allow_ctad_t;
  template <typename T> struct S { S(T) {} };
  S(allow_ctad_t) -> S<void>;

This warning is off by default.  It doesn't warn when the type comes
from a system header unless -Wsystem-headers.

gcc/c-family/ChangeLog:

	* c.opt (Wctad-maybe-unsupported): New option.

gcc/cp/ChangeLog:

	* pt.c (deduction_guides_for): Add a bool parameter.  Set it.
	(do_class_deduction): Warn when CTAD succeeds but the type doesn't
	have any explicit deduction guides.

gcc/ChangeLog:

	* doc/invoke.texi: Document -Wctad-maybe-unsupported.

gcc/testsuite/ChangeLog:

	* g++.dg/warn/Wctad-maybe-unsupported.C: New test.
	* g++.dg/warn/Wctad-maybe-unsupported2.C: New test.
	* g++.dg/warn/Wctad-maybe-unsupported3.C: New test.
	* g++.dg/warn/Wctad-maybe-unsupported.h: New file.
---
 gcc/c-family/c.opt                            |  5 ++
 gcc/cp/pt.c                                   | 28 +++++-
 gcc/doc/invoke.texi                           | 22 ++++-
 .../g++.dg/warn/Wctad-maybe-unsupported.C     | 88 +++++++++++++++++++
 .../g++.dg/warn/Wctad-maybe-unsupported.h     |  4 +
 .../g++.dg/warn/Wctad-maybe-unsupported2.C    |  6 ++
 .../g++.dg/warn/Wctad-maybe-unsupported3.C    |  6 ++
 7 files changed, 154 insertions(+), 5 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/warn/Wctad-maybe-unsupported.C
 create mode 100644 gcc/testsuite/g++.dg/warn/Wctad-maybe-unsupported.h
 create mode 100644 gcc/testsuite/g++.dg/warn/Wctad-maybe-unsupported2.C
 create mode 100644 gcc/testsuite/g++.dg/warn/Wctad-maybe-unsupported3.C

diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 7a61351bf84..da6c3e1a224 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -475,6 +475,11 @@ Wcpp
 C ObjC C++ ObjC++ CppReason(CPP_W_WARNING_DIRECTIVE)
 ; Documented in common.opt
 
+Wctad-maybe-unsupported
+C++ ObjC++ Var(warn_ctad_maybe_unsupported) Warning
+Warn when performing class template argument deduction on a type with no
+deduction guides.
+
 Wctor-dtor-privacy
 C++ ObjC++ Var(warn_ctor_dtor_privacy) Warning
 Warn when all constructors and destructors are private.
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index fe45de8d796..97d0c245f7e 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -28830,17 +28830,19 @@ static GTY((deletable)) hash_map<tree, tree_pair_p> *dguide_cache;
 
 /* Return the non-aggregate deduction guides for deducible template TMPL.  The
    aggregate candidate is added separately because it depends on the
-   initializer.  */
+   initializer.  Set ANY_DGUIDES_P if we find a non-implicit deduction
+   guide.  */
 
 static tree
-deduction_guides_for (tree tmpl, tsubst_flags_t complain)
+deduction_guides_for (tree tmpl, bool &any_dguides_p, tsubst_flags_t complain)
 {
   tree guides = NULL_TREE;
   if (DECL_ALIAS_TEMPLATE_P (tmpl))
     {
       tree under = DECL_ORIGINAL_TYPE (DECL_TEMPLATE_RESULT (tmpl));
       tree tinfo = get_template_info (under);
-      guides = deduction_guides_for (TI_TEMPLATE (tinfo), complain);
+      guides = deduction_guides_for (TI_TEMPLATE (tinfo), any_dguides_p,
+				     complain);
     }
   else
     {
@@ -28849,6 +28851,8 @@ deduction_guides_for (tree tmpl, tsubst_flags_t complain)
 				      LOOK_want::NORMAL, /*complain*/false);
       if (guides == error_mark_node)
 	guides = NULL_TREE;
+      else
+	any_dguides_p = true;
     }
 
   /* Cache the deduction guides for a template.  We also remember the result of
@@ -28974,7 +28978,8 @@ do_class_deduction (tree ptype, tree tmpl, tree init,
   if (args == NULL)
     return error_mark_node;
 
-  tree cands = deduction_guides_for (tmpl, complain);
+  bool any_dguides_p = false;
+  tree cands = deduction_guides_for (tmpl, any_dguides_p, complain);
   if (cands == error_mark_node)
     return error_mark_node;
 
@@ -29063,6 +29068,21 @@ do_class_deduction (tree ptype, tree tmpl, tree init,
 		"for copy-initialization");
     }
 
+  /* If CTAD succeeded but the type doesn't have any explicit deduction
+     guides, this deduction might not be what the user intended.  */
+  if (call != error_mark_node && !any_dguides_p)
+    {
+      tree fndecl = cp_get_callee_fndecl_nofold (call);
+      if (fndecl != NULL_TREE
+	  && (!DECL_IN_SYSTEM_HEADER (fndecl)
+	      || global_dc->dc_warn_system_headers)
+	  && warning (OPT_Wctad_maybe_unsupported,
+		      "%qT may not intend to support class template argument "
+		      "deduction", type))
+	inform (input_location, "add a deduction guide to suppress this "
+		"warning");
+    }
+
   return cp_build_qualified_type (TREE_TYPE (call), cp_type_quals (ptype));
 }
 
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 8be2b4f4de6..665c0ffc4a1 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -236,7 +236,8 @@ in the following sections.
 -Wabi-tag  -Wcatch-value  -Wcatch-value=@var{n} @gol
 -Wno-class-conversion  -Wclass-memaccess @gol
 -Wcomma-subscript  -Wconditionally-supported @gol
--Wno-conversion-null  -Wctor-dtor-privacy  -Wno-delete-incomplete @gol
+-Wno-conversion-null  -Wctad-maybe-unsupported @gol
+-Wctor-dtor-privacy  -Wno-delete-incomplete @gol
 -Wdelete-non-virtual-dtor  -Wdeprecated-copy  -Wdeprecated-copy-dtor @gol
 -Weffc++  -Wextra-semi  -Wno-inaccessible-base @gol
 -Wno-inherited-variadic-ctor  -Wno-init-list-lifetime @gol
@@ -3304,6 +3305,25 @@ void f(int *a, int b, int c) @{
 
 Enabled by default with @option{-std=c++20}.
 
+@item -Wctad-maybe-unsupported @r{(C++ and Objective-C++ only)}
+@opindex Wctad-maybe-unsupported
+@opindex Wno-ctad-maybe-unsupported
+Warn when performing class template argument deduction (CTAD) on a type with
+no explicitly written deduction guides.  This warning will point out cases
+where CTAD succeeded only because the compiler synthesized the implicit
+deduction guides, which might not be what the programmer intended.  Certain
+style guides allow CTAD only on types that specifically "opt-in"; i.e., on
+types that are designed to support CTAD.  This warning can be suppressed with
+the following pattern:
+
+@smallexample
+struct allow_ctad_t; // any name works
+template <typename T> struct S @{
+  S(T) @{ @}
+@};
+S(allow_ctad_t) -> S<void>; // guide with incomplete parameter type will never be considered
+@end smallexample
+
 @item -Wctor-dtor-privacy @r{(C++ and Objective-C++ only)}
 @opindex Wctor-dtor-privacy
 @opindex Wno-ctor-dtor-privacy
diff --git a/gcc/testsuite/g++.dg/warn/Wctad-maybe-unsupported.C b/gcc/testsuite/g++.dg/warn/Wctad-maybe-unsupported.C
new file mode 100644
index 00000000000..903e6f1f340
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wctad-maybe-unsupported.C
@@ -0,0 +1,88 @@
+// Test -Wctad-maybe-unsupported.
+// { dg-do compile { target c++17 } }
+// { dg-options "-Wctad-maybe-unsupported" }
+
+template <typename T> struct Empty { };
+
+template <typename T>
+struct A {
+  A(T); // generates 'template<class T> A(T)-> A<T>'
+  A(T, int); // generates 'template<class T> A(T, int)-> A<T>'
+};
+
+// These only succeed because of the implicit guide.  That may be
+// undesired.
+A a1(42); // { dg-warning "may not intend to support class template argument deduction" }
+A a2{42}; // { dg-warning "may not intend to support class template argument deduction" }
+A a3 = {42}; // { dg-warning "may not intend to support class template argument deduction" }
+
+template <typename T>
+struct B {
+  B(T);
+  B(T, int);
+};
+template <typename T> B(T, int) -> B<Empty<T>>;
+
+B b1(42);
+B b2{42};
+B b3 = {42};
+
+// Motivating examples from Stephan Lavavej's 2018 CppCon talk.
+template <class T, class U>
+struct Pair {
+  T first;
+  U second;
+  explicit Pair(const T &t, const U &u) {}
+};
+// deduces to Pair<int, char[12]>
+Pair p1(42, "hello world"); // { dg-warning "may not intend to support class template argument deduction" }
+Pair p1b{42, "hello world"}; // { dg-warning "may not intend to support class template argument deduction" }
+
+template <class T, class U>
+struct Pair2 {
+  T first;
+  U second;
+  explicit Pair2(T t, U u) {}
+};
+// deduces to Pair2<int, const char*>
+Pair2 p2(42, "hello world"); // { dg-warning "may not intend to support class template argument deduction" }
+Pair2 p2b{42, "hello world"}; // { dg-warning "may not intend to support class template argument deduction" }
+
+template <class T, class U>
+struct Pair3 {
+  T first;
+  U second;
+  explicit Pair3(T const& t, U const& u) {}
+};
+template<class T1, class T2>
+Pair3(T1, T2) -> Pair3<T1, T2>;
+ // deduces to Pair3<int, const char*>
+Pair3 p3(42, "hello world");
+static_assert(__is_same(decltype(p3), Pair3<int, const char*>));
+
+// Test that explicit guides suppress the warning even if they
+// aren't used as candidates.
+template <typename T>
+struct C {
+  C(T) { }
+};
+template <typename T>
+explicit C(C<T> const&) -> C<void>;
+C<int> c{42};
+C c2 = c;
+static_assert(__is_same(decltype(c2), C<int>));
+
+// Clang's suppression test.
+struct allow_ctad_t {
+  allow_ctad_t() = delete;
+};
+
+template <typename T>
+struct S {
+  S(T) {}
+};
+S(allow_ctad_t) -> S<void>;
+S s("abc");
+S s2{"abc"};
+static_assert(__is_same(decltype(s), S<const char *>));
+static_assert(__is_same(decltype(s2), S<const char *>));
diff --git a/gcc/testsuite/g++.dg/warn/Wctad-maybe-unsupported.h b/gcc/testsuite/g++.dg/warn/Wctad-maybe-unsupported.h
new file mode 100644
index 00000000000..9bb31549ddf
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wctad-maybe-unsupported.h
@@ -0,0 +1,4 @@
+#pragma GCC system_header
+
+template <typename T>
+struct A { A(T); };
diff --git a/gcc/testsuite/g++.dg/warn/Wctad-maybe-unsupported2.C b/gcc/testsuite/g++.dg/warn/Wctad-maybe-unsupported2.C
new file mode 100644
index 00000000000..ce664bd8dc8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wctad-maybe-unsupported2.C
@@ -0,0 +1,6 @@
+// { dg-do compile { target c++17 } }
+// { dg-options "-Wctad-maybe-unsupported" }
+
+#include "Wctad-maybe-unsupported.h"
+
+A a{42}; // { dg-bogus "may not intend to support class template argument deduction" }
diff --git a/gcc/testsuite/g++.dg/warn/Wctad-maybe-unsupported3.C b/gcc/testsuite/g++.dg/warn/Wctad-maybe-unsupported3.C
new file mode 100644
index 00000000000..c0ae633744b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wctad-maybe-unsupported3.C
@@ -0,0 +1,6 @@
+// { dg-do compile { target c++17 } }
+// { dg-options "-Wctad-maybe-unsupported -Wsystem-headers" }
+
+#include "Wctad-maybe-unsupported.h"
+
+A a{42}; // { dg-warning "may not intend to support class template argument deduction" }

base-commit: b6ff694e592669e7865d39a884100dd677e7ceec
Jason Merrill Sept. 21, 2020, 9:45 p.m. UTC | #3
On 9/21/20 3:57 PM, Marek Polacek wrote:
> On Mon, Sep 21, 2020 at 01:04:27AM -0400, Jason Merrill via Gcc-patches wrote:
>> On 9/19/20 5:34 PM, Marek Polacek wrote:
>>> I noticed that clang++ has this CTAD warning and thought that it might
>>> be useful to have it.  From clang++: "Some style guides want to allow
>>> using CTAD only on types that "opt-in"; i.e. on types that are designed
>>> to support it and not just types that *happen* to work with it."
>>
>> That's a weird name for the warning, but I guess if that's what clang calls
>> it then we shouldn't change it.
> 
> Yes.  Naming is hard, but this seem like a particularly bad name.  I think
> -Wctad-maybe-unintended would have been better.  But diverging would be worse
> for users.
> 
>>> +  /* If CTAD succeeded but the type doesn't have any explicit deduction
>>> +     guides, this deduction might not be what the user intended.  */
>>> +  if (call != error_mark_node
>>> +      && !any_dguides_p
>>> +      && warning (OPT_Wctad_maybe_unsupported,
>>> +		  "%qT may not intend to support class template argument "
>>> +		  "deduction", type))
>>> +    inform (input_location, "add a deduction guide to suppress this warning");
>>
>> I think you want to avoid warning for types defined in a system header
>> without -Wsystem-headers.
> 
> Ack, fixed.
> 
>>> +@item -Wctad-maybe-unsupported @r{(C++ and Objective-C++ only)}
>>> +@opindex Wctad-maybe-unsupported
>>> +@opindex Wno-ctad-maybe-unsupported
>>> +Warn when performing class template argument deduction on a type with no
>>> +deduction guides.  This warning will point out cases where CTAD succeeded
>>     ^explicitly written
>>> +only because the compiler synthesized the implicit deduction guides, which
>>> +might not be what the programmer intended.  This warning can be suppressed
>>> +with the following pattern:
>>> +
>>> +@smallexample
>>> +struct allow_ctad_t;
>>> +template <typename T> struct S @{
>>> +  S(T) @{ @}
>>> +@};
>>> +S(allow_ctad_t) -> S<void>; // will never be considered
>>> +@end smallexample
>>
>> This should mention the style guide motivation, and clarify that the
>> suppression doesn't require the "allow_ctad_t" name.
> 
> Fixed both things.
> 
> Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?

OK.

> -- >8 --
> I noticed that clang++ has this CTAD warning and thought that it might
> be useful to have it.  From clang++: "Some style guides want to allow
> using CTAD only on types that "opt-in"; i.e. on types that are designed
> to support it and not just types that *happen* to work with it."
> 
> So this warning warns when CTAD deduced a type, but the type does not
> define any deduction guides.  In that case CTAD worked only because the
> compiler synthesized the implicit deduction guides.  That might not be
> intended.
> 
> It can be suppressed by adding a deduction guide that will never be
> considered:
> 
>    struct allow_ctad_t;
>    template <typename T> struct S { S(T) {} };
>    S(allow_ctad_t) -> S<void>;
> 
> This warning is off by default.  It doesn't warn when the type comes
> from a system header unless -Wsystem-headers.
> 
> gcc/c-family/ChangeLog:
> 
> 	* c.opt (Wctad-maybe-unsupported): New option.
> 
> gcc/cp/ChangeLog:
> 
> 	* pt.c (deduction_guides_for): Add a bool parameter.  Set it.
> 	(do_class_deduction): Warn when CTAD succeeds but the type doesn't
> 	have any explicit deduction guides.
> 
> gcc/ChangeLog:
> 
> 	* doc/invoke.texi: Document -Wctad-maybe-unsupported.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* g++.dg/warn/Wctad-maybe-unsupported.C: New test.
> 	* g++.dg/warn/Wctad-maybe-unsupported2.C: New test.
> 	* g++.dg/warn/Wctad-maybe-unsupported3.C: New test.
> 	* g++.dg/warn/Wctad-maybe-unsupported.h: New file.
> ---
>   gcc/c-family/c.opt                            |  5 ++
>   gcc/cp/pt.c                                   | 28 +++++-
>   gcc/doc/invoke.texi                           | 22 ++++-
>   .../g++.dg/warn/Wctad-maybe-unsupported.C     | 88 +++++++++++++++++++
>   .../g++.dg/warn/Wctad-maybe-unsupported.h     |  4 +
>   .../g++.dg/warn/Wctad-maybe-unsupported2.C    |  6 ++
>   .../g++.dg/warn/Wctad-maybe-unsupported3.C    |  6 ++
>   7 files changed, 154 insertions(+), 5 deletions(-)
>   create mode 100644 gcc/testsuite/g++.dg/warn/Wctad-maybe-unsupported.C
>   create mode 100644 gcc/testsuite/g++.dg/warn/Wctad-maybe-unsupported.h
>   create mode 100644 gcc/testsuite/g++.dg/warn/Wctad-maybe-unsupported2.C
>   create mode 100644 gcc/testsuite/g++.dg/warn/Wctad-maybe-unsupported3.C
> 
> diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
> index 7a61351bf84..da6c3e1a224 100644
> --- a/gcc/c-family/c.opt
> +++ b/gcc/c-family/c.opt
> @@ -475,6 +475,11 @@ Wcpp
>   C ObjC C++ ObjC++ CppReason(CPP_W_WARNING_DIRECTIVE)
>   ; Documented in common.opt
>   
> +Wctad-maybe-unsupported
> +C++ ObjC++ Var(warn_ctad_maybe_unsupported) Warning
> +Warn when performing class template argument deduction on a type with no
> +deduction guides.
> +
>   Wctor-dtor-privacy
>   C++ ObjC++ Var(warn_ctor_dtor_privacy) Warning
>   Warn when all constructors and destructors are private.
> diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
> index fe45de8d796..97d0c245f7e 100644
> --- a/gcc/cp/pt.c
> +++ b/gcc/cp/pt.c
> @@ -28830,17 +28830,19 @@ static GTY((deletable)) hash_map<tree, tree_pair_p> *dguide_cache;
>   
>   /* Return the non-aggregate deduction guides for deducible template TMPL.  The
>      aggregate candidate is added separately because it depends on the
> -   initializer.  */
> +   initializer.  Set ANY_DGUIDES_P if we find a non-implicit deduction
> +   guide.  */
>   
>   static tree
> -deduction_guides_for (tree tmpl, tsubst_flags_t complain)
> +deduction_guides_for (tree tmpl, bool &any_dguides_p, tsubst_flags_t complain)
>   {
>     tree guides = NULL_TREE;
>     if (DECL_ALIAS_TEMPLATE_P (tmpl))
>       {
>         tree under = DECL_ORIGINAL_TYPE (DECL_TEMPLATE_RESULT (tmpl));
>         tree tinfo = get_template_info (under);
> -      guides = deduction_guides_for (TI_TEMPLATE (tinfo), complain);
> +      guides = deduction_guides_for (TI_TEMPLATE (tinfo), any_dguides_p,
> +				     complain);
>       }
>     else
>       {
> @@ -28849,6 +28851,8 @@ deduction_guides_for (tree tmpl, tsubst_flags_t complain)
>   				      LOOK_want::NORMAL, /*complain*/false);
>         if (guides == error_mark_node)
>   	guides = NULL_TREE;
> +      else
> +	any_dguides_p = true;
>       }
>   
>     /* Cache the deduction guides for a template.  We also remember the result of
> @@ -28974,7 +28978,8 @@ do_class_deduction (tree ptype, tree tmpl, tree init,
>     if (args == NULL)
>       return error_mark_node;
>   
> -  tree cands = deduction_guides_for (tmpl, complain);
> +  bool any_dguides_p = false;
> +  tree cands = deduction_guides_for (tmpl, any_dguides_p, complain);
>     if (cands == error_mark_node)
>       return error_mark_node;
>   
> @@ -29063,6 +29068,21 @@ do_class_deduction (tree ptype, tree tmpl, tree init,
>   		"for copy-initialization");
>       }
>   
> +  /* If CTAD succeeded but the type doesn't have any explicit deduction
> +     guides, this deduction might not be what the user intended.  */
> +  if (call != error_mark_node && !any_dguides_p)
> +    {
> +      tree fndecl = cp_get_callee_fndecl_nofold (call);
> +      if (fndecl != NULL_TREE
> +	  && (!DECL_IN_SYSTEM_HEADER (fndecl)
> +	      || global_dc->dc_warn_system_headers)
> +	  && warning (OPT_Wctad_maybe_unsupported,
> +		      "%qT may not intend to support class template argument "
> +		      "deduction", type))
> +	inform (input_location, "add a deduction guide to suppress this "
> +		"warning");
> +    }
> +
>     return cp_build_qualified_type (TREE_TYPE (call), cp_type_quals (ptype));
>   }
>   
> diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
> index 8be2b4f4de6..665c0ffc4a1 100644
> --- a/gcc/doc/invoke.texi
> +++ b/gcc/doc/invoke.texi
> @@ -236,7 +236,8 @@ in the following sections.
>   -Wabi-tag  -Wcatch-value  -Wcatch-value=@var{n} @gol
>   -Wno-class-conversion  -Wclass-memaccess @gol
>   -Wcomma-subscript  -Wconditionally-supported @gol
> --Wno-conversion-null  -Wctor-dtor-privacy  -Wno-delete-incomplete @gol
> +-Wno-conversion-null  -Wctad-maybe-unsupported @gol
> +-Wctor-dtor-privacy  -Wno-delete-incomplete @gol
>   -Wdelete-non-virtual-dtor  -Wdeprecated-copy  -Wdeprecated-copy-dtor @gol
>   -Weffc++  -Wextra-semi  -Wno-inaccessible-base @gol
>   -Wno-inherited-variadic-ctor  -Wno-init-list-lifetime @gol
> @@ -3304,6 +3305,25 @@ void f(int *a, int b, int c) @{
>   
>   Enabled by default with @option{-std=c++20}.
>   
> +@item -Wctad-maybe-unsupported @r{(C++ and Objective-C++ only)}
> +@opindex Wctad-maybe-unsupported
> +@opindex Wno-ctad-maybe-unsupported
> +Warn when performing class template argument deduction (CTAD) on a type with
> +no explicitly written deduction guides.  This warning will point out cases
> +where CTAD succeeded only because the compiler synthesized the implicit
> +deduction guides, which might not be what the programmer intended.  Certain
> +style guides allow CTAD only on types that specifically "opt-in"; i.e., on
> +types that are designed to support CTAD.  This warning can be suppressed with
> +the following pattern:
> +
> +@smallexample
> +struct allow_ctad_t; // any name works
> +template <typename T> struct S @{
> +  S(T) @{ @}
> +@};
> +S(allow_ctad_t) -> S<void>; // guide with incomplete parameter type will never be considered
> +@end smallexample
> +
>   @item -Wctor-dtor-privacy @r{(C++ and Objective-C++ only)}
>   @opindex Wctor-dtor-privacy
>   @opindex Wno-ctor-dtor-privacy
> diff --git a/gcc/testsuite/g++.dg/warn/Wctad-maybe-unsupported.C b/gcc/testsuite/g++.dg/warn/Wctad-maybe-unsupported.C
> new file mode 100644
> index 00000000000..903e6f1f340
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/warn/Wctad-maybe-unsupported.C
> @@ -0,0 +1,88 @@
> +// Test -Wctad-maybe-unsupported.
> +// { dg-do compile { target c++17 } }
> +// { dg-options "-Wctad-maybe-unsupported" }
> +
> +template <typename T> struct Empty { };
> +
> +template <typename T>
> +struct A {
> +  A(T); // generates 'template<class T> A(T)-> A<T>'
> +  A(T, int); // generates 'template<class T> A(T, int)-> A<T>'
> +};
> +
> +// These only succeed because of the implicit guide.  That may be
> +// undesired.
> +A a1(42); // { dg-warning "may not intend to support class template argument deduction" }
> +A a2{42}; // { dg-warning "may not intend to support class template argument deduction" }
> +A a3 = {42}; // { dg-warning "may not intend to support class template argument deduction" }
> +
> +template <typename T>
> +struct B {
> +  B(T);
> +  B(T, int);
> +};
> +template <typename T> B(T, int) -> B<Empty<T>>;
> +
> +B b1(42);
> +B b2{42};
> +B b3 = {42};
> +
> +// Motivating examples from Stephan Lavavej's 2018 CppCon talk.
> +template <class T, class U>
> +struct Pair {
> +  T first;
> +  U second;
> +  explicit Pair(const T &t, const U &u) {}
> +};
> +// deduces to Pair<int, char[12]>
> +Pair p1(42, "hello world"); // { dg-warning "may not intend to support class template argument deduction" }
> +Pair p1b{42, "hello world"}; // { dg-warning "may not intend to support class template argument deduction" }
> +
> +template <class T, class U>
> +struct Pair2 {
> +  T first;
> +  U second;
> +  explicit Pair2(T t, U u) {}
> +};
> +// deduces to Pair2<int, const char*>
> +Pair2 p2(42, "hello world"); // { dg-warning "may not intend to support class template argument deduction" }
> +Pair2 p2b{42, "hello world"}; // { dg-warning "may not intend to support class template argument deduction" }
> +
> +template <class T, class U>
> +struct Pair3 {
> +  T first;
> +  U second;
> +  explicit Pair3(T const& t, U const& u) {}
> +};
> +template<class T1, class T2>
> +Pair3(T1, T2) -> Pair3<T1, T2>;
> + // deduces to Pair3<int, const char*>
> +Pair3 p3(42, "hello world");
> +static_assert(__is_same(decltype(p3), Pair3<int, const char*>));
> +
> +// Test that explicit guides suppress the warning even if they
> +// aren't used as candidates.
> +template <typename T>
> +struct C {
> +  C(T) { }
> +};
> +template <typename T>
> +explicit C(C<T> const&) -> C<void>;
> +C<int> c{42};
> +C c2 = c;
> +static_assert(__is_same(decltype(c2), C<int>));
> +
> +// Clang's suppression test.
> +struct allow_ctad_t {
> +  allow_ctad_t() = delete;
> +};
> +
> +template <typename T>
> +struct S {
> +  S(T) {}
> +};
> +S(allow_ctad_t) -> S<void>;
> +S s("abc");
> +S s2{"abc"};
> +static_assert(__is_same(decltype(s), S<const char *>));
> +static_assert(__is_same(decltype(s2), S<const char *>));
> diff --git a/gcc/testsuite/g++.dg/warn/Wctad-maybe-unsupported.h b/gcc/testsuite/g++.dg/warn/Wctad-maybe-unsupported.h
> new file mode 100644
> index 00000000000..9bb31549ddf
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/warn/Wctad-maybe-unsupported.h
> @@ -0,0 +1,4 @@
> +#pragma GCC system_header
> +
> +template <typename T>
> +struct A { A(T); };
> diff --git a/gcc/testsuite/g++.dg/warn/Wctad-maybe-unsupported2.C b/gcc/testsuite/g++.dg/warn/Wctad-maybe-unsupported2.C
> new file mode 100644
> index 00000000000..ce664bd8dc8
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/warn/Wctad-maybe-unsupported2.C
> @@ -0,0 +1,6 @@
> +// { dg-do compile { target c++17 } }
> +// { dg-options "-Wctad-maybe-unsupported" }
> +
> +#include "Wctad-maybe-unsupported.h"
> +
> +A a{42}; // { dg-bogus "may not intend to support class template argument deduction" }
> diff --git a/gcc/testsuite/g++.dg/warn/Wctad-maybe-unsupported3.C b/gcc/testsuite/g++.dg/warn/Wctad-maybe-unsupported3.C
> new file mode 100644
> index 00000000000..c0ae633744b
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/warn/Wctad-maybe-unsupported3.C
> @@ -0,0 +1,6 @@
> +// { dg-do compile { target c++17 } }
> +// { dg-options "-Wctad-maybe-unsupported -Wsystem-headers" }
> +
> +#include "Wctad-maybe-unsupported.h"
> +
> +A a{42}; // { dg-warning "may not intend to support class template argument deduction" }
> 
> base-commit: b6ff694e592669e7865d39a884100dd677e7ceec
>
diff mbox series

Patch

diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index c1d8fd336e8..fef4f09f72e 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -467,6 +467,11 @@  Wcpp
 C ObjC C++ ObjC++ CppReason(CPP_W_WARNING_DIRECTIVE)
 ; Documented in common.opt
 
+Wctad-maybe-unsupported
+C++ ObjC++ Var(warn_ctad_maybe_unsupported) Warning
+Warn when performing class template argument deduction on a type with no
+deduction guides.
+
 Wctor-dtor-privacy
 C++ ObjC++ Var(warn_ctor_dtor_privacy) Warning
 Warn when all constructors and destructors are private.
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index fe45de8d796..177b762883d 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -28830,17 +28830,19 @@  static GTY((deletable)) hash_map<tree, tree_pair_p> *dguide_cache;
 
 /* Return the non-aggregate deduction guides for deducible template TMPL.  The
    aggregate candidate is added separately because it depends on the
-   initializer.  */
+   initializer.  Set ANY_DGUIDES_P if we find a non-implicit deduction
+   guide.  */
 
 static tree
-deduction_guides_for (tree tmpl, tsubst_flags_t complain)
+deduction_guides_for (tree tmpl, bool &any_dguides_p, tsubst_flags_t complain)
 {
   tree guides = NULL_TREE;
   if (DECL_ALIAS_TEMPLATE_P (tmpl))
     {
       tree under = DECL_ORIGINAL_TYPE (DECL_TEMPLATE_RESULT (tmpl));
       tree tinfo = get_template_info (under);
-      guides = deduction_guides_for (TI_TEMPLATE (tinfo), complain);
+      guides = deduction_guides_for (TI_TEMPLATE (tinfo), any_dguides_p,
+				     complain);
     }
   else
     {
@@ -28849,6 +28851,8 @@  deduction_guides_for (tree tmpl, tsubst_flags_t complain)
 				      LOOK_want::NORMAL, /*complain*/false);
       if (guides == error_mark_node)
 	guides = NULL_TREE;
+      else
+	any_dguides_p = true;
     }
 
   /* Cache the deduction guides for a template.  We also remember the result of
@@ -28974,7 +28978,8 @@  do_class_deduction (tree ptype, tree tmpl, tree init,
   if (args == NULL)
     return error_mark_node;
 
-  tree cands = deduction_guides_for (tmpl, complain);
+  bool any_dguides_p = false;
+  tree cands = deduction_guides_for (tmpl, any_dguides_p, complain);
   if (cands == error_mark_node)
     return error_mark_node;
 
@@ -29063,6 +29068,15 @@  do_class_deduction (tree ptype, tree tmpl, tree init,
 		"for copy-initialization");
     }
 
+  /* If CTAD succeeded but the type doesn't have any explicit deduction
+     guides, this deduction might not be what the user intended.  */
+  if (call != error_mark_node
+      && !any_dguides_p
+      && warning (OPT_Wctad_maybe_unsupported,
+		  "%qT may not intend to support class template argument "
+		  "deduction", type))
+    inform (input_location, "add a deduction guide to suppress this warning");
+
   return cp_build_qualified_type (TREE_TYPE (call), cp_type_quals (ptype));
 }
 
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 8086e27aefb..74864535f53 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -236,7 +236,8 @@  in the following sections.
 -Wabi-tag  -Wcatch-value  -Wcatch-value=@var{n} @gol
 -Wno-class-conversion  -Wclass-memaccess @gol
 -Wcomma-subscript  -Wconditionally-supported @gol
--Wno-conversion-null  -Wctor-dtor-privacy  -Wno-delete-incomplete @gol
+-Wno-conversion-null  -Wctad-maybe-unsupported @gol
+-Wctor-dtor-privacy  -Wno-delete-incomplete @gol
 -Wdelete-non-virtual-dtor  -Wdeprecated-copy  -Wdeprecated-copy-dtor @gol
 -Weffc++  -Wextra-semi  -Wno-inaccessible-base @gol
 -Wno-inherited-variadic-ctor  -Wno-init-list-lifetime @gol
@@ -3304,6 +3305,23 @@  void f(int *a, int b, int c) @{
 
 Enabled by default with @option{-std=c++20}.
 
+@item -Wctad-maybe-unsupported @r{(C++ and Objective-C++ only)}
+@opindex Wctad-maybe-unsupported
+@opindex Wno-ctad-maybe-unsupported
+Warn when performing class template argument deduction on a type with no
+deduction guides.  This warning will point out cases where CTAD succeeded
+only because the compiler synthesized the implicit deduction guides, which
+might not be what the programmer intended.  This warning can be suppressed
+with the following pattern:
+
+@smallexample
+struct allow_ctad_t;
+template <typename T> struct S @{
+  S(T) @{ @}
+@};
+S(allow_ctad_t) -> S<void>; // will never be considered
+@end smallexample
+
 @item -Wctor-dtor-privacy @r{(C++ and Objective-C++ only)}
 @opindex Wctor-dtor-privacy
 @opindex Wno-ctor-dtor-privacy
diff --git a/gcc/testsuite/g++.dg/warn/Wctad-maybe-unsupported.C b/gcc/testsuite/g++.dg/warn/Wctad-maybe-unsupported.C
new file mode 100644
index 00000000000..903e6f1f340
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wctad-maybe-unsupported.C
@@ -0,0 +1,88 @@ 
+// Test -Wctad-maybe-unsupported.
+// { dg-do compile { target c++17 } }
+// { dg-options "-Wctad-maybe-unsupported" }
+
+template <typename T> struct Empty { };
+
+template <typename T>
+struct A {
+  A(T); // generates 'template<class T> A(T)-> A<T>'
+  A(T, int); // generates 'template<class T> A(T, int)-> A<T>'
+};
+
+// These only succeed because of the implicit guide.  That may be
+// undesired.
+A a1(42); // { dg-warning "may not intend to support class template argument deduction" }
+A a2{42}; // { dg-warning "may not intend to support class template argument deduction" }
+A a3 = {42}; // { dg-warning "may not intend to support class template argument deduction" }
+
+template <typename T>
+struct B {
+  B(T);
+  B(T, int);
+};
+template <typename T> B(T, int) -> B<Empty<T>>;
+
+B b1(42);
+B b2{42};
+B b3 = {42};
+
+// Motivating examples from Stephan Lavavej's 2018 CppCon talk.
+template <class T, class U>
+struct Pair {
+  T first;
+  U second;
+  explicit Pair(const T &t, const U &u) {}
+};
+// deduces to Pair<int, char[12]>
+Pair p1(42, "hello world"); // { dg-warning "may not intend to support class template argument deduction" }
+Pair p1b{42, "hello world"}; // { dg-warning "may not intend to support class template argument deduction" }
+
+template <class T, class U>
+struct Pair2 {
+  T first;
+  U second;
+  explicit Pair2(T t, U u) {}
+};
+// deduces to Pair2<int, const char*>
+Pair2 p2(42, "hello world"); // { dg-warning "may not intend to support class template argument deduction" }
+Pair2 p2b{42, "hello world"}; // { dg-warning "may not intend to support class template argument deduction" }
+
+template <class T, class U>
+struct Pair3 {
+  T first;
+  U second;
+  explicit Pair3(T const& t, U const& u) {}
+};
+template<class T1, class T2>
+Pair3(T1, T2) -> Pair3<T1, T2>;
+ // deduces to Pair3<int, const char*>
+Pair3 p3(42, "hello world");
+static_assert(__is_same(decltype(p3), Pair3<int, const char*>));
+
+// Test that explicit guides suppress the warning even if they
+// aren't used as candidates.
+template <typename T>
+struct C {
+  C(T) { }
+};
+template <typename T>
+explicit C(C<T> const&) -> C<void>;
+C<int> c{42};
+C c2 = c;
+static_assert(__is_same(decltype(c2), C<int>));
+
+// Clang's suppression test.
+struct allow_ctad_t {
+  allow_ctad_t() = delete;
+};
+
+template <typename T>
+struct S {
+  S(T) {}
+};
+S(allow_ctad_t) -> S<void>;
+S s("abc");
+S s2{"abc"};
+static_assert(__is_same(decltype(s), S<const char *>));
+static_assert(__is_same(decltype(s2), S<const char *>));