diff --git a/libcxx/.clang-format b/libcxx/.clang-format --- a/libcxx/.clang-format +++ b/libcxx/.clang-format @@ -39,6 +39,7 @@ '_LIBCPP_INLINE_VISIBILITY', '_LIBCPP_CONSTEVAL', '_LIBCPP_NOALIAS', + '_LIBCPP_NO_SANITIZE', '_LIBCPP_USING_IF_EXISTS', '_LIBCPP_DEPRECATED', '_LIBCPP_DEPRECATED_IN_CXX11', diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -713,6 +713,7 @@ __utility/forward.h __utility/forward_like.h __utility/in_place.h + __utility/is_pointer_in_range.h __utility/integer_sequence.h __utility/move.h __utility/pair.h diff --git a/libcxx/include/__config b/libcxx/include/__config --- a/libcxx/include/__config +++ b/libcxx/include/__config @@ -34,6 +34,8 @@ #ifdef __cplusplus +// The attributes supported by clang are documented at https://clang.llvm.org/docs/AttributeReference.html + // _LIBCPP_VERSION represents the version of libc++, which matches the version of LLVM. // Given a LLVM release LLVM XX.YY.ZZ (e.g. LLVM 17.0.1 == 17.00.01), _LIBCPP_VERSION is // defined to XXYYZZ. @@ -1089,6 +1091,12 @@ # define _LIBCPP_PREFERRED_NAME(x) # endif +# if __has_attribute(__no_sanitize__) +# define _LIBCPP_NO_SANITIZE(...) __attribute__((__no_sanitize__(__VA_ARGS__))) +# else +# define _LIBCPP_NO_SANITIZE(...) +# endif + // We often repeat things just for handling wide characters in the library. // When wide characters are disabled, it can be useful to have a quick way of // disabling it without having to resort to #if-#endif, which has a larger diff --git a/libcxx/include/__utility/is_pointer_in_range.h b/libcxx/include/__utility/is_pointer_in_range.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__utility/is_pointer_in_range.h @@ -0,0 +1,69 @@ +//===----------------------------------------------------------------------===// +// +// 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 _LIBCPP___UTILITY_IS_POINTER_IN_RANGE_H +#define _LIBCPP___UTILITY_IS_POINTER_IN_RANGE_H + +#include <__assert> +#include <__config> +#include <__type_traits/enable_if.h> +#include <__type_traits/integral_constant.h> +#include <__type_traits/is_constant_evaluated.h> +#include <__type_traits/is_function.h> +#include <__type_traits/is_member_pointer.h> +#include <__type_traits/void_t.h> +#include <__utility/declval.h> + +_LIBCPP_BEGIN_NAMESPACE_STD + +template +struct __is_less_than_comparable : false_type {}; + +template +struct __is_less_than_comparable<_Tp, _Up, __void_t() < std::declval<_Up>())> > : true_type { +}; + +template +_LIBCPP_CONSTEXPR_SINCE_CXX14 void __check_pointer_requirements() { + static_assert(!is_function<_Tp>::value && !is_function<_Up>::value, + "__is_pointer_in_range should not be called with function pointers"); + static_assert(!is_member_pointer<_Tp>::value && !is_member_pointer<_Up>::value, + "__is_pointer_in_range should not be called with member pointers"); +} + +template ::value, int> = 0> +_LIBCPP_CONSTEXPR_SINCE_CXX14 _LIBCPP_HIDE_FROM_ABI _LIBCPP_NO_SANITIZE("address") bool __is_pointer_in_range( + const _Tp* __begin, const _Tp* __end, const _Up* __ptr) { + std::__check_pointer_requirements(); + if (__libcpp_is_constant_evaluated()) { + _LIBCPP_ASSERT(__builtin_constant_p(__begin <= __end), "__begin and __end do not form a range"); + + // If this is not a constant during constant evaluation we know that __ptr is not part of the allocation where + // [__begin, __end) is. + if (!__builtin_constant_p(__begin <= __ptr && __ptr < __end)) + return false; + } + + // Checking this for unrelated pointers is technically UB, but no compiler optimizes based on it (currently). + return __begin <= __ptr && __ptr < __end; +} + +template ::value, int> = 0> +_LIBCPP_CONSTEXPR_SINCE_CXX14 _LIBCPP_HIDE_FROM_ABI _LIBCPP_NO_SANITIZE("address") bool __is_pointer_in_range( + const _Tp* __begin, const _Tp* __end, const _Up* __ptr) { + std::__check_pointer_requirements(); + if (__libcpp_is_constant_evaluated()) + return false; + + auto __t_ptr = reinterpret_cast(__ptr); + return reinterpret_cast(__begin) <= __t_ptr && __t_ptr < reinterpret_cast(__end); +} + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP___UTILITY_IS_POINTER_IN_RANGE_H diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in --- a/libcxx/include/module.modulemap.in +++ b/libcxx/include/module.modulemap.in @@ -1590,6 +1590,7 @@ module forward_like { private header "__utility/forward_like.h" } module in_place { private header "__utility/in_place.h" } module integer_sequence { private header "__utility/integer_sequence.h" } + module is_pointer_in_range { private header "__utility/is_pointer_in_range.h" } module move { private header "__utility/move.h" } module pair { private header "__utility/pair.h" } module pair_fwd { private header "__fwd/pair.h" } diff --git a/libcxx/include/string b/libcxx/include/string --- a/libcxx/include/string +++ b/libcxx/include/string @@ -563,6 +563,7 @@ #include <__type_traits/is_allocator.h> #include <__type_traits/noexcept_move_assign_container.h> #include <__utility/auto_cast.h> +#include <__utility/is_pointer_in_range.h> #include <__utility/move.h> #include <__utility/swap.h> #include <__utility/unreachable.h> @@ -1968,14 +1969,9 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __invalidate_iterators_past(size_type); - template - _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 - bool __addr_in_range(_Tp&& __t) const { - // assume that the ranges overlap, because we can't check during constant evaluation - if (__libcpp_is_constant_evaluated()) - return true; - const volatile void *__p = std::addressof(__t); - return data() <= __p && __p <= data() + size(); + template + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 bool __addr_in_range(_Tp&& __v) const { + return std::__is_pointer_in_range(data(), data() + size(), std::addressof(__v)); } _LIBCPP_NORETURN _LIBCPP_HIDE_FROM_ABI @@ -3063,10 +3059,6 @@ size_type __cap = capacity(); if (__cap - __sz + __n1 >= __n2) { - if (__libcpp_is_constant_evaluated()) { - __grow_by_and_replace(__cap, 0, __sz, __pos, __n1, __n2, __s); - return *this; - } value_type* __p = std::__to_address(__get_pointer()); if (__n1 != __n2) { @@ -3079,7 +3071,7 @@ traits_type::move(__p + __pos + __n2, __p + __pos + __n1, __n_move); return __null_terminate_at(__p, __sz + (__n2 - __n1)); } - if (__p + __pos < __s && __s < __p + __sz) + if (std::__is_pointer_in_range(__p + __pos + 1, __p + __sz, __s)) { if (__p + __pos + __n1 <= __s) __s += __n2 - __n1; diff --git a/libcxx/test/libcxx/private_headers.verify.cpp b/libcxx/test/libcxx/private_headers.verify.cpp --- a/libcxx/test/libcxx/private_headers.verify.cpp +++ b/libcxx/test/libcxx/private_headers.verify.cpp @@ -571,6 +571,7 @@ #include <__split_buffer> // expected-error@*:* {{use of private header from outside its module: '__split_buffer'}} #include <__std_stream> // expected-error@*:* {{use of private header from outside its module: '__std_stream'}} #include <__string/char_traits.h> // expected-error@*:* {{use of private header from outside its module: '__string/char_traits.h'}} +#include <__string/constexpr_c_functions.h> // expected-error@*:* {{use of private header from outside its module: '__string/constexpr_c_functions.h'}} #include <__string/extern_template_lists.h> // expected-error@*:* {{use of private header from outside its module: '__string/extern_template_lists.h'}} #include <__thread/poll_with_backoff.h> // expected-error@*:* {{use of private header from outside its module: '__thread/poll_with_backoff.h'}} #include <__thread/timed_backoff_policy.h> // expected-error@*:* {{use of private header from outside its module: '__thread/timed_backoff_policy.h'}} @@ -725,6 +726,7 @@ #include <__utility/forward_like.h> // expected-error@*:* {{use of private header from outside its module: '__utility/forward_like.h'}} #include <__utility/in_place.h> // expected-error@*:* {{use of private header from outside its module: '__utility/in_place.h'}} #include <__utility/integer_sequence.h> // expected-error@*:* {{use of private header from outside its module: '__utility/integer_sequence.h'}} +#include <__utility/is_pointer_in_range.h> // expected-error@*:* {{use of private header from outside its module: '__utility/is_pointer_in_range.h'}} #include <__utility/move.h> // expected-error@*:* {{use of private header from outside its module: '__utility/move.h'}} #include <__utility/pair.h> // expected-error@*:* {{use of private header from outside its module: '__utility/pair.h'}} #include <__utility/piecewise_construct.h> // expected-error@*:* {{use of private header from outside its module: '__utility/piecewise_construct.h'}} diff --git a/libcxx/test/libcxx/utilities/is_pointer_in_range.pass.cpp b/libcxx/test/libcxx/utilities/is_pointer_in_range.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/utilities/is_pointer_in_range.pass.cpp @@ -0,0 +1,66 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// ADDITIONAL_COMPILE_FLAGS: -Wno-private-header + +#include <__utility/is_pointer_in_range.h> +#include + +#include "test_macros.h" + +template +TEST_CONSTEXPR_CXX14 void test_cv_quals() { + T i = 0; + U j = 0; + assert(!std::__is_pointer_in_range(&i, &i, &i)); + assert(std::__is_pointer_in_range(&i, &i + 1, &i)); + assert(!std::__is_pointer_in_range(&i, &i + 1, &j)); +} + +TEST_CONSTEXPR_CXX14 bool test() { + test_cv_quals(); + test_cv_quals(); + test_cv_quals(); + test_cv_quals(); + test_cv_quals(); + test_cv_quals(); + test_cv_quals(); + +#if TEST_STD_VER >= 20 + { + int* arr1 = new int[]{1, 2, 3, 4}; + int* arr2 = new int[]{5, 6, 7, 8}; + + assert(!std::__is_pointer_in_range(arr1, arr1 + 4, arr2)); + assert(std::__is_pointer_in_range(arr1, arr1 + 4, arr1 + 3)); + assert(!std::__is_pointer_in_range(arr1, arr1, arr1 + 3)); + + delete[] arr1; + delete[] arr2; + } +#endif + + if (!TEST_IS_CONSTANT_EVALUATED) { + int* arr1 = new int[]{1, 2, 3, 4}; + + assert(std::__is_pointer_in_range(arr1, arr1 + 4, reinterpret_cast(arr1 + 2))); + + delete[] arr1; + } + + return true; +} + +int main(int, char**) { + test(); +#if TEST_STD_VER >= 14 + static_assert(test()); +#endif + + return 0; +}