diff --git a/stl/debugger/STL.natvis b/stl/debugger/STL.natvis
index b9f0787bff6..eee583f922f 100644
--- a/stl/debugger/STL.natvis
+++ b/stl/debugger/STL.natvis
@@ -1404,6 +1404,14 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+ basic_const_iterator {_Current}
+
+ - _Current
+
+
+
+
-i*{-_Val[1]}
{_Val[0]}-i*{-_Val[1]}
diff --git a/stl/inc/xutility b/stl/inc/xutility
index ba8ed1e070b..59150ecce96 100644
--- a/stl/inc/xutility
+++ b/stl/inc/xutility
@@ -1691,6 +1691,345 @@ _NODISCARD constexpr const _Elem* data(initializer_list<_Elem> _Ilist) noexcept
template
concept _Not_same_as = (!same_as, remove_cvref_t<_Ty2>>);
+#if _HAS_CXX23
+_EXPORT_STD template
+using iter_const_reference_t = common_reference_t&&, iter_reference_t<_Ty>>;
+
+template
+concept _Constant_iterator = input_iterator<_Ty> && same_as, iter_reference_t<_Ty>>;
+
+_EXPORT_STD template
+class basic_const_iterator;
+
+_EXPORT_STD template
+using const_iterator = conditional_t<_Constant_iterator<_Iter>, _Iter, basic_const_iterator<_Iter>>;
+
+template
+struct _Const_sentinel {
+ using type = _Sent;
+};
+
+template
+struct _Const_sentinel<_Sent> {
+ using type = const_iterator<_Sent>;
+};
+
+_EXPORT_STD template
+using const_sentinel = typename _Const_sentinel<_Sent>::type;
+
+// clang-format off
+template
+concept _Not_a_const_iterator = !_Is_specialization_v<_Ty, basic_const_iterator>;
+// clang-format on
+
+template
+struct _Basic_const_iterator_category {};
+
+template
+struct _Basic_const_iterator_category<_Iter> {
+ using iterator_category = typename iterator_traits<_Iter>::iterator_category;
+};
+
+// TRANSITION, LLVM-55945: These are distinct concepts as a workaround
+template
+concept _Bci_order =
+ _Not_same_as<_Ty, basic_const_iterator<_Iter>> && random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Ty>;
+
+template
+concept _Bci_order_3way = _Bci_order<_Ty, _Iter> && three_way_comparable_with<_Iter, _Ty>;
+
+template
+concept _Not_bci_order =
+ _Not_a_const_iterator<_Ty> && random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Ty>;
+
+_EXPORT_STD template
+class basic_const_iterator : public _Basic_const_iterator_category<_Iter> {
+private:
+ /* [[no_unique_address]] */ _Iter _Current{};
+
+ using _Reference = iter_const_reference_t<_Iter>;
+
+ _NODISCARD static _CONSTEVAL auto _Get_iter_concept() noexcept {
+ if constexpr (contiguous_iterator<_Iter>) {
+ return contiguous_iterator_tag{};
+ } else if constexpr (random_access_iterator<_Iter>) {
+ return random_access_iterator_tag{};
+ } else if constexpr (bidirectional_iterator<_Iter>) {
+ return bidirectional_iterator_tag{};
+ } else if constexpr (forward_iterator<_Iter>) {
+ return forward_iterator_tag{};
+ } else {
+ return input_iterator_tag{};
+ }
+ }
+
+public:
+ using iterator_concept = decltype(_Get_iter_concept());
+ using value_type = iter_value_t<_Iter>;
+ using difference_type = iter_difference_t<_Iter>;
+
+ // clang-format off
+ basic_const_iterator() requires default_initializable<_Iter> = default;
+ // clang-format on
+
+ constexpr basic_const_iterator(_Iter _Current_) noexcept(is_nothrow_move_constructible_v<_Iter>) // strengthened
+ : _Current(_STD move(_Current_)) {}
+
+ template _Other>
+ constexpr basic_const_iterator(basic_const_iterator<_Other> _Current_) noexcept(
+ is_nothrow_constructible_v<_Iter, _Other>) // strengthened
+ : _Current(_STD move(_Current_._Current)) {}
+
+ template <_Not_same_as _Other>
+ requires convertible_to<_Other, _Iter>
+ constexpr basic_const_iterator(_Other&& _Current_) noexcept(
+ is_nothrow_constructible_v<_Iter, _Other>) // strengthened
+ : _Current(_STD forward<_Other>(_Current_)) {}
+
+ _NODISCARD constexpr const _Iter& base() const& noexcept {
+ return _Current;
+ }
+
+ _NODISCARD constexpr _Iter base() && noexcept(is_nothrow_move_constructible_v<_Iter>) /* strengthened */ {
+ return _STD move(_Current);
+ }
+
+ _NODISCARD constexpr _Reference operator*() const
+ noexcept(noexcept(static_cast<_Reference>(*_Current))) /* strengthened */ {
+ return static_cast<_Reference>(*_Current);
+ }
+
+ _NODISCARD constexpr const value_type* operator->() const
+ noexcept(contiguous_iterator<_Iter> || noexcept(*_Current)) /* strengthened */
+ requires is_lvalue_reference_v>
+ && same_as>, value_type>
+ {
+ if constexpr (contiguous_iterator<_Iter>) {
+ return _STD to_address(_Current);
+ } else {
+ return _STD addressof(*_Current);
+ }
+ }
+
+ constexpr basic_const_iterator& operator++() noexcept(noexcept(++_Current)) /* strengthened */ {
+ ++_Current;
+ return *this;
+ }
+
+ constexpr void operator++(int) noexcept(noexcept(++_Current)) /* strengthened */ {
+ ++_Current;
+ }
+
+ constexpr basic_const_iterator operator++(int) noexcept(
+ noexcept(++*this) && is_nothrow_copy_constructible_v) // strengthened
+ requires forward_iterator<_Iter>
+ {
+ auto _Tmp = *this;
+ ++*this;
+ return _Tmp;
+ }
+
+ constexpr basic_const_iterator& operator--() noexcept(noexcept(--_Current)) // strengthened
+ requires bidirectional_iterator<_Iter>
+ {
+ --_Current;
+ return *this;
+ }
+
+ constexpr basic_const_iterator operator--(int) noexcept(
+ noexcept(--*this) && is_nothrow_copy_constructible_v) // strengthened
+ requires bidirectional_iterator<_Iter>
+ {
+ auto _Tmp = *this;
+ --*this;
+ return _Tmp;
+ }
+
+ constexpr basic_const_iterator& operator+=(const difference_type _Off) noexcept(
+ noexcept(_Current += _Off)) // strengthened
+ requires random_access_iterator<_Iter>
+ {
+ _Current += _Off;
+ return *this;
+ }
+
+ constexpr basic_const_iterator& operator-=(const difference_type _Off) noexcept(
+ noexcept(_Current -= _Off)) // strengthened
+ requires random_access_iterator<_Iter>
+ {
+ _Current -= _Off;
+ return *this;
+ }
+
+ _NODISCARD constexpr _Reference operator[](const difference_type _Idx) const
+ noexcept(noexcept(static_cast<_Reference>(_Current[_Idx]))) // strengthened
+ requires random_access_iterator<_Iter>
+ {
+ return static_cast<_Reference>(_Current[_Idx]);
+ }
+
+ template _Sent>
+ _NODISCARD constexpr bool operator==(const _Sent& _Se) const // per LWG-3769
+ noexcept(noexcept(_Fake_copy_init(_Current == _Se))) /* strengthened */ {
+ return _Current == _Se;
+ }
+
+ _NODISCARD_FRIEND constexpr bool
+ operator<(const basic_const_iterator& _Left, const basic_const_iterator& _Right) noexcept(
+ noexcept(_Fake_copy_init(_Left._Current < _Right._Current))) // strengthened
+ requires random_access_iterator<_Iter>
+ {
+ return _Left._Current < _Right._Current;
+ }
+
+ _NODISCARD_FRIEND constexpr bool
+ operator>(const basic_const_iterator& _Left, const basic_const_iterator& _Right) noexcept(
+ noexcept(_Fake_copy_init(_Left._Current > _Right._Current))) // strengthened
+ requires random_access_iterator<_Iter>
+ {
+ return _Left._Current > _Right._Current;
+ }
+
+ _NODISCARD_FRIEND constexpr bool
+ operator<=(const basic_const_iterator& _Left, const basic_const_iterator& _Right) noexcept(
+ noexcept(_Fake_copy_init(_Left._Current <= _Right._Current))) // strengthened
+ requires random_access_iterator<_Iter>
+ {
+ return _Left._Current <= _Right._Current;
+ }
+
+ _NODISCARD_FRIEND constexpr bool
+ operator>=(const basic_const_iterator& _Left, const basic_const_iterator& _Right) noexcept(
+ noexcept(_Fake_copy_init(_Left._Current >= _Right._Current))) // strengthened
+ requires random_access_iterator<_Iter>
+ {
+ return _Left._Current >= _Right._Current;
+ }
+
+ _NODISCARD_FRIEND constexpr auto operator<=>(const basic_const_iterator& _Left,
+ const basic_const_iterator& _Right) noexcept(noexcept(_Left._Current <=> _Right._Current)) // strengthened
+ requires random_access_iterator<_Iter> && three_way_comparable<_Iter>
+ {
+ return _Left._Current <=> _Right._Current;
+ }
+
+ template <_Bci_order<_Iter> _Other>
+ _NODISCARD_FRIEND constexpr bool operator<(const basic_const_iterator& _Left, const _Other& _Right) noexcept(
+ noexcept(_Fake_copy_init(_Left._Current < _Right))) /* strengthened */ {
+ return _Left._Current < _Right;
+ }
+
+ template <_Bci_order<_Iter> _Other>
+ _NODISCARD_FRIEND constexpr bool operator>(const basic_const_iterator& _Left, const _Other& _Right) noexcept(
+ noexcept(_Fake_copy_init(_Left._Current > _Right))) /* strengthened */ {
+ return _Left._Current > _Right;
+ }
+
+ template <_Bci_order<_Iter> _Other>
+ _NODISCARD_FRIEND constexpr bool operator<=(const basic_const_iterator& _Left, const _Other& _Right) noexcept(
+ noexcept(_Fake_copy_init(_Left._Current <= _Right))) /* strengthened */ {
+ return _Left._Current <= _Right;
+ }
+
+ template <_Bci_order<_Iter> _Other>
+ _NODISCARD_FRIEND constexpr bool operator>=(const basic_const_iterator& _Left, const _Other& _Right) noexcept(
+ noexcept(_Fake_copy_init(_Left._Current >= _Right))) /* strengthened */ {
+ return _Left._Current >= _Right;
+ }
+
+ template <_Not_bci_order<_Iter> _Other>
+ _NODISCARD_FRIEND constexpr bool operator<(const _Other& _Left, const basic_const_iterator& _Right) noexcept(
+ noexcept(_Fake_copy_init(_Left < _Right._Current))) /* strengthened */ {
+ return _Left < _Right._Current;
+ }
+
+ template <_Not_bci_order<_Iter> _Other>
+ _NODISCARD_FRIEND constexpr bool operator>(const _Other& _Left, const basic_const_iterator& _Right) noexcept(
+ noexcept(_Fake_copy_init(_Left > _Right._Current))) /* strengthened */ {
+ return _Left > _Right._Current;
+ }
+
+ template <_Not_bci_order<_Iter> _Other>
+ _NODISCARD_FRIEND constexpr bool operator<=(const _Other& _Left, const basic_const_iterator& _Right) noexcept(
+ noexcept(_Fake_copy_init(_Left <= _Right._Current))) /* strengthened */ {
+ return _Left <= _Right._Current;
+ }
+
+ template <_Not_bci_order<_Iter> _Other>
+ _NODISCARD_FRIEND constexpr bool operator>=(const _Other& _Left, const basic_const_iterator& _Right) noexcept(
+ noexcept(_Fake_copy_init(_Left >= _Right._Current))) /* strengthened */ {
+ return _Left >= _Right._Current;
+ }
+
+ template <_Bci_order_3way<_Iter> _Other>
+ _NODISCARD_FRIEND constexpr auto operator<=>(const basic_const_iterator& _Left, const _Other& _Right) noexcept(
+ noexcept(_Left._Current <=> _Right)) /* strengthened */ {
+ return _Left._Current <=> _Right;
+ }
+
+ _NODISCARD_FRIEND constexpr basic_const_iterator operator+(const basic_const_iterator& _It,
+ const difference_type _Off) noexcept(noexcept(basic_const_iterator{_It._Current + _Off})) // strengthened
+ requires random_access_iterator<_Iter>
+ {
+ return basic_const_iterator{_It._Current + _Off};
+ }
+
+ _NODISCARD_FRIEND constexpr basic_const_iterator operator+(const difference_type _Off,
+ const basic_const_iterator& _It) noexcept(noexcept(basic_const_iterator{_It._Current + _Off})) // strengthened
+ requires random_access_iterator<_Iter>
+ {
+ return basic_const_iterator{_It._Current + _Off};
+ }
+
+ _NODISCARD_FRIEND constexpr basic_const_iterator operator-(const basic_const_iterator& _It,
+ const difference_type _Off) noexcept(noexcept(basic_const_iterator{_It._Current - _Off})) // strengthened
+ requires random_access_iterator<_Iter>
+ {
+ return basic_const_iterator{_It._Current - _Off};
+ }
+
+ template _Sent>
+ _NODISCARD constexpr difference_type operator-(const _Sent& _Se) const // per LWG-3769
+ noexcept(noexcept(_Current - _Se)) /* strengthened */ {
+ return _Current - _Se;
+ }
+
+ template <_Not_a_const_iterator _Sent>
+ requires sized_sentinel_for<_Sent, _Iter>
+ _NODISCARD_FRIEND constexpr difference_type operator-(const _Sent& _Se, const basic_const_iterator& _It) noexcept(
+ noexcept(_Se - _It._Current)) /* strengthened */ { // per LWG-3769
+ return _Se - _It._Current;
+ }
+};
+
+template _Ty2>
+struct common_type, _Ty2> {
+ using type = basic_const_iterator>;
+};
+
+template _Ty2>
+struct common_type<_Ty2, basic_const_iterator<_Ty1>> {
+ using type = basic_const_iterator>;
+};
+
+template _Ty2>
+struct common_type, basic_const_iterator<_Ty2>> {
+ using type = basic_const_iterator>;
+};
+
+_EXPORT_STD template
+_NODISCARD constexpr const_iterator<_Iter> make_const_iterator(_Iter _It) noexcept(
+ is_nothrow_constructible_v, _Iter&>) /* strengthened */ {
+ return _It;
+}
+
+_EXPORT_STD template
+_NODISCARD constexpr const_sentinel<_Sent> make_const_sentinel(_Sent _Se) noexcept(
+ is_nothrow_constructible_v, _Sent&>) /* strengthened */ {
+ return _Se;
+}
+#endif // _HAS_CXX23
+
namespace ranges {
template
inline constexpr bool _Has_complete_elements = false;
diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h
index 7e7c78319ff..cd8749a4571 100644
--- a/stl/inc/yvals_core.h
+++ b/stl/inc/yvals_core.h
@@ -322,6 +322,8 @@
// P2166R1 Prohibiting basic_string And basic_string_view Construction From nullptr
// P2186R2 Removing Garbage Collection Support
// P2273R3 constexpr unique_ptr
+// P2278R4 cbegin Should Always Return A Constant Iterator
+// ("Iterators" section from the paper only)
// P2291R3 constexpr Integral
// P2302R4 ranges::contains, ranges::contains_subrange
// P2321R2 zip
diff --git a/tests/std/include/range_algorithm_support.hpp b/tests/std/include/range_algorithm_support.hpp
index 1f88f13a11d..1c4c0db2ce6 100644
--- a/tests/std/include/range_algorithm_support.hpp
+++ b/tests/std/include/range_algorithm_support.hpp
@@ -361,7 +361,7 @@ namespace test {
template },
- // Model sentinel_for with self (and sized_sentinel_for if Diff; implies copyable)?
+ // Model sentinel_for with self (and sized_sentinel_for if Diff implies copyable)?
CanCompare Eq = CanCompare{derived_from},
// Use a ProxyRef reference type (instead of Element&)?
ProxyRef Proxy = ProxyRef{!derived_from},
diff --git a/tests/std/test.lst b/tests/std/test.lst
index 24d9ded681f..2501eeab87c 100644
--- a/tests/std/test.lst
+++ b/tests/std/test.lst
@@ -545,6 +545,8 @@ tests\P2136R3_invoke_r
tests\P2162R2_std_visit_for_derived_classes_from_variant
tests\P2231R1_complete_constexpr_optional_variant
tests\P2273R3_constexpr_unique_ptr
+tests\P2278R4_basic_const_iterator
+tests\P2278R4_ranges_const_iterator_machinery
tests\P2302R4_ranges_alg_contains
tests\P2302R4_ranges_alg_contains_subrange
tests\P2321R2_proxy_reference
diff --git a/tests/std/tests/P2278R4_basic_const_iterator/env.lst b/tests/std/tests/P2278R4_basic_const_iterator/env.lst
new file mode 100644
index 00000000000..18e2d7c71ec
--- /dev/null
+++ b/tests/std/tests/P2278R4_basic_const_iterator/env.lst
@@ -0,0 +1,4 @@
+# Copyright (c) Microsoft Corporation.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+RUNALL_INCLUDE ..\concepts_latest_matrix.lst
diff --git a/tests/std/tests/P2278R4_basic_const_iterator/test.cpp b/tests/std/tests/P2278R4_basic_const_iterator/test.cpp
new file mode 100644
index 00000000000..b08f706aa6e
--- /dev/null
+++ b/tests/std/tests/P2278R4_basic_const_iterator/test.cpp
@@ -0,0 +1,288 @@
+// Copyright (c) Microsoft Corporation.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+#include
+#include
+#include
+#include
+#include
+
+#include "range_algorithm_support.hpp"
+
+using namespace std;
+
+template
+concept Pointer = is_pointer_v;
+
+template
+concept HasPeek = requires(const It& iter) {
+ { iter.peek() } -> Pointer;
+ requires convertible_to*>;
+ };
+
+static_assert(!HasPeek);
+static_assert(!HasPeek>);
+static_assert(HasPeek>);
+
+template
+constexpr void test_one(It iter) {
+ using ConstIt = basic_const_iterator;
+ using Se = test::sentinel>;
+
+ // Validate iterator concepts
+ static_assert(input_iterator);
+ static_assert(forward_iterator == forward_iterator);
+ static_assert(bidirectional_iterator == bidirectional_iterator);
+ static_assert(random_access_iterator == random_access_iterator);
+ static_assert(contiguous_iterator == contiguous_iterator);
+
+ // Validate nested types
+ static_assert(same_as>);
+ static_assert(same_as>);
+ if constexpr (forward_iterator) {
+ using Cat = typename iterator_traits::iterator_category;
+ static_assert(same_as);
+ }
+
+ // Validate default-initializability
+ static_assert(default_initializable == default_initializable);
+
+ same_as auto citer = basic_const_iterator{iter};
+ if (!forward_iterator) { // intentionally not if constexpr
+ return;
+ }
+
+ { // Validate basic_const_iterator::operator*()
+ same_as> decltype(auto) val = *citer;
+ assert(val == *iter);
+ static_assert(noexcept(*citer) == noexcept(static_cast>(*iter))); // strengthened
+ }
+
+ // Validate basic_const_iterator::operator->()
+ if constexpr (is_lvalue_reference_v>
+ && same_as>, iter_value_t>) {
+ const same_as*> auto ptr = citer.operator->();
+ assert(ptr == addressof(*citer));
+
+ if constexpr (contiguous_iterator) {
+ static_assert(noexcept(citer.operator->()) == noexcept(to_address(iter))); // strengthened
+ } else {
+ static_assert(noexcept(citer.operator->()) == noexcept(addressof(*iter))); // strengthened
+ }
+ }
+
+ { // Validate basic_const_iterator::operator++()
+ same_as decltype(auto) citer2 = ++citer;
+ ++iter;
+ assert(citer2 == iter);
+ assert(*citer2 == *iter);
+ static_assert(noexcept(++citer) == noexcept(++iter)); // strengthened
+ }
+
+ // Validate basic_const_iterator::operator++(int)
+ if constexpr (forward_iterator) {
+ same_as decltype(auto) citer2 = citer++;
+ assert(citer2 == iter);
+ assert(*citer2 == *iter);
+ static_assert(
+ noexcept(citer++) == (noexcept(iter++) && is_nothrow_copy_constructible_v) ); // strengthened
+ } else {
+ static_assert(is_void_v);
+ static_assert(noexcept(citer++) == noexcept(iter++)); // strengthened
+ }
+
+ iter++;
+ assert(citer == iter);
+ assert(*citer == *iter);
+
+
+ if constexpr (bidirectional_iterator) {
+ { // Validate basic_const_iterator::operator--()
+ same_as decltype(auto) citer2 = --citer;
+ --iter;
+ assert(citer2 == iter);
+ assert(*citer2 == *iter);
+ static_assert(noexcept(--citer) == noexcept(--iter)); // strengthened
+ }
+
+ { // Validate basic_const_iterator::operator--(int)
+ same_as decltype(auto) citer2 = citer--;
+ assert(citer2 == iter);
+ assert(*citer2 == *iter);
+ static_assert(
+ noexcept(citer--) == (noexcept(iter--) && is_nothrow_copy_constructible_v) ); // strengthened
+
+ iter--;
+ assert(citer == iter);
+ assert(*citer == *iter);
+ }
+ }
+
+ if constexpr (random_access_iterator) {
+ { // Validate basic_const_iterator::operator+=()
+ same_as decltype(auto) citer2 = (citer += 2);
+ iter += 2;
+ assert(citer2 == iter);
+ assert(*citer2 == *iter);
+ static_assert(noexcept(citer += 2) == noexcept(iter += 2)); // strengthened
+ }
+
+ { // Validate basic_const_iterator::operator-=()
+ same_as decltype(auto) citer2 = (citer -= 2);
+ iter -= 2;
+ assert(citer2 == iter);
+ assert(*citer2 == *iter);
+ static_assert(noexcept(citer -= 2) == noexcept(iter -= 2)); // strengthened
+ }
+
+ { // Validate basic_const_iterator::operator[]
+ same_as> decltype(auto) val = citer[0];
+ assert(val == iter[0]);
+ static_assert(
+ noexcept(citer[0]) == noexcept(static_cast>(iter[0]))); // strengthened
+ }
+
+ { // Validate operator{<, >, <=, >=, <=>}(const basic_const_iterator&, const basic_const_iterator&)
+ const same_as auto citer2 = (citer + 1);
+ assert(citer < citer2);
+ assert(citer2 > citer);
+ assert(citer <= citer2);
+ assert(citer2 >= citer);
+ assert(citer2 <=> citer == partial_ordering::greater);
+
+ static_assert(noexcept(citer < citer2) == noexcept(citer.base() < citer2.base())); // strengthened
+ static_assert(noexcept(citer2 > citer) == noexcept(citer2.base() > citer.base())); // strengthened
+ static_assert(noexcept(citer <= citer2) == noexcept(citer.base() <= citer2.base())); // strengthened
+ static_assert(noexcept(citer2 >= citer) == noexcept(citer2.base() >= citer.base())); // strengthened
+ static_assert(noexcept(citer2 <=> citer) == noexcept(citer2.base() <=> citer.base())); // strengthened
+ }
+
+ iter += 2; // advance iter temporarily
+
+ { // Validate operator{<, >, <=, >=, <=>}(const basic_const_iterator&, const "not same as
+ // basic_const_iterator"&)
+ assert(citer < iter);
+ assert(!(citer > iter));
+ assert(citer <= iter);
+ assert(!(citer >= iter));
+ assert(citer <=> iter == partial_ordering::less);
+
+ static_assert(noexcept(citer < iter) == noexcept(citer.base() < iter)); // strengthened
+ static_assert(noexcept(!(citer > iter)) == noexcept(!(citer.base() > iter))); // strengthened
+ static_assert(noexcept(citer <= iter) == noexcept(citer.base() <= iter)); // strengthened
+ static_assert(noexcept(!(citer >= iter)) == noexcept(!(citer.base() >= iter))); // strengthened
+ static_assert(noexcept(citer <=> iter) == noexcept(citer.base() <=> iter)); // strengthened
+ }
+
+ { // Validate operator{<, >, <=, >=}(const "not a const iterator"&, const basic_const_iterator&)
+ assert(!(iter < citer));
+ assert(iter > citer);
+ assert(!(iter <= citer));
+ assert(iter >= citer);
+
+ static_assert(noexcept(!(iter < citer)) == noexcept(!(iter < citer.base()))); // strengthened
+ static_assert(noexcept(iter > citer) == noexcept(iter > citer.base())); // strengthened
+ static_assert(noexcept(!(iter <= citer)) == noexcept(!(iter <= citer.base()))); // strengthened
+ static_assert(noexcept(iter >= citer) == noexcept(iter >= citer.base())); // strengthened
+ }
+
+ { // Validate operator+(const basic_const_iterator&, difference_type)
+ const same_as auto citer2 = (citer + 2);
+ const same_as auto citer3 = (2 + citer);
+ assert(citer2 == citer3);
+ assert(*citer2 == *iter);
+
+ static_assert(noexcept(citer + 2) == noexcept(iter + 2)); // strengthened
+ static_assert(noexcept(2 + citer) == noexcept(2 + iter)); // strengthened
+ }
+
+ { // Validate operator-(const basic_const_iterator&, difference_type)
+ citer += 4;
+ const same_as auto citer2 = (citer - 2);
+ assert(*citer2 == *iter);
+ citer -= 4;
+
+ static_assert(noexcept(citer - 2) == noexcept(iter - 2)); // strengthened
+ }
+
+ { // Validate operator-(const basic_const_iterator&, sized_sentinel)
+ assert(citer - citer == 0);
+ assert(citer - iter == -2);
+ assert(iter - citer == 2);
+ static_assert(noexcept(citer - citer) == noexcept(iter - iter)); // strengthened
+ static_assert(noexcept(citer - iter) == noexcept(iter - iter)); // strengthened
+ static_assert(noexcept(iter - citer) == noexcept(iter - iter)); // strengthened
+
+ if constexpr (HasPeek && sized_sentinel_for) {
+ Se sent{iter.peek()};
+ assert(citer - sent == -2);
+ assert(sent - citer == 2);
+ static_assert(noexcept(citer - sent) == noexcept(iter - sent)); // strengthened
+ static_assert(noexcept(sent - citer) == noexcept(sent - iter)); // strengthened
+ }
+ }
+
+ iter -= 2;
+ }
+
+ // Validate to_address
+ if constexpr (contiguous_iterator && HasPeek) {
+ assert(to_address(citer) == iter.peek());
+ }
+
+ // Validate basic_const_iterator::operator==()
+ assert(citer == iter);
+ assert(iter == citer);
+ static_assert(noexcept(citer == iter) == noexcept(iter == iter));
+ if constexpr (HasPeek && sentinel_for) {
+ Se sent{iter.peek()};
+ assert(citer == sent);
+ static_assert(noexcept(citer == sent) == noexcept(iter == sent)); // strengthened
+ }
+
+ { // Validate basic_const_iterator::base() const&
+ [[maybe_unused]] same_as decltype(auto) base = citer.base();
+ static_assert(noexcept(citer.base()));
+ }
+
+ { // Validate basic_const_iterator::base() &&
+ [[maybe_unused]] same_as decltype(auto) base = move(citer).base();
+ static_assert(noexcept(move(citer).base()) == is_nothrow_move_constructible_v); // strengthened
+ }
+}
+
+static constexpr int some_ints[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+
+struct instantiator {
+ template
+ static constexpr void call() {
+ It iter{some_ints};
+ test_one(iter);
+ }
+};
+
+template
+using test_iterator = test::iterator}>;
+
+constexpr void instantiation_test() {
+ using test::CanDifference;
+
+ // The iterator is sensitive to category and differencing, but oblivious to proxyness
+ instantiator::call>();
+ instantiator::call>();
+ instantiator::call>();
+ instantiator::call>();
+ instantiator::call>();
+
+ instantiator::call>();
+ instantiator::call>();
+ instantiator::call>();
+ instantiator::call>();
+ instantiator::call>();
+}
+
+int main() {
+ static_assert((instantiation_test(), true));
+ instantiation_test();
+}
diff --git a/tests/std/tests/P2278R4_ranges_const_iterator_machinery/env.lst b/tests/std/tests/P2278R4_ranges_const_iterator_machinery/env.lst
new file mode 100644
index 00000000000..18e2d7c71ec
--- /dev/null
+++ b/tests/std/tests/P2278R4_ranges_const_iterator_machinery/env.lst
@@ -0,0 +1,4 @@
+# Copyright (c) Microsoft Corporation.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+RUNALL_INCLUDE ..\concepts_latest_matrix.lst
diff --git a/tests/std/tests/P2278R4_ranges_const_iterator_machinery/test.compile.pass.cpp b/tests/std/tests/P2278R4_ranges_const_iterator_machinery/test.compile.pass.cpp
new file mode 100644
index 00000000000..9a33ffe39d4
--- /dev/null
+++ b/tests/std/tests/P2278R4_ranges_const_iterator_machinery/test.compile.pass.cpp
@@ -0,0 +1,196 @@
+// Copyright (c) Microsoft Corporation.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+using namespace std;
+
+template
+concept CanIterConstRef = requires { typename iter_const_reference_t; };
+
+template
+concept CanConstIterator = requires(It it) {
+ typename const_iterator;
+ { make_const_iterator(move(it)) } -> same_as>;
+ };
+
+template
+concept CanConstSentinel = requires(Se se) {
+ typename const_sentinel;
+ { make_const_sentinel(move(se)) } -> same_as>;
+ };
+
+static_assert(!CanIterConstRef);
+static_assert(!CanIterConstRef>);
+static_assert(!CanIterConstRef>);
+
+namespace test_pointer {
+ using Ptr = int*;
+ static_assert(CanIterConstRef);
+ static_assert(CanConstIterator);
+ static_assert(CanConstSentinel);
+ static_assert(same_as, const int&>);
+ static_assert(same_as, basic_const_iterator>);
+ static_assert(same_as>, const int&>);
+ static_assert(same_as, basic_const_iterator>);
+ static_assert(same_as>, const int&>);
+
+ using ConstPtr = const int*;
+ static_assert(CanIterConstRef);
+ static_assert(CanConstIterator);
+ static_assert(CanConstSentinel);
+ static_assert(same_as, const int&>);
+ static_assert(same_as, ConstPtr>);
+ static_assert(same_as>, const int&>);
+ static_assert(same_as, ConstPtr>);
+
+ // Validate common_type
+ static_assert(same_as, ConstPtr>, basic_const_iterator>);
+ static_assert(same_as>, basic_const_iterator>);
+ static_assert(same_as, basic_const_iterator>,
+ basic_const_iterator>);
+} // namespace test_pointer
+
+namespace test_random_access_iter {
+ using Iter = deque::iterator;
+ static_assert(CanIterConstRef);
+ static_assert(CanConstIterator);
+ static_assert(CanConstSentinel);
+ static_assert(same_as, const int&>);
+ static_assert(same_as, basic_const_iterator>);
+ static_assert(same_as>, const int&>);
+ static_assert(same_as, basic_const_iterator>);
+ static_assert(same_as>, const int&>);
+
+ using ConstIter = deque::const_iterator;
+ static_assert(CanIterConstRef);
+ static_assert(CanConstIterator);
+ static_assert(CanConstSentinel);
+ static_assert(same_as, const int&>);
+ static_assert(same_as, ConstIter>);
+ static_assert(same_as>, const int&>);
+ static_assert(same_as, ConstIter>);
+} // namespace test_random_access_iter
+
+namespace test_bidirectional_iter {
+ using Iter = list::iterator;
+ static_assert(CanIterConstRef);
+ static_assert(CanConstIterator);
+ static_assert(CanConstSentinel);
+ static_assert(same_as, const int&>);
+ static_assert(same_as, basic_const_iterator>);
+ static_assert(same_as>, const int&>);
+ static_assert(same_as, basic_const_iterator>);
+ static_assert(same_as>, const int&>);
+
+ using ConstIter = list::const_iterator;
+ static_assert(CanIterConstRef);
+ static_assert(CanConstIterator);
+ static_assert(CanConstSentinel);
+ static_assert(same_as, const int&>);
+ static_assert(same_as, ConstIter>);
+ static_assert(same_as>, const int&>);
+ static_assert(same_as, ConstIter>);
+} // namespace test_bidirectional_iter
+
+namespace test_forward_iter {
+ using Iter = forward_list::iterator;
+ static_assert(CanIterConstRef);
+ static_assert(CanConstIterator);
+ static_assert(CanConstSentinel);
+ static_assert(same_as, const int&>);
+ static_assert(same_as, basic_const_iterator>);
+ static_assert(same_as>, const int&>);
+ static_assert(same_as, basic_const_iterator>);
+ static_assert(same_as>, const int&>);
+
+ using ConstIter = forward_list::const_iterator;
+ static_assert(CanIterConstRef);
+ static_assert(CanConstIterator);
+ static_assert(CanConstSentinel);
+ static_assert(same_as, const int&>);
+ static_assert(same_as, ConstIter>);
+ static_assert(same_as>, const int&>);
+ static_assert(same_as, ConstIter>);
+} // namespace test_forward_iter
+
+namespace test_input_iter {
+ using Iter = ranges::iterator_t>;
+ static_assert(CanIterConstRef);
+ static_assert(CanConstIterator);
+ static_assert(!CanConstSentinel);
+ static_assert(same_as, const int&>);
+ static_assert(same_as, basic_const_iterator>);
+ static_assert(same_as>, const int&>);
+} // namespace test_input_iter
+
+namespace test_prvalue_iter {
+ using TransformView = ranges::transform_view>, int (*)(float)>;
+
+ using Iter = ranges::iterator_t;
+ static_assert(CanIterConstRef);
+ static_assert(CanConstIterator);
+ static_assert(CanConstSentinel);
+ static_assert(same_as, int>);
+ static_assert(same_as, Iter>);
+ static_assert(same_as>, int>);
+ static_assert(same_as, Iter>);
+ static_assert(same_as