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

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.
This commit is contained in:
Marek Polacek 2020-09-19 16:17:42 -04:00
parent 68402af1c6
commit 7029dfa38b
7 changed files with 154 additions and 5 deletions

View File

@ -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.

View File

@ -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));
}

View File

@ -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

View File

@ -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 *>));

View File

@ -0,0 +1,4 @@
#pragma GCC system_header
template <typename T>
struct A { A(T); };

View File

@ -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" }

View File

@ -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" }