Add -Wctad-maybe-unsupported to diagnose CTAD on types with no user defined deduction guides.

Summary:
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.

This patch implements the `-Wctad-maybe-unsupported` warning, which is off by default, which warns when CTAD is used on a type that does not define any deduction guides.

The following pattern can be used to suppress the warning in cases where the type intentionally doesn't define any deduction guides:

```
struct allow_ctad_t;

template <class T>
struct TestSuppression {
  TestSuppression(T) {}
};
TestSuppression(allow_ctad_t)->TestSuppression<void>; // guides with incomplete parameter types are never considered.
```

Reviewers: rsmith, james.dennett, gromer

Reviewed By: rsmith

Subscribers: jdennett, Quuxplusone, lebedev.ri, cfe-commits

Differential Revision: https://reviews.llvm.org/D56731

llvm-svn: 351484
This commit is contained in:
Eric Fiselier 2019-01-17 21:44:24 +00:00
parent 4541be0686
commit 73b51ae160
4 changed files with 106 additions and 0 deletions

View File

@ -1050,3 +1050,5 @@ def NoDeref : DiagGroup<"noderef">;
// A group for cross translation unit static analysis related warnings.
def CrossTU : DiagGroup<"ctu">;
def CTADMaybeUnsupported : DiagGroup<"ctad-maybe-unsupported">;

View File

@ -2129,6 +2129,12 @@ def warn_cxx14_compat_class_template_argument_deduction : Warning<
"class template argument deduction is incompatible with C++ standards "
"before C++17%select{|; for compatibility, use explicit type name %1}0">,
InGroup<CXXPre17Compat>, DefaultIgnore;
def warn_ctad_maybe_unsupported : Warning<
"%0 may not intend to support class template argument deduction">,
InGroup<CTADMaybeUnsupported>, DefaultIgnore;
def note_suppress_ctad_maybe_unsupported : Note<
"add a deduction guide to suppress this warning">;
// C++14 deduced return types
def err_auto_fn_deduction_failure : Error<

View File

@ -9264,9 +9264,14 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer(
OverloadCandidateSet Candidates(Kind.getLocation(),
OverloadCandidateSet::CSK_Normal);
OverloadCandidateSet::iterator Best;
bool HasAnyDeductionGuide = false;
auto tryToResolveOverload =
[&](bool OnlyListConstructors) -> OverloadingResult {
Candidates.clear(OverloadCandidateSet::CSK_Normal);
HasAnyDeductionGuide = false;
for (auto I = Guides.begin(), E = Guides.end(); I != E; ++I) {
NamedDecl *D = (*I)->getUnderlyingDecl();
if (D->isInvalidDecl())
@ -9278,6 +9283,9 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer(
if (!GD)
continue;
if (!GD->isImplicit())
HasAnyDeductionGuide = true;
// C++ [over.match.ctor]p1: (non-list copy-initialization from non-class)
// For copy-initialization, the candidate functions are all the
// converting constructors (12.3.1) of that class.
@ -9430,5 +9438,15 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer(
Diag(TSInfo->getTypeLoc().getBeginLoc(),
diag::warn_cxx14_compat_class_template_argument_deduction)
<< TSInfo->getTypeLoc().getSourceRange() << 1 << DeducedType;
// Warn if CTAD was used on a type that does not have any user-defined
// deduction guides.
if (!HasAnyDeductionGuide) {
Diag(TSInfo->getTypeLoc().getBeginLoc(),
diag::warn_ctad_maybe_unsupported)
<< TemplateName;
Diag(Template->getLocation(), diag::note_suppress_ctad_maybe_unsupported);
}
return DeducedType;
}

View File

@ -409,6 +409,86 @@ B b(0, {});
}
#pragma clang diagnostic push
#pragma clang diagnostic warning "-Wctad-maybe-unsupported"
namespace test_implicit_ctad_warning {
template <class T>
struct Tag {};
template <class T>
struct NoExplicit { // expected-note {{add a deduction guide to suppress this warning}}
NoExplicit(T) {}
NoExplicit(T, int) {}
};
// expected-warning@+1 {{'NoExplicit' may not intend to support class template argument deduction}}
NoExplicit ne(42);
template <class U>
struct HasExplicit {
HasExplicit(U) {}
HasExplicit(U, int) {}
};
template <class U> HasExplicit(U, int) -> HasExplicit<Tag<U>>;
HasExplicit he(42);
// Motivating examples from (taken from Stephan Lavavej's 2018 Cppcon talk)
template <class T, class U>
struct AmateurPair { // expected-note {{add a deduction guide to suppress this warning}}
T first;
U second;
explicit AmateurPair(const T &t, const U &u) {}
};
// expected-warning@+1 {{'AmateurPair' may not intend to support class template argument deduction}}
AmateurPair p1(42, "hello world"); // deduces to Pair<int, char[12]>
template <class T, class U>
struct AmateurPair2 { // expected-note {{add a deduction guide to suppress this warning}}
T first;
U second;
explicit AmateurPair2(T t, U u) {}
};
// expected-warning@+1 {{'AmateurPair2' may not intend to support class template argument deduction}}
AmateurPair2 p2(42, "hello world"); // deduces to Pair2<int, const char*>
template <class T, class U>
struct ProPair {
T first; U second;
explicit ProPair(T const& t, U const& u) {}
};
template<class T1, class T2>
ProPair(T1, T2) -> ProPair<T1, T2>;
ProPair p3(42, "hello world"); // deduces to ProPair<int, const char*>
static_assert(__is_same(decltype(p3), ProPair<int, const char*>));
// Test that user-defined explicit guides suppress the warning even if they
// aren't used as candidates.
template <class T>
struct TestExplicitCtor {
TestExplicitCtor(T) {}
};
template <class T>
explicit TestExplicitCtor(TestExplicitCtor<T> const&) -> TestExplicitCtor<void>;
TestExplicitCtor<int> ce1{42};
TestExplicitCtor ce2 = ce1;
static_assert(__is_same(decltype(ce2), TestExplicitCtor<int>), "");
struct allow_ctad_t {
allow_ctad_t() = delete;
};
template <class T>
struct TestSuppression {
TestSuppression(T) {}
};
TestSuppression(allow_ctad_t)->TestSuppression<void>;
TestSuppression ta("abc");
static_assert(__is_same(decltype(ta), TestSuppression<const char *>), "");
}
#pragma clang diagnostic pop
#else
// expected-no-diagnostics