[libcxx-commits] [libcxx] a06073f - [libc++] Add a utility to check whether a range is valid (#87665)
via libcxx-commits
libcxx-commits at lists.llvm.org
Mon Apr 15 07:45:30 PDT 2024
Author: Louis Dionne
Date: 2024-04-15T10:45:26-04:00
New Revision: a06073f91e7bbbb532e68bbf6b903c2f5051f4c2
URL: https://github.com/llvm/llvm-project/commit/a06073f91e7bbbb532e68bbf6b903c2f5051f4c2
DIFF: https://github.com/llvm/llvm-project/commit/a06073f91e7bbbb532e68bbf6b903c2f5051f4c2.diff
LOG: [libc++] Add a utility to check whether a range is valid (#87665)
In the future, this utility could be made to also work with iterators,
including bounded iterators. We could also query the ASAN runtime for
this information when it's around.
Added:
libcxx/include/__utility/is_valid_range.h
libcxx/test/libcxx/utilities/is_valid_range.pass.cpp
Modified:
libcxx/include/CMakeLists.txt
libcxx/include/__utility/is_pointer_in_range.h
libcxx/include/libcxx.imp
libcxx/include/module.modulemap
Removed:
################################################################################
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 148a51a216ed4a..a2af1d9915be40 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -860,6 +860,7 @@ set(files
__utility/in_place.h
__utility/integer_sequence.h
__utility/is_pointer_in_range.h
+ __utility/is_valid_range.h
__utility/move.h
__utility/no_destroy.h
__utility/pair.h
diff --git a/libcxx/include/__utility/is_pointer_in_range.h b/libcxx/include/__utility/is_pointer_in_range.h
index 68cdfea6f94529..9eee8bf811c603 100644
--- a/libcxx/include/__utility/is_pointer_in_range.h
+++ b/libcxx/include/__utility/is_pointer_in_range.h
@@ -17,6 +17,7 @@
#include <__type_traits/is_constant_evaluated.h>
#include <__type_traits/void_t.h>
#include <__utility/declval.h>
+#include <__utility/is_valid_range.h>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
@@ -34,16 +35,15 @@ struct __is_less_than_comparable<_Tp, _Up, __void_t<decltype(std::declval<_Tp>()
template <class _Tp, class _Up, __enable_if_t<__is_less_than_comparable<const _Tp*, const _Up*>::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) {
- if (__libcpp_is_constant_evaluated()) {
- _LIBCPP_ASSERT_VALID_INPUT_RANGE(__builtin_constant_p(__begin <= __end), "__begin and __end do not form a range");
+ _LIBCPP_ASSERT_VALID_INPUT_RANGE(std::__is_valid_range(__begin, __end), "[__begin, __end) is not a valid range");
+ if (__libcpp_is_constant_evaluated()) {
// 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 !__less<>()(__ptr, __begin) && __less<>()(__ptr, __end);
}
diff --git a/libcxx/include/__utility/is_valid_range.h b/libcxx/include/__utility/is_valid_range.h
new file mode 100644
index 00000000000000..7286662dbf3092
--- /dev/null
+++ b/libcxx/include/__utility/is_valid_range.h
@@ -0,0 +1,37 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_VALID_RANGE_H
+#define _LIBCPP___UTILITY_IS_VALID_RANGE_H
+
+#include <__algorithm/comp.h>
+#include <__config>
+#include <__type_traits/is_constant_evaluated.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+template <class _Tp>
+_LIBCPP_CONSTEXPR_SINCE_CXX14 _LIBCPP_HIDE_FROM_ABI _LIBCPP_NO_SANITIZE("address") bool
+__is_valid_range(const _Tp* __first, const _Tp* __last) {
+ if (__libcpp_is_constant_evaluated()) {
+ // If this is not a constant during constant evaluation, that is because __first and __last are not
+ // part of the same allocation. If they are part of the same allocation, we must still make sure they
+ // are ordered properly.
+ return __builtin_constant_p(__first <= __last) && __first <= __last;
+ }
+
+ return !__less<>()(__last, __first);
+}
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___UTILITY_IS_VALID_RANGE_H
diff --git a/libcxx/include/libcxx.imp b/libcxx/include/libcxx.imp
index 6c77ba8343c608..8820fb8c0936f9 100644
--- a/libcxx/include/libcxx.imp
+++ b/libcxx/include/libcxx.imp
@@ -853,6 +853,7 @@
{ include: [ "<__utility/in_place.h>", "private", "<utility>", "public" ] },
{ include: [ "<__utility/integer_sequence.h>", "private", "<utility>", "public" ] },
{ include: [ "<__utility/is_pointer_in_range.h>", "private", "<utility>", "public" ] },
+ { include: [ "<__utility/is_valid_range.h>", "private", "<utility>", "public" ] },
{ include: [ "<__utility/move.h>", "private", "<utility>", "public" ] },
{ include: [ "<__utility/no_destroy.h>", "private", "<utility>", "public" ] },
{ include: [ "<__utility/pair.h>", "private", "<utility>", "public" ] },
diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap
index 7312a744ef6793..ce133e471deb70 100644
--- a/libcxx/include/module.modulemap
+++ b/libcxx/include/module.modulemap
@@ -2075,6 +2075,7 @@ module std_private_utility_forward_like [system] { header "__utility/f
module std_private_utility_in_place [system] { header "__utility/in_place.h" }
module std_private_utility_integer_sequence [system] { header "__utility/integer_sequence.h" }
module std_private_utility_is_pointer_in_range [system] { header "__utility/is_pointer_in_range.h" }
+module std_private_utility_is_valid_range [system] { header "__utility/is_valid_range.h" }
module std_private_utility_move [system] {
header "__utility/move.h"
export std_private_type_traits_is_copy_constructible
diff --git a/libcxx/test/libcxx/utilities/is_valid_range.pass.cpp b/libcxx/test/libcxx/utilities/is_valid_range.pass.cpp
new file mode 100644
index 00000000000000..345e2feeda81c7
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/is_valid_range.pass.cpp
@@ -0,0 +1,68 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 <__utility/is_valid_range.h>
+#include <cassert>
+
+#include "test_macros.h"
+
+template <class T, class TQualified>
+TEST_CONSTEXPR_CXX14 void check_type() {
+ {
+ // We need to ensure that the addresses of i and j are ordered as &i < &j for
+ // the test below to work portably, so we define them in a struct.
+ struct {
+ T i = 0;
+ T j = 0;
+ } storage;
+ assert(std::__is_valid_range(static_cast<TQualified*>(&storage.i), static_cast<TQualified*>(&storage.i)));
+ assert(std::__is_valid_range(static_cast<TQualified*>(&storage.i), static_cast<TQualified*>(&storage.i + 1)));
+
+ assert(!std::__is_valid_range(static_cast<TQualified*>(&storage.j), static_cast<TQualified*>(&storage.i)));
+ assert(!std::__is_valid_range(static_cast<TQualified*>(&storage.i + 1), static_cast<TQualified*>(&storage.i)));
+
+ // We detect this as being a valid range even though it is not really valid.
+ assert(std::__is_valid_range(static_cast<TQualified*>(&storage.i), static_cast<TQualified*>(&storage.j)));
+ }
+
+ {
+ T arr[3] = {1, 2, 3};
+ assert(std::__is_valid_range(static_cast<TQualified*>(&arr[0]), static_cast<TQualified*>(&arr[0])));
+ assert(std::__is_valid_range(static_cast<TQualified*>(&arr[0]), static_cast<TQualified*>(&arr[1])));
+ assert(std::__is_valid_range(static_cast<TQualified*>(&arr[0]), static_cast<TQualified*>(&arr[2])));
+
+ assert(!std::__is_valid_range(static_cast<TQualified*>(&arr[1]), static_cast<TQualified*>(&arr[0])));
+ assert(!std::__is_valid_range(static_cast<TQualified*>(&arr[2]), static_cast<TQualified*>(&arr[0])));
+ }
+
+#if TEST_STD_VER >= 20
+ {
+ T* arr = new int[4]{1, 2, 3, 4};
+ assert(std::__is_valid_range(static_cast<TQualified*>(arr), static_cast<TQualified*>(arr + 4)));
+ delete[] arr;
+ }
+#endif
+}
+
+TEST_CONSTEXPR_CXX14 bool test() {
+ check_type<int, int>();
+ check_type<int, int const>();
+ check_type<int, int volatile>();
+ check_type<int, int const volatile>();
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+#if TEST_STD_VER >= 14
+ static_assert(test(), "");
+#endif
+
+ return 0;
+}
More information about the libcxx-commits
mailing list