llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang-tools-extra @llvm/pr-subscribers-clang-tidy Author: Denis Mikhailov (denzor200) <details> <summary>Changes</summary> The contents of this pull request were substantially written using Cursors default model and Deepseek. I've reviewed to the best of my ability (I had 8 months expirience working on Clang Tidy). I've tested it on llvm codebase with `-DLLVM_ENABLE_PROJECTS="bolt;clang;clang-tools-extra;compiler-rt;cross-project-tests;libc;libclc;lld;lldb;mlir;polly"`. It provided 1288 warnings without fixits. See [18.log](https://github.com/user-attachments/files/25328253/18.log) based on trunk https://github.com/llvm/llvm-project/commit/038591a1c8452099cd5212b2795b325787a6e57b. Fixes: https://github.com/llvm/llvm-project/issues/86471 --- Patch is 34.13 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/181570.diff 13 Files Affected: - (modified) clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp (+3) - (modified) clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt (+1) - (added) clang-tools-extra/clang-tidy/bugprone/SmartPtrInitializationCheck.cpp (+160) - (added) clang-tools-extra/clang-tidy/bugprone/SmartPtrInitializationCheck.h (+38) - (modified) clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp (+3) - (modified) clang-tools-extra/docs/ReleaseNotes.rst (+11) - (added) clang-tools-extra/docs/clang-tidy/checks/bugprone/smart-ptr-initialization.rst (+106) - (added) clang-tools-extra/docs/clang-tidy/checks/cert/mem56-cpp.rst (+10) - (modified) clang-tools-extra/docs/clang-tidy/checks/list.rst (+1) - (added) clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/smart-ptr-initialization/std_smart_ptr.h (+101) - (added) clang-tools-extra/test/clang-tidy/checkers/bugprone/smart-ptr-initialization-array-cxx17.cpp (+116) - (added) clang-tools-extra/test/clang-tidy/checkers/bugprone/smart-ptr-initialization-array.cpp (+118) - (added) clang-tools-extra/test/clang-tidy/checkers/bugprone/smart-ptr-initialization.cpp (+160) ``````````diff diff --git a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp index 4150442c25d61..4dfeac254f1d6 100644 --- a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp @@ -74,6 +74,7 @@ #include "SignedCharMisuseCheck.h" #include "SizeofContainerCheck.h" #include "SizeofExpressionCheck.h" +#include "SmartPtrInitializationCheck.h" #include "SpuriouslyWakeUpFunctionsCheck.h" #include "StandaloneEmptyCheck.h" #include "StdNamespaceModificationCheck.h" @@ -176,6 +177,8 @@ class BugproneModule : public ClangTidyModule { "bugprone-incorrect-enable-if"); CheckFactories.registerCheck<IncorrectEnableSharedFromThisCheck>( "bugprone-incorrect-enable-shared-from-this"); + CheckFactories.registerCheck<SmartPtrInitializationCheck>( + "bugprone-smart-ptr-initialization"); CheckFactories.registerCheck<UnintendedCharOstreamOutputCheck>( "bugprone-unintended-char-ostream-output"); CheckFactories.registerCheck<ReturnConstRefFromParameterCheck>( diff --git a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt index db1256d91d311..e46edc5bafea2 100644 --- a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt @@ -37,6 +37,7 @@ add_clang_library(clangTidyBugproneModule STATIC IncorrectEnableIfCheck.cpp IncorrectEnableSharedFromThisCheck.cpp InvalidEnumDefaultInitializationCheck.cpp + SmartPtrInitializationCheck.cpp UnintendedCharOstreamOutputCheck.cpp ReturnConstRefFromParameterCheck.cpp SuspiciousStringviewDataUsageCheck.cpp diff --git a/clang-tools-extra/clang-tidy/bugprone/SmartPtrInitializationCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/SmartPtrInitializationCheck.cpp new file mode 100644 index 0000000000000..786d949903d7a --- /dev/null +++ b/clang-tools-extra/clang-tidy/bugprone/SmartPtrInitializationCheck.cpp @@ -0,0 +1,160 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "SmartPtrInitializationCheck.h" +#include "../utils/OptionsUtils.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/DeclCXX.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/ASTMatchers/ASTMatchers.h" + +using namespace clang::ast_matchers; + +namespace clang::tidy::bugprone { + +namespace { + +const auto DefaultSharedPointers = "::std::shared_ptr;::boost::shared_ptr"; +const auto DefaultUniquePointers = "::std::unique_ptr"; +const auto DefaultDefaultDeleters = "::std::default_delete"; + +} // namespace + +SmartPtrInitializationCheck::SmartPtrInitializationCheck( + StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context), + SharedPointers(utils::options::parseStringList( + Options.get("SharedPointers", DefaultSharedPointers))), + UniquePointers(utils::options::parseStringList( + Options.get("UniquePointers", DefaultUniquePointers))), + DefaultDeleters(utils::options::parseStringList( + Options.get("DefaultDeleters", DefaultDefaultDeleters))) {} + +void SmartPtrInitializationCheck::storeOptions( + ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "SharedPointers", + utils::options::serializeStringList(SharedPointers)); + Options.store(Opts, "UniquePointers", + utils::options::serializeStringList(UniquePointers)); + Options.store(Opts, "DefaultDeleters", + utils::options::serializeStringList(DefaultDeleters)); +} + +void SmartPtrInitializationCheck::registerMatchers(MatchFinder *Finder) { + const auto IsSharedPtr = hasAnyName(SharedPointers); + const auto IsUniquePtr = hasAnyName(UniquePointers); + const auto IsSmartPtr = anyOf(IsSharedPtr, IsUniquePtr); + const auto IsDefaultDeleter = hasAnyName(DefaultDeleters); + + const auto IsSharedPtrRecord = cxxRecordDecl(IsSharedPtr); + const auto IsUniquePtrRecord = cxxRecordDecl(IsUniquePtr); + const auto IsSmartPtrRecord = cxxRecordDecl(IsSmartPtr); + + auto ReleaseMethod = cxxMethodDecl(hasName("release")); + auto ResetMethod = cxxMethodDecl(hasName("reset")); + + auto ReleaseCallMatcher = cxxMemberCallExpr(callee(ReleaseMethod)); + + // Array automatically decays to pointer + auto PointerArg = expr(anyOf(hasType(pointerType()), hasType(arrayType()))) + .bind("pointer-arg"); + + // Matcher for unique_ptr types with custom deleters + auto UniquePtrWithCustomDeleter = classTemplateSpecializationDecl( + IsUniquePtr, templateArgumentCountIs(2), + hasTemplateArgument( + 1, refersToType( + unless(hasUnqualifiedDesugaredType(recordType(hasDeclaration( + classTemplateSpecializationDecl(IsDefaultDeleter)))))))); + + // Matcher for smart pointer constructors + // Exclude constructors with custom deleters: + // - shared_ptr with 2+ arguments (second is deleter) + // - unique_ptr with 2+ template args where second is not default_delete + auto HasCustomDeleter = anyOf( + allOf(hasDeclaration(cxxConstructorDecl(ofClass(IsSharedPtrRecord))), + hasArgument(1, anything())), + allOf(hasType(hasUnqualifiedDesugaredType( + recordType(hasDeclaration(UniquePtrWithCustomDeleter)))), + hasDeclaration(cxxConstructorDecl(ofClass(IsUniquePtrRecord))))); + + auto SmartPtrConstructorMatcher = + cxxConstructExpr( + hasDeclaration(cxxConstructorDecl(ofClass(IsSmartPtrRecord))), + hasArgument(0, PointerArg), unless(HasCustomDeleter), + unless(hasArgument(0, cxxNewExpr())), + unless(hasArgument(0, ReleaseCallMatcher))) + .bind("constructor"); + + // Matcher for reset() calls + // Exclude reset() calls with custom deleters: + // - shared_ptr with 2+ arguments (second is deleter) + // - unique_ptr with custom deleter type (2+ template args where second is not + // default_delete) + auto HasCustomDeleterInReset = anyOf( + allOf(on(hasType(hasUnqualifiedDesugaredType(recordType(hasDeclaration( + classTemplateSpecializationDecl(IsSharedPtr)))))), + hasArgument(1, anything())), + on(hasType(hasUnqualifiedDesugaredType( + recordType(hasDeclaration(UniquePtrWithCustomDeleter)))))); + + auto ResetCallMatcher = + cxxMemberCallExpr( + on(hasType(hasUnqualifiedDesugaredType(recordType( + hasDeclaration(classTemplateSpecializationDecl(IsSmartPtr)))))), + callee(ResetMethod), hasArgument(0, PointerArg), + unless(HasCustomDeleterInReset), unless(hasArgument(0, cxxNewExpr())), + unless(hasArgument(0, ReleaseCallMatcher))) + .bind("reset-call"); + + Finder->addMatcher(SmartPtrConstructorMatcher, this); + Finder->addMatcher(ResetCallMatcher, this); +} + +void SmartPtrInitializationCheck::check( + const MatchFinder::MatchResult &Result) { + const auto *PointerArg = Result.Nodes.getNodeAs<Expr>("pointer-arg"); + const auto *Constructor = + Result.Nodes.getNodeAs<CXXConstructExpr>("constructor"); + const auto *ResetCall = + Result.Nodes.getNodeAs<CXXMemberCallExpr>("reset-call"); + assert(PointerArg); + + const SourceLocation Loc = PointerArg->getBeginLoc(); + const CXXMethodDecl *MethodDecl = + Constructor ? Constructor->getConstructor() + : (ResetCall ? ResetCall->getMethodDecl() : nullptr); + if (!MethodDecl) + return; + + const auto *Record = MethodDecl->getParent(); + if (!Record) + return; + + const std::string TypeName = Record->getQualifiedNameAsString(); + diag(Loc, "passing a raw pointer '%0' to %1%2 may cause double deletion") + << getPointerDescription(PointerArg, *Result.Context) << TypeName + << (Constructor ? " constructor" : "::reset()"); +} + +std::string +SmartPtrInitializationCheck::getPointerDescription(const Expr *PointerExpr, + ASTContext &Context) { + std::string Description; + llvm::raw_string_ostream OS(Description); + + // Try to get a readable representation of the expression + PrintingPolicy Policy(Context.getLangOpts()); + Policy.SuppressSpecifiers = false; + Policy.SuppressTagKeyword = true; + + PointerExpr->printPretty(OS, nullptr, Policy); + return OS.str(); +} + +} // namespace clang::tidy::bugprone diff --git a/clang-tools-extra/clang-tidy/bugprone/SmartPtrInitializationCheck.h b/clang-tools-extra/clang-tidy/bugprone/SmartPtrInitializationCheck.h new file mode 100644 index 0000000000000..e8814387d0bc4 --- /dev/null +++ b/clang-tools-extra/clang-tidy/bugprone/SmartPtrInitializationCheck.h @@ -0,0 +1,38 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_SMARTPTRINITIALIZATIONCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_SMARTPTRINITIALIZATIONCHECK_H + +#include "../ClangTidyCheck.h" + +namespace clang::tidy::bugprone { + +/// Detects dangerous initialization of smart pointers with raw pointers +/// that are already owned elsewhere, which can lead to double deletion. +/// +/// For the user-facing documentation see: +/// https://clang.llvm.org/extra/clang-tidy/checks/bugprone/smart-ptr-initialization.html +class SmartPtrInitializationCheck : public ClangTidyCheck { +public: + SmartPtrInitializationCheck(StringRef Name, ClangTidyContext *Context); + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + void storeOptions(ClangTidyOptions::OptionMap &Opts) override; + +private: + std::string getPointerDescription(const Expr *PointerExpr, + ASTContext &Context); + const std::vector<StringRef> SharedPointers; + const std::vector<StringRef> UniquePointers; + const std::vector<StringRef> DefaultDeleters; +}; + +} // namespace clang::tidy::bugprone + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_SMARTPTRINITIALIZATIONCHECK_H diff --git a/clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp b/clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp index f64cb47d18b4e..32d273c374511 100644 --- a/clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp @@ -21,6 +21,7 @@ #include "../bugprone/SignalHandlerCheck.h" #include "../bugprone/SignedCharMisuseCheck.h" #include "../bugprone/SizeofExpressionCheck.h" +#include "../bugprone/SmartPtrInitializationCheck.h" #include "../bugprone/SpuriouslyWakeUpFunctionsCheck.h" #include "../bugprone/StdNamespaceModificationCheck.h" #include "../bugprone/SuspiciousMemoryComparisonCheck.h" @@ -267,6 +268,8 @@ class CERTModule : public ClangTidyModule { CheckFactories.registerCheck<misc::ThrowByValueCatchByReferenceCheck>( "cert-err61-cpp"); // MEM + CheckFactories.registerCheck<bugprone::SmartPtrInitializationCheck>( + "cert-mem56-cpp"); CheckFactories .registerCheck<bugprone::DefaultOperatorNewOnOveralignedTypeCheck>( "cert-mem57-cpp"); diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index 8cf2006172d3f..e3c3ea6cc648d 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -97,6 +97,12 @@ Improvements to clang-tidy New checks ^^^^^^^^^^ +- New :doc:`bugprone-smart-ptr-initialization + <clang-tidy/checks/bugprone/smart-ptr-initialization>` check. + + Detects dangerous initialization of smart pointers with raw pointers that are + already owned elsewhere, which can lead to double deletion. + - New :doc:`llvm-type-switch-case-types <clang-tidy/checks/llvm/type-switch-case-types>` check. @@ -137,6 +143,11 @@ New checks New check aliases ^^^^^^^^^^^^^^^^^ +- New alias :doc:`cert-mem56-cpp <clang-tidy/checks/cert/mem56-cpp>` to + :doc:`bugprone-smart-ptr-initialization + <clang-tidy/checks/bugprone/smart-ptr-initialization>` + was added. + Changes in existing checks ^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang-tools-extra/docs/clang-tidy/checks/bugprone/smart-ptr-initialization.rst b/clang-tools-extra/docs/clang-tidy/checks/bugprone/smart-ptr-initialization.rst new file mode 100644 index 0000000000000..f1cce7e97c0fd --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/bugprone/smart-ptr-initialization.rst @@ -0,0 +1,106 @@ +.. title:: clang-tidy - bugprone-smart-ptr-initialization + +bugprone-smart-ptr-initialization +================================== + +Detects dangerous initialization of smart pointers with raw pointers that are +already owned elsewhere, which can lead to double deletion. + +This check implements CERT C++ rule `MEM56-CPP. Do not store an already-owned +pointer value in an unrelated smart pointer +<https://wiki.sei.cmu.edu/confluence/display/cplusplus/MEM56-CPP.+Do+not+store+an+already-owned+pointer+value+in+an+unrelated+smart+pointer>`_. + +Examples +-------- + +The check flags cases where raw pointers that are already owned or managed +elsewhere are passed to smart pointer constructors or ``reset()`` methods: + +.. code-block:: c++ + + A& getA(); + void foo() { + // Warning: '&getA()' is already managed elsewhere + std::shared_ptr<A> a(&getA()); + } + + void bar() { + int x = 10; + // Warning: '&x' points to a local variable + std::unique_ptr<int> ptr(&x); + } + + void baz() { + std::vector<int> vec{1, 2, 3}; + std::shared_ptr<int> sp; + // Warning: '&vec[0]' is managed by the vector + sp.reset(&vec[0]); + } + +Allowed cases +------------- + +The check ignores legitimate cases: + +1. **New expressions**: Pointers from ``new`` operators are safe: + + .. code-block:: c++ + + std::unique_ptr<int> p(new int(5)); // OK + +2. **Release calls**: Pointers from ``release()`` method are transferred: + + .. code-block:: c++ + + auto p1 = std::make_unique<int>(5); + std::unique_ptr<int> p2(p1.release()); // OK + +3. **Custom deleters**: Smart pointers with custom deleters are ignored: + + .. code-block:: c++ + + void customDeleter(int* p) { delete p; } + std::unique_ptr<int, decltype(&customDeleter)> p(&getA(), customDeleter); + +4. **Null pointers**: ``nullptr`` is always safe: + + .. code-block:: c++ + + std::shared_ptr<int> p(nullptr); // OK + p.reset(nullptr); // OK + +Options +------- + +.. option:: SharedPointers + + A semicolon-separated list of (fully qualified) shared pointer type names + that should be checked. Default value is + `::std::shared_ptr;::boost::shared_ptr`. + +.. option:: UniquePointers + + A semicolon-separated list of (fully qualified) unique pointer type names + that should be checked. Default value is + `::std::unique_ptr`. + +.. option:: DefaultDeleters + + A semicolon-separated list of (fully qualified) default deleter type names. + Smart pointers with deleters matching these types are considered to use the + default deleter and are checked. Smart pointers with custom deleters are + ignored. Default value is `::std::default_delete`. + +Limitations +----------- + +This check only supports smart pointers with shared and unique ownership +semantics. Smart pointers with different semantics, such as +``boost::scoped_ptr``, cannot be used with the current version of this check. + +References +---------- + +* `CERT C++ MEM56-CPP <https://wiki.sei.cmu.edu/confluence/display/cplusplus/MEM56-CPP.+Do+not+store+an+already-owned+pointer+value+in+an+unrelated+smart+pointer>`_ +* `C++ Core Guidelines R.3: A raw pointer (a T*) is non-owning <https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#r3-a-raw-pointer-a-t-is-non-owning>`_ +* `C++ Core Guidelines R.20: Use unique_ptr or shared_ptr to represent ownership <https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#r20-use-unique_ptr-or-shared_ptr-to-represent-ownership>`_ diff --git a/clang-tools-extra/docs/clang-tidy/checks/cert/mem56-cpp.rst b/clang-tools-extra/docs/clang-tidy/checks/cert/mem56-cpp.rst new file mode 100644 index 0000000000000..c822756ad921b --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/cert/mem56-cpp.rst @@ -0,0 +1,10 @@ +.. title:: clang-tidy - cert-mem56-cpp +.. meta:: + :http-equiv=refresh: 5;URL=../bugprone/smart-ptr-initialization.html + +cert-mem56-cpp +============== + +The `cert-mem56-cpp` check is an alias, please see +:doc:`bugprone-smart-ptr-initialization <../bugprone/smart-ptr-initialization>` +for more information. diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst index 34d1c2ce0a174..a21d986e3a02b 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/list.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -144,6 +144,7 @@ Clang-Tidy Checks :doc:`bugprone-signed-char-misuse <bugprone/signed-char-misuse>`, :doc:`bugprone-sizeof-container <bugprone/sizeof-container>`, :doc:`bugprone-sizeof-expression <bugprone/sizeof-expression>`, + :doc:`bugprone-smart-ptr-initialization <bugprone/smart-ptr-initialization>`, :doc:`bugprone-spuriously-wake-up-functions <bugprone/spuriously-wake-up-functions>`, :doc:`bugprone-standalone-empty <bugprone/standalone-empty>`, "Yes" :doc:`bugprone-std-namespace-modification <bugprone/std-namespace-modification>`, diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/smart-ptr-initialization/std_smart_ptr.h b/clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/smart-ptr-initialization/std_smart_ptr.h new file mode 100644 index 0000000000000..afb9a2e95792a --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/smart-ptr-initialization/std_smart_ptr.h @@ -0,0 +1,101 @@ +namespace std { + +typedef decltype(nullptr) nullptr_t; +typedef unsigned long size_t; + +template <typename T> +struct default_delete { + void operator()(T* p) const; +}; + +template <typename T> +struct default_delete<T[]> { + void operator()(T* p) const; +}; + +template <typename T, typename Deleter = default_delete<T>> +class unique_ptr { +public: + unique_ptr(); + explicit unique_ptr(T* p); + unique_ptr(T* p, Deleter d) {} + unique_ptr(std::nullptr_t); + + T* release(); + + void reset(T* p = nullptr); + + template <typename D> + void reset(T* p, D d) {} +}; + +template <typename T, typename Deleter> +class unique_ptr<T[], Deleter> { +public: + unique_ptr(); + template <typename U> + explicit unique_ptr(U* p); + template <typename U> + unique_ptr(U* p, Deleter d) {} + unique_ptr(std::nullptr_t); + + T* release(); + + void reset(T* p = nullptr); + + template <typename D> + void reset(T* p, D d) {} +}; + +template <typename T> +class shared_ptr { +public: + shared_ptr(); + explicit shared_ptr(T* p); + template <typename Deleter> + shared_ptr(T* p, Deleter d) {} + shared_ptr(std::nullptr_t); + + T* release(); + + void reset(T* p = nullptr); + + template <typename Deleter> + void reset(T* p, Deleter d) {} +}; + +template <typename T> +class shared_ptr<T[]> { +public: + shared_ptr(); + template <typename U> + explicit shared_ptr(U* p); + template <typename U, typename Deleter> + shared_ptr(U* p, Deleter d) {} + shared_ptr(std::nullptr_t); + + T* release(); + + void reset(T* p = nullptr); + + template <typename Deleter> + void reset(T* p, Deleter d) {} +}; + +template<typename T> + struct remove_reference + { using type = T; }; + +template<typename T> + struct remove_reference<T&> + { using type = T; }; + +template<typename T> + struct remove_reference<T&&> + { using type = T; }; + +template<typename T> + constexpr typename std::remove_reference<T>::typ... [truncated] `````````` </details> https://github.com/llvm/llvm-project/pull/181570 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
