forked from OSchip/llvm-project
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.
|
||||
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 "
|
||||
"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<
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue