rZhBoYao created this revision. rZhBoYao added reviewers: EricWF, rsmith. Herald added a project: All. rZhBoYao requested review of this revision. Herald added a project: clang. Herald added a subscriber: cfe-commits.
1. '-Wctad-copy-not-wrap' warns when copy deduction candidate is selected in CTAD, and the initialization is a direct-initialization. The warning is suppressed when the class template is in a system header and has at least one explicit deduction guide. 2. Add a group '-Wctad' which incldues '-Wctad-copy-not-wrap' and '-Wctad-maybe-unsupported'. Fixes #63288 Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D155475 Files: clang/docs/ReleaseNotes.rst clang/include/clang/Basic/DiagnosticGroups.td clang/include/clang/Basic/DiagnosticSemaKinds.td clang/lib/Sema/SemaInit.cpp clang/test/SemaTemplate/ctad.cpp
Index: clang/test/SemaTemplate/ctad.cpp =================================================================== --- clang/test/SemaTemplate/ctad.cpp +++ clang/test/SemaTemplate/ctad.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -std=c++17 -verify %s +// RUN: %clang_cc1 -std=c++23 -verify %s namespace pr41427 { template <typename T> class A { @@ -44,3 +44,32 @@ }; D z = {Z(), {}}; } + +namespace warn_ctad_copy { +#pragma clang diagnostic push +#pragma clang diagnostic warning "-Wctad-copy-not-wrap" +// like std::revrse_iterator, the motivating example +template <typename T> struct CTAD { + template <typename From> + requires(!__is_same(From, T) && __is_convertible_to(From, T)) + CTAD(const CTAD<From>&) {} // #1 + CTAD(T) {} +}; +CTAD(void) -> CTAD<void>; // does not suppress -Wctad-copy-not-wrap + +CTAD<int>& fc1(); +CTAD<double> fc2(), + c1 = fc1(); // uses #1 +CTAD c2 = fc2(), // OK, uses copy-initialization + c3 = CTAD(4.2), // OK, copy deduction candidate not selected + c4{fc2()}; // test prvalue expression +// expected-warning@-1{{move-constructing not wrapping a 'CTAD<double>' \ +(aka 'warn_ctad_copy::CTAD<double>'), use copy-list-initialization to suppress this warning}} +CTAD c5 = CTAD{fc1()}, // test lvalue expression +// expected-warning@-1{{copy-constructing not wrapping a 'CTAD<int>' \ +(aka 'warn_ctad_copy::CTAD<int>'), use copy-list-initialization to suppress this warning}} + c6 = CTAD((CTAD<int>&&)c5); // test xvalue expression +// expected-warning@-1{{move-constructing not wrapping a 'CTAD<int>' \ +(aka 'warn_ctad_copy::CTAD<int>'), use copy-initialization to suppress this warning}} +#pragma clang diagnostic pop +} // namespace warn_ctad_copy Index: clang/lib/Sema/SemaInit.cpp =================================================================== --- clang/lib/Sema/SemaInit.cpp +++ clang/lib/Sema/SemaInit.cpp @@ -19,6 +19,7 @@ #include "clang/Basic/CharInfo.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/TargetInfo.h" +#include "clang/Lex/Preprocessor.h" #include "clang/Sema/Designator.h" #include "clang/Sema/EnterExpressionEvaluationContext.h" #include "clang/Sema/Initialization.h" @@ -10908,6 +10909,17 @@ diag::warn_cxx14_compat_class_template_argument_deduction) << TSInfo->getTypeLoc().getSourceRange() << 1 << DeducedType; + // Warn if the copy deduction candidate is selected in direct-initialization. + auto DiagnoseCTADCopy = [&] { + if (auto IK = Kind.getKind(); + IK <= InitializationKind::IK_DirectList && + cast<CXXDeductionGuideDecl>(Best->Function) + ->getDeductionCandidateKind() == DeductionCandidate::Copy) { + Diag(Kind.getLocation(), diag::warn_ctad_copy_not_wrap) + << Inits.front()->isLValue() << DeducedType << !ListInit; + } + }; + // Warn if CTAD was used on a type that does not have any user-defined // deduction guides. if (!FoundDeductionGuide) { @@ -10915,6 +10927,9 @@ diag::warn_ctad_maybe_unsupported) << TemplateName; Diag(Template->getLocation(), diag::note_suppress_ctad_maybe_unsupported); + DiagnoseCTADCopy(); + } else if (!PP.getSourceManager().isInSystemHeader(Template->getLocation())) { + DiagnoseCTADCopy(); } return DeducedType; Index: clang/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticSemaKinds.td +++ clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -2488,6 +2488,10 @@ InGroup<CTADMaybeUnsupported>, DefaultIgnore; def note_suppress_ctad_maybe_unsupported : Note< "add a deduction guide to suppress this warning">; +def warn_ctad_copy_not_wrap : Warning< + "%select{move|copy}0-constructing not wrapping a %1, " + "use copy-%select{list-|}2initialization to suppress this warning">, + InGroup<CTADCopyNotWrap>, DefaultIgnore; // C++14 deduced return types Index: clang/include/clang/Basic/DiagnosticGroups.td =================================================================== --- clang/include/clang/Basic/DiagnosticGroups.td +++ clang/include/clang/Basic/DiagnosticGroups.td @@ -1380,6 +1380,9 @@ def CrossTU : DiagGroup<"ctu">; def CTADMaybeUnsupported : DiagGroup<"ctad-maybe-unsupported">; +def CTADCopyNotWrap : DiagGroup<"ctad-copy-not-wrap">; +def CTAD : DiagGroup<"ctad", [CTADMaybeUnsupported, + CTADCopyNotWrap]>; def FortifySource : DiagGroup<"fortify-source">; Index: clang/docs/ReleaseNotes.rst =================================================================== --- clang/docs/ReleaseNotes.rst +++ clang/docs/ReleaseNotes.rst @@ -421,6 +421,20 @@ - ``-Wformat`` will no longer suggest a no-op fix-it for fixing scoped enum format warnings. Instead, it will suggest casting the enum object to the type specified in the format string. +- Clang now warns when the class template argument deduction deduces the type to be the same as the initializer + of a direct-initialization. Turn this on with ``-Wctad-copy-not-wrap``. If the class template is in a system header + and a reachable explicit deduction guide is present, the warning is suppressed. + (`#63288 <https://github.com/llvm/llvm-project/issues/63288>`_). + + .. code-block:: c++ + + auto vec = std::vector{42}; + auto v = std::vector{vec}; + // no warning, user-defined deduction guides exist and the template is in a system header + + std::reverse_iterator it1 = v.rbegin(); // no warning, copy-initialization + auto it2 = std::reverse_iterator{v.rbegin()}; // warns, the intent might be the next line + auto it3 = std::reverse_iterator<decltype(v.rbegin())>(v.rbegin()); // no warning, no CTAD Bug Fixes in This Version -------------------------
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits