From 934650b24fbfea88c34f735b9cfd0d86e0f608d9 Mon Sep 17 00:00:00 2001 From: Nikolas Klauser Date: Wed, 11 Jan 2023 00:01:37 +0100 Subject: [PATCH] [libc++] Add [[clang::lifetimebound]] to min/max algorithms Reviewed By: Mordante, #libc Spies: libcxx-commits Differential Revision: https://reviews.llvm.org/D142608 --- libcxx/include/__algorithm/max.h | 4 +- libcxx/include/__algorithm/min.h | 4 +- libcxx/include/__algorithm/minmax.h | 4 +- libcxx/include/__algorithm/ranges_max.h | 5 +- libcxx/include/__algorithm/ranges_min.h | 5 +- libcxx/include/__algorithm/ranges_minmax.h | 5 +- libcxx/include/module.modulemap.in | 5 +- .../libcxx/algorithms/lifetimebound.verify.cpp | 71 ++++++++++++++++++++++ 8 files changed, 93 insertions(+), 10 deletions(-) create mode 100644 libcxx/test/libcxx/algorithms/lifetimebound.verify.cpp diff --git a/libcxx/include/__algorithm/max.h b/libcxx/include/__algorithm/max.h index a08a3fc5..97f61f2 100644 --- a/libcxx/include/__algorithm/max.h +++ b/libcxx/include/__algorithm/max.h @@ -28,7 +28,7 @@ template _LIBCPP_NODISCARD_EXT inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_SINCE_CXX14 const _Tp& -max(const _Tp& __a, const _Tp& __b, _Compare __comp) +max(_LIBCPP_LIFETIMEBOUND const _Tp& __a, _LIBCPP_LIFETIMEBOUND const _Tp& __b, _Compare __comp) { return __comp(__a, __b) ? __b : __a; } @@ -37,7 +37,7 @@ template _LIBCPP_NODISCARD_EXT inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_SINCE_CXX14 const _Tp& -max(const _Tp& __a, const _Tp& __b) +max(_LIBCPP_LIFETIMEBOUND const _Tp& __a, _LIBCPP_LIFETIMEBOUND const _Tp& __b) { return _VSTD::max(__a, __b, __less<_Tp>()); } diff --git a/libcxx/include/__algorithm/min.h b/libcxx/include/__algorithm/min.h index 2882485..d073a16 100644 --- a/libcxx/include/__algorithm/min.h +++ b/libcxx/include/__algorithm/min.h @@ -28,7 +28,7 @@ template _LIBCPP_NODISCARD_EXT inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_SINCE_CXX14 const _Tp& -min(const _Tp& __a, const _Tp& __b, _Compare __comp) +min(_LIBCPP_LIFETIMEBOUND const _Tp& __a, _LIBCPP_LIFETIMEBOUND const _Tp& __b, _Compare __comp) { return __comp(__b, __a) ? __b : __a; } @@ -37,7 +37,7 @@ template _LIBCPP_NODISCARD_EXT inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_SINCE_CXX14 const _Tp& -min(const _Tp& __a, const _Tp& __b) +min(_LIBCPP_LIFETIMEBOUND const _Tp& __a, _LIBCPP_LIFETIMEBOUND const _Tp& __b) { return _VSTD::min(__a, __b, __less<_Tp>()); } diff --git a/libcxx/include/__algorithm/minmax.h b/libcxx/include/__algorithm/minmax.h index 6ef0a77..f486de2e 100644 --- a/libcxx/include/__algorithm/minmax.h +++ b/libcxx/include/__algorithm/minmax.h @@ -27,7 +27,7 @@ template _LIBCPP_NODISCARD_EXT inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_SINCE_CXX14 pair -minmax(const _Tp& __a, const _Tp& __b, _Compare __comp) +minmax(_LIBCPP_LIFETIMEBOUND const _Tp& __a, _LIBCPP_LIFETIMEBOUND const _Tp& __b, _Compare __comp) { return __comp(__b, __a) ? pair(__b, __a) : pair(__a, __b); @@ -37,7 +37,7 @@ template _LIBCPP_NODISCARD_EXT inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_SINCE_CXX14 pair -minmax(const _Tp& __a, const _Tp& __b) +minmax(_LIBCPP_LIFETIMEBOUND const _Tp& __a, _LIBCPP_LIFETIMEBOUND const _Tp& __b) { return std::minmax(__a, __b, __less<_Tp>()); } diff --git a/libcxx/include/__algorithm/ranges_max.h b/libcxx/include/__algorithm/ranges_max.h index 55aef99..2a16159 100644 --- a/libcxx/include/__algorithm/ranges_max.h +++ b/libcxx/include/__algorithm/ranges_max.h @@ -40,7 +40,10 @@ struct __fn { template > _Comp = ranges::less> _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr - const _Tp& operator()(const _Tp& __a, const _Tp& __b, _Comp __comp = {}, _Proj __proj = {}) const { + const _Tp& operator()(_LIBCPP_LIFETIMEBOUND const _Tp& __a, + _LIBCPP_LIFETIMEBOUND const _Tp& __b, + _Comp __comp = {}, + _Proj __proj = {}) const { return std::invoke(__comp, std::invoke(__proj, __a), std::invoke(__proj, __b)) ? __b : __a; } diff --git a/libcxx/include/__algorithm/ranges_min.h b/libcxx/include/__algorithm/ranges_min.h index 0e31f57..d7a0227 100644 --- a/libcxx/include/__algorithm/ranges_min.h +++ b/libcxx/include/__algorithm/ranges_min.h @@ -39,7 +39,10 @@ struct __fn { template > _Comp = ranges::less> _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr - const _Tp& operator()(const _Tp& __a, const _Tp& __b, _Comp __comp = {}, _Proj __proj = {}) const { + const _Tp& operator()(_LIBCPP_LIFETIMEBOUND const _Tp& __a, + _LIBCPP_LIFETIMEBOUND const _Tp& __b, + _Comp __comp = {}, + _Proj __proj = {}) const { return std::invoke(__comp, std::invoke(__proj, __b), std::invoke(__proj, __a)) ? __b : __a; } diff --git a/libcxx/include/__algorithm/ranges_minmax.h b/libcxx/include/__algorithm/ranges_minmax.h index f82e005..ea6e54c 100644 --- a/libcxx/include/__algorithm/ranges_minmax.h +++ b/libcxx/include/__algorithm/ranges_minmax.h @@ -46,7 +46,10 @@ struct __fn { template > _Comp = ranges::less> _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr ranges::minmax_result - operator()(const _Type& __a, const _Type& __b, _Comp __comp = {}, _Proj __proj = {}) const { + operator()(_LIBCPP_LIFETIMEBOUND const _Type& __a, + _LIBCPP_LIFETIMEBOUND const _Type& __b, + _Comp __comp = {}, + _Proj __proj = {}) const { if (std::invoke(__comp, std::invoke(__proj, __b), std::invoke(__proj, __a))) return {__b, __a}; return {__a, __b}; diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in index a128483..cd86006 100644 --- a/libcxx/include/module.modulemap.in +++ b/libcxx/include/module.modulemap.in @@ -298,7 +298,10 @@ module std [system] { module min { private header "__algorithm/min.h" } module min_element { private header "__algorithm/min_element.h" } module min_max_result { private header "__algorithm/min_max_result.h" } - module minmax { private header "__algorithm/minmax.h" } + module minmax { + private header "__algorithm/minmax.h" + export * + } module minmax_element { private header "__algorithm/minmax_element.h" } module mismatch { private header "__algorithm/mismatch.h" } module move { private header "__algorithm/move.h" } diff --git a/libcxx/test/libcxx/algorithms/lifetimebound.verify.cpp b/libcxx/test/libcxx/algorithms/lifetimebound.verify.cpp new file mode 100644 index 0000000..b16ecfb --- /dev/null +++ b/libcxx/test/libcxx/algorithms/lifetimebound.verify.cpp @@ -0,0 +1,71 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03 +// ADDITIONAL_COMPILE_FLAGS: -Wno-pessimizing-move -Wno-unused-variable + +#include + +#include "test_macros.h" + +struct Comp { + template + bool operator()(T, U) { + return false; + } +}; + +void func() { + int i = 0; + { + auto&& v1 = std::min(0, i); // expected-warning {{temporary bound to local reference 'v1' will be destroyed at the end of the full-expression}} + auto&& v2 = std::min(i, 0); // expected-warning {{temporary bound to local reference 'v2' will be destroyed at the end of the full-expression}} + auto&& v3 = std::min(0, i, Comp{}); // expected-warning {{temporary bound to local reference 'v3' will be destroyed at the end of the full-expression}} + auto&& v4 = std::min(i, 0, Comp{}); // expected-warning {{temporary bound to local reference 'v4' will be destroyed at the end of the full-expression}} + } + { + auto&& v1 = std::max(0, i); // expected-warning {{temporary bound to local reference 'v1' will be destroyed at the end of the full-expression}} + auto&& v2 = std::max(i, 0); // expected-warning {{temporary bound to local reference 'v2' will be destroyed at the end of the full-expression}} + auto&& v3 = std::max(0, i, Comp{}); // expected-warning {{temporary bound to local reference 'v3' will be destroyed at the end of the full-expression}} + auto&& v4 = std::max(i, 0, Comp{}); // expected-warning {{temporary bound to local reference 'v4' will be destroyed at the end of the full-expression}} + } + { + auto&& v1 = std::minmax(0, i); // expected-warning {{temporary bound to local reference 'v1' will be destroyed at the end of the full-expression}} + auto&& v2 = std::minmax(i, 0); // expected-warning {{temporary bound to local reference 'v2' will be destroyed at the end of the full-expression}} + auto&& v3 = std::minmax(0, i, Comp{}); // expected-warning {{temporary bound to local reference 'v3' will be destroyed at the end of the full-expression}} + auto&& v4 = std::minmax(i, 0, Comp{}); // expected-warning {{temporary bound to local reference 'v4' will be destroyed at the end of the full-expression}} + auto v5 = std::minmax(0, i); // expected-warning {{temporary whose address is used as value of local variable 'v5' will be destroyed at the end of the full-expression}} + auto v6 = std::minmax(i, 0); // expected-warning {{temporary whose address is used as value of local variable 'v6' will be destroyed at the end of the full-expression}} + auto v7 = std::minmax(0, i, Comp{}); // expected-warning {{temporary whose address is used as value of local variable 'v7' will be destroyed at the end of the full-expression}} + auto v8 = std::minmax(i, 0, Comp{}); // expected-warning {{temporary whose address is used as value of local variable 'v8' will be destroyed at the end of the full-expression}} + } +#if TEST_STD_VER >= 20 + { + auto&& v1 = std::ranges::min(0, i); // expected-warning {{temporary bound to local reference 'v1' will be destroyed at the end of the full-expression}} + auto&& v2 = std::ranges::min(i, 0); // expected-warning {{temporary bound to local reference 'v2' will be destroyed at the end of the full-expression}} + auto&& v3 = std::ranges::min(0, i, Comp{}); // expected-warning {{temporary bound to local reference 'v3' will be destroyed at the end of the full-expression}} + auto&& v4 = std::ranges::min(i, 0, Comp{}); // expected-warning {{temporary bound to local reference 'v4' will be destroyed at the end of the full-expression}} + } + { + auto&& v1 = std::ranges::max(0, i); // expected-warning {{temporary bound to local reference 'v1' will be destroyed at the end of the full-expression}} + auto&& v2 = std::ranges::max(i, 0); // expected-warning {{temporary bound to local reference 'v2' will be destroyed at the end of the full-expression}} + auto&& v3 = std::ranges::max(0, i, Comp{}); // expected-warning {{temporary bound to local reference 'v3' will be destroyed at the end of the full-expression}} + auto&& v4 = std::ranges::max(i, 0, Comp{}); // expected-warning {{temporary bound to local reference 'v4' will be destroyed at the end of the full-expression}} + } + { + auto&& v1 = std::ranges::minmax(0, i); // expected-warning {{temporary bound to local reference 'v1' will be destroyed at the end of the full-expression}} + auto&& v2 = std::ranges::minmax(i, 0); // expected-warning {{temporary bound to local reference 'v2' will be destroyed at the end of the full-expression}} + auto&& v3 = std::ranges::minmax(0, i, Comp{}); // expected-warning {{temporary bound to local reference 'v3' will be destroyed at the end of the full-expression}} + auto&& v4 = std::ranges::minmax(i, 0, Comp{}); // expected-warning {{temporary bound to local reference 'v4' will be destroyed at the end of the full-expression}} + auto v5 = std::ranges::minmax(0, i); // expected-warning {{temporary whose address is used as value of local variable 'v5' will be destroyed at the end of the full-expression}} + auto v6 = std::ranges::minmax(i, 0); // expected-warning {{temporary whose address is used as value of local variable 'v6' will be destroyed at the end of the full-expression}} + auto v7 = std::ranges::minmax(0, i, Comp{}); // expected-warning {{temporary whose address is used as value of local variable 'v7' will be destroyed at the end of the full-expression}} + auto v8 = std::ranges::minmax(i, 0, Comp{}); // expected-warning {{temporary whose address is used as value of local variable 'v8' will be destroyed at the end of the full-expression}} + } +#endif // TEST_STD_VER >= 20 +} -- 2.7.4