Index: include/clang/Basic/DiagnosticGroups.td =================================================================== --- include/clang/Basic/DiagnosticGroups.td +++ include/clang/Basic/DiagnosticGroups.td @@ -1050,3 +1050,5 @@ // A group for cross translation unit static analysis related warnings. def CrossTU : DiagGroup<"ctu">; + +def ImplicitCTADUsage : DiagGroup<"implicit-ctad">; Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -2125,6 +2125,9 @@ "class template argument deduction is incompatible with C++ standards " "before C++17%select{|; for compatibility, use explicit type name %1}0">, InGroup, DefaultIgnore; +def warn_class_template_argument_deduction_no_user_defined_guides : Warning< + "using class template argument deduction for %0 that has no user-defined deduction guides" >, + InGroup, DefaultIgnore; // C++14 deduced return types def err_auto_fn_deduction_failure : Error< Index: lib/Sema/SemaInit.cpp =================================================================== --- lib/Sema/SemaInit.cpp +++ lib/Sema/SemaInit.cpp @@ -9264,9 +9264,15 @@ OverloadCandidateSet Candidates(Kind.getLocation(), OverloadCandidateSet::CSK_Normal); OverloadCandidateSet::iterator Best; + + // Record if at least one user-defined deduction guide was considered + bool HasUserDefinedDeductionGuideCandidate = false; + auto tryToResolveOverload = [&](bool OnlyListConstructors) -> OverloadingResult { Candidates.clear(OverloadCandidateSet::CSK_Normal); + HasUserDefinedDeductionGuideCandidate = false; + for (auto I = Guides.begin(), E = Guides.end(); I != E; ++I) { NamedDecl *D = (*I)->getUnderlyingDecl(); if (D->isInvalidDecl()) @@ -9278,6 +9284,9 @@ if (!GD) continue; + if (!GD->isImplicit()) + HasUserDefinedDeductionGuideCandidate = 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 +9439,15 @@ Diag(TSInfo->getTypeLoc().getBeginLoc(), diag::warn_cxx14_compat_class_template_argument_deduction) << TSInfo->getTypeLoc().getSourceRange() << 1 << DeducedType; + + + // -Wimplicit-ctad: Warn if CTAD was used on a type that does not have any + // user-defined deduction guides. + if (!HasUserDefinedDeductionGuideCandidate) { + Diag(TSInfo->getTypeLoc().getBeginLoc(), + diag::warn_class_template_argument_deduction_no_user_defined_guides) + << TemplateName; + } + return DeducedType; } Index: test/SemaCXX/cxx1z-class-template-argument-deduction.cpp =================================================================== --- test/SemaCXX/cxx1z-class-template-argument-deduction.cpp +++ test/SemaCXX/cxx1z-class-template-argument-deduction.cpp @@ -409,6 +409,61 @@ } +#pragma clang diagnostic push +#pragma clang diagnostic warning "-Wimplicit-ctad" +namespace test_implicit_ctad_warning { + +template +struct Tag {}; + +template +struct NoExplicit { + NoExplicit(T) {} + NoExplicit(T, int) {} +}; + +// expected-warning@+1 {{using class template argument deduction for 'NoExplicit' that has no user-defined deduction guides}} +NoExplicit ne(42); + +template +struct HasExplicit { + HasExplicit(U) {} + HasExplicit(U, int) {} +}; +template HasExplicit(U, int) -> HasExplicit>; + +HasExplicit he(42); + +// Motivating examples from (taken from Stephan Lavavej's 2018 Cppcon talk) +template +struct AmateurPair { + T first; U second; + explicit AmateurPair(const T& t, const U& u) {} +}; +// expected-warning@+1 {{using class template argument deduction for 'AmateurPair' that has no user-defined deduction guides}} +AmateurPair p1(42, "hello world"); // deduces to Pair + +template +struct AmateurPair2 { + T first; U second; + explicit AmateurPair2(T t, U u) {} +}; +// expected-warning@+1 {{using class template argument deduction for 'AmateurPair2' that has no user-defined deduction guides}} +AmateurPair2 p2(42, "hello world"); // deduces to Pair2 + +template +struct ProPair { + T first; U second; + explicit ProPair(T const& t, U const& u) {} +}; +template +ProPair(T1, T2) -> ProPair; +ProPair p3(42, "hello world"); // deduces to ProPair +static_assert(__is_same(decltype(p3), ProPair)); + +} +#pragma clang diagnostic pop + #else // expected-no-diagnostics