diff --git a/libcxx/docs/Status/RangesAlgorithms.csv b/libcxx/docs/Status/RangesAlgorithms.csv
--- a/libcxx/docs/Status/RangesAlgorithms.csv
+++ b/libcxx/docs/Status/RangesAlgorithms.csv
@@ -32,7 +32,7 @@
Read-only,includes,Not assigned,n/a,Not started
Read-only,is_heap,Not assigned,n/a,Not started
Read-only,is_heap_until,Not assigned,n/a,Not started
-Read-only,clamp,Not assigned,n/a,Not started
+Read-only,clamp,Nikolas Klauser,` `_,✅
Read-only,is_permutation,Not assigned,n/a,Not started
Read-only,for_each,Nikolas Klauser,n/a,✅
Read-only,for_each_n,Nikolas Klauser,n/a,✅
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -66,6 +66,7 @@
__algorithm/pop_heap.h
__algorithm/prev_permutation.h
__algorithm/push_heap.h
+ __algorithm/ranges_clamp.h
__algorithm/ranges_copy.h
__algorithm/ranges_copy_backward.h
__algorithm/ranges_copy_if.h
diff --git a/libcxx/include/__algorithm/clamp.h b/libcxx/include/__algorithm/clamp.h
--- a/libcxx/include/__algorithm/clamp.h
+++ b/libcxx/include/__algorithm/clamp.h
@@ -22,7 +22,7 @@
#if _LIBCPP_STD_VER > 14
template
_LIBCPP_NODISCARD_EXT inline
-_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR
+_LIBCPP_INLINE_VISIBILITY constexpr
const _Tp&
clamp(const _Tp& __v, const _Tp& __lo, const _Tp& __hi, _Compare __comp)
{
@@ -33,7 +33,7 @@
template
_LIBCPP_NODISCARD_EXT inline
-_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR
+_LIBCPP_INLINE_VISIBILITY constexpr
const _Tp&
clamp(const _Tp& __v, const _Tp& __lo, const _Tp& __hi)
{
diff --git a/libcxx/include/__algorithm/ranges_clamp.h b/libcxx/include/__algorithm/ranges_clamp.h
new file mode 100644
--- /dev/null
+++ b/libcxx/include/__algorithm/ranges_clamp.h
@@ -0,0 +1,64 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___ALGORITHM_RANGES_CLAMP_H
+#define _LIBCPP___ALGORITHM_RANGES_CLAMP_H
+
+#include <__assert>
+#include <__config>
+#include <__functional/identity.h>
+#include <__functional/invoke.h>
+#include <__functional/ranges_operations.h>
+#include <__iterator/concepts.h>
+#include <__iterator/projected.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+#if _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES)
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+namespace ranges {
+namespace __clamp {
+struct __fn {
+
+ template > _Comp = ranges::less>
+ _LIBCPP_HIDE_FROM_ABI constexpr
+ const _Type& operator()(const _Type& __value,
+ const _Type& __low,
+ const _Type& __high,
+ _Comp __comp = {},
+ _Proj __proj = {}) const {
+ _LIBCPP_ASSERT(bool(std::invoke(__comp, std::invoke(__proj, __high), std::invoke(__proj, __low))),
+ "Bad bounds passed to std::ranges::clamp");
+ auto&& __val = std::invoke(__proj, __value);
+ if (std::invoke(__comp, __val, std::invoke(__proj, __low)))
+ return __low;
+ else if (std::invoke(__comp, std::invoke(__proj, __high), __val))
+ return __high;
+ else
+ return __value;
+ }
+
+};
+} // namespace __clamp
+
+inline namespace __cpo {
+ inline constexpr auto clamp = __clamp::__fn{};
+} // namespace __cpo
+} // namespace ranges
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES)
+
+#endif // _LIBCPP___ALGORITHM_RANGES_CLAMP_H
diff --git a/libcxx/include/algorithm b/libcxx/include/algorithm
--- a/libcxx/include/algorithm
+++ b/libcxx/include/algorithm
@@ -265,6 +265,11 @@
indirect_unary_predicate, Proj>> Pred>
constexpr bool ranges::is_partitioned(R&& r, Pred pred, Proj proj = {}); // since C++20
+ template> Comp = ranges::less>
+ constexpr const T&
+ ranges::clamp(const T& v, const T& lo, const T& hi, Comp comp = {}, Proj proj = {}); // since C++20
+
}
constexpr bool // constexpr in C++20
@@ -981,6 +986,7 @@
#include <__algorithm/pop_heap.h>
#include <__algorithm/prev_permutation.h>
#include <__algorithm/push_heap.h>
+#include <__algorithm/ranges_clamp.h>
#include <__algorithm/ranges_copy.h>
#include <__algorithm/ranges_copy_backward.h>
#include <__algorithm/ranges_copy_if.h>
diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap
--- a/libcxx/include/module.modulemap
+++ b/libcxx/include/module.modulemap
@@ -299,6 +299,7 @@
module pop_heap { private header "__algorithm/pop_heap.h" }
module prev_permutation { private header "__algorithm/prev_permutation.h" }
module push_heap { private header "__algorithm/push_heap.h" }
+ module ranges_clamp { private header "__algorithm/ranges_clamp.h" }
module ranges_copy { private header "__algorithm/ranges_copy.h" }
module ranges_copy_backward { private header "__algorithm/ranges_copy_backward.h" }
module ranges_copy_if { private header "__algorithm/ranges_copy_if.h" }
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
@@ -103,6 +103,7 @@
#include <__algorithm/pop_heap.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/pop_heap.h'}}
#include <__algorithm/prev_permutation.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/prev_permutation.h'}}
#include <__algorithm/push_heap.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/push_heap.h'}}
+#include <__algorithm/ranges_clamp.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_clamp.h'}}
#include <__algorithm/ranges_copy.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_copy.h'}}
#include <__algorithm/ranges_copy_backward.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_copy_backward.h'}}
#include <__algorithm/ranges_copy_if.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_copy_if.h'}}
diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.clamp/ranges.clamp.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.clamp/ranges.clamp.pass.cpp
new file mode 100644
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.clamp/ranges.clamp.pass.cpp
@@ -0,0 +1,63 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-has-no-incomplete-ranges
+
+// template> Comp = ranges::less>
+// constexpr const T&
+// ranges::clamp(const T& v, const T& lo, const T& hi, Comp comp = {}, Proj proj = {});
+
+#include
+#include
+#include
+
+constexpr bool test() {
+ { // low < val < high
+ auto val = 20;
+ auto low = 10;
+ auto high = 30;
+ std::same_as decltype(auto) ret = std::ranges::clamp(val, low, high);
+ assert(ret == 20);
+ }
+ // val < low < high
+ assert(std::ranges::clamp(10, 20, 30) == 20);
+
+ // low < high < val
+ assert(std::ranges::clamp(30, 10, 20) == 20);
+
+ // check that the comparator is used
+ assert(std::ranges::clamp(10, 30, 20, std::ranges::greater{}) == 20);
+
+ { // low = val = high
+ int val = 10;
+ assert(&std::ranges::clamp(val, 10, 10) == &val);
+ }
+
+ { // check that the projection is used
+ struct S {
+ int i;
+ };
+
+ auto val = S{10};
+ auto low = S{20};
+ auto high = S{30};
+ assert(&std::ranges::clamp(val, low, high, {}, &S::i) == &low);
+ }
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}