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:
parent
4541be0686
commit
73b51ae160
|
@ -1050,3 +1050,5 @@ def NoDeref : DiagGroup<"noderef">;
|
||||||
|
|
||||||
// A group for cross translation unit static analysis related warnings.
|
// A group for cross translation unit static analysis related warnings.
|
||||||
def CrossTU : DiagGroup<"ctu">;
|
def CrossTU : DiagGroup<"ctu">;
|
||||||
|
|
||||||
|
def CTADMaybeUnsupported : DiagGroup<"ctad-maybe-unsupported">;
|
||||||
|
|
|
@ -2129,6 +2129,12 @@ def warn_cxx14_compat_class_template_argument_deduction : Warning<
|
||||||
"class template argument deduction is incompatible with C++ standards "
|
"class template argument deduction is incompatible with C++ standards "
|
||||||
"before C++17%select{|; for compatibility, use explicit type name %1}0">,
|
"before C++17%select{|; for compatibility, use explicit type name %1}0">,
|
||||||
InGroup<CXXPre17Compat>, DefaultIgnore;
|
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
|
// C++14 deduced return types
|
||||||
def err_auto_fn_deduction_failure : Error<
|
def err_auto_fn_deduction_failure : Error<
|
||||||
|
|
|
@ -9264,9 +9264,14 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer(
|
||||||
OverloadCandidateSet Candidates(Kind.getLocation(),
|
OverloadCandidateSet Candidates(Kind.getLocation(),
|
||||||
OverloadCandidateSet::CSK_Normal);
|
OverloadCandidateSet::CSK_Normal);
|
||||||
OverloadCandidateSet::iterator Best;
|
OverloadCandidateSet::iterator Best;
|
||||||
|
|
||||||
|
bool HasAnyDeductionGuide = false;
|
||||||
|
|
||||||
auto tryToResolveOverload =
|
auto tryToResolveOverload =
|
||||||
[&](bool OnlyListConstructors) -> OverloadingResult {
|
[&](bool OnlyListConstructors) -> OverloadingResult {
|
||||||
Candidates.clear(OverloadCandidateSet::CSK_Normal);
|
Candidates.clear(OverloadCandidateSet::CSK_Normal);
|
||||||
|
HasAnyDeductionGuide = false;
|
||||||
|
|
||||||
for (auto I = Guides.begin(), E = Guides.end(); I != E; ++I) {
|
for (auto I = Guides.begin(), E = Guides.end(); I != E; ++I) {
|
||||||
NamedDecl *D = (*I)->getUnderlyingDecl();
|
NamedDecl *D = (*I)->getUnderlyingDecl();
|
||||||
if (D->isInvalidDecl())
|
if (D->isInvalidDecl())
|
||||||
|
@ -9278,6 +9283,9 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer(
|
||||||
if (!GD)
|
if (!GD)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
if (!GD->isImplicit())
|
||||||
|
HasAnyDeductionGuide = true;
|
||||||
|
|
||||||
// C++ [over.match.ctor]p1: (non-list copy-initialization from non-class)
|
// C++ [over.match.ctor]p1: (non-list copy-initialization from non-class)
|
||||||
// For copy-initialization, the candidate functions are all the
|
// For copy-initialization, the candidate functions are all the
|
||||||
// converting constructors (12.3.1) of that class.
|
// converting constructors (12.3.1) of that class.
|
||||||
|
@ -9430,5 +9438,15 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer(
|
||||||
Diag(TSInfo->getTypeLoc().getBeginLoc(),
|
Diag(TSInfo->getTypeLoc().getBeginLoc(),
|
||||||
diag::warn_cxx14_compat_class_template_argument_deduction)
|
diag::warn_cxx14_compat_class_template_argument_deduction)
|
||||||
<< TSInfo->getTypeLoc().getSourceRange() << 1 << DeducedType;
|
<< 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;
|
return DeducedType;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
#else
|
||||||
|
|
||||||
// expected-no-diagnostics
|
// expected-no-diagnostics
|
||||||
|
|
Loading…
Reference in New Issue