Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
72bb6e9
Augment tests
smallp-o-p Aug 23, 2025
df2e9e7
formatting
smallp-o-p Aug 25, 2025
322079d
Forgot to gate a test
smallp-o-p Aug 25, 2025
2a014d8
Update generated files
smallp-o-p Aug 25, 2025
294cb8e
Formatting
smallp-o-p Aug 25, 2025
762bb8c
Update documentation
smallp-o-p Aug 22, 2025
223ce49
Update optional to allow T&, disable iterator and value_or for T(&)()
smallp-o-p Aug 22, 2025
92137cb
Remove nullptr dereference in iterator
smallp-o-p Aug 27, 2025
c123551
Left in an extraneous paren
smallp-o-p Aug 27, 2025
9075442
Python formatting
smallp-o-p Sep 24, 2025
0ca1d51
Fix merge oops
smallp-o-p Sep 24, 2025
4ba0c8f
Formatting and include
smallp-o-p Sep 24, 2025
660716c
Gate ref tests, use std::same_as suggestion
smallp-o-p Sep 25, 2025
717c5f9
Fix GCC shadow warning
smallp-o-p Sep 25, 2025
1612378
Add constraint to emplace, add deleted optional<T&> constructors, add…
smallp-o-p Sep 27, 2025
ea6ce10
Remove __can_bind_reference() in favor of __reference_constructs_from…
smallp-o-p Sep 28, 2025
089cb71
Assignment shouldn't modify the underlying value, test for that and a…
smallp-o-p Oct 8, 2025
c602898
Clang-Format
smallp-o-p Oct 8, 2025
42637ff
Address comments + Add some tests for function refs and array refs
smallp-o-p Oct 23, 2025
3c9c51b
Change a few more static_asserts
smallp-o-p Oct 24, 2025
dfb3349
Fix up swap, make_optional, add tests
smallp-o-p Oct 26, 2025
bc68310
Address nits
smallp-o-p Oct 26, 2025
4f87438
Formatting
smallp-o-p Oct 26, 2025
e586acd
Formatting x2
smallp-o-p Oct 27, 2025
18c1b96
Fix typo
smallp-o-p Oct 27, 2025
1add5c5
Allow make_optional for T&, add test
smallp-o-p Oct 27, 2025
85db654
Formatting
smallp-o-p Oct 27, 2025
65e92c4
Use std::swap, mark T& assignment and __swap noexcept
smallp-o-p Oct 31, 2025
5cf4603
Replace all usages of optional::value_type with _Tp, remove reference…
smallp-o-p Nov 8, 2025
3c2473d
Clean up deleted constructors signatures
smallp-o-p Nov 8, 2025
4f58bf2
Drive-by: Add a ranges::range<optional<T>> test for iterators
smallp-o-p Nov 8, 2025
ba998d7
Add enable_borrowed_range<optional<T&>> and corresponding test
smallp-o-p Nov 8, 2025
44d8d05
Add to module inc
smallp-o-p Nov 8, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions libcxx/docs/FeatureTestMacroTable.rst
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,8 @@ Status
---------------------------------------------------------- -----------------
``__cpp_lib_not_fn`` ``202306L``
---------------------------------------------------------- -----------------
``__cpp_lib_optional`` ``202506L``
---------------------------------------------------------- -----------------
``__cpp_lib_optional_range_support`` ``202406L``
---------------------------------------------------------- -----------------
``__cpp_lib_out_ptr`` ``202311L``
Expand Down
1 change: 1 addition & 0 deletions libcxx/docs/ReleaseNotes/22.rst
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ Implemented Papers

- P2321R2: ``zip`` (`Github <https://llvm.org/PR105169>`__) (The paper is partially implemented. ``zip_transform_view``
is implemented in this release)
- P2988R12: ``std::optional<T&>`` (`Github <https://llvm.org/PR148131>`__)
- P3044R2: sub-``string_view`` from ``string`` (`Github <https://llvm.org/PR148140>`__)
- P3223R2: Making ``std::istream::ignore`` less surprising (`Github <https://llvm.org/PR148178>`__)
- P3060R3: Add ``std::views::indices(n)`` (`Github <https://llvm.org/PR148175>`__)
Expand Down
2 changes: 1 addition & 1 deletion libcxx/docs/Status/Cxx2cPapers.csv
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@
"`P3293R3 <https://wg21.link/P3293R3>`__","Splicing a base class subobject","2025-06 (Sofia)","","","`#148125 <https://github.com/llvm/llvm-project/issues/148125>`__",""
"`P3491R3 <https://wg21.link/P3491R3>`__","``define_static_{string,object,array}``","2025-06 (Sofia)","","","`#148126 <https://github.com/llvm/llvm-project/issues/148126>`__",""
"`P3096R12 <https://wg21.link/P3096R12>`__","Function Parameter Reflection in Reflection for C++26","2025-06 (Sofia)","","","`#148127 <https://github.com/llvm/llvm-project/issues/148127>`__",""
"`P2988R12 <https://wg21.link/P2988R12>`__","``std::optional<T&>``","2025-06 (Sofia)","","","`#148131 <https://github.com/llvm/llvm-project/issues/148131>`__",""
"`P2988R12 <https://wg21.link/P2988R12>`__","``std::optional<T&>``","2025-06 (Sofia)","|Complete|","22","`#148131 <https://github.com/llvm/llvm-project/issues/148131>`__",""
"`P3348R4 <https://wg21.link/P3348R4>`__","C++26 should refer to C23 not C17","2025-06 (Sofia)","","","`#148133 <https://github.com/llvm/llvm-project/issues/148133>`__",""
"`P3037R6 <https://wg21.link/P3037R6>`__","``constexpr`` ``std::shared_ptr`` and friends","2025-06 (Sofia)","","","`#148135 <https://github.com/llvm/llvm-project/issues/148135>`__",""
"`P3284R4 <https://wg21.link/P3284R4>`__","``write_env`` and ``unstoppable`` Sender Adaptors","2025-06 (Sofia)","","","`#148136 <https://github.com/llvm/llvm-project/issues/148136>`__",""
Expand Down
4 changes: 2 additions & 2 deletions libcxx/include/__iterator/wrap_iter.h
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,8 @@ class __wrap_iter {
friend class span;
template <class _Tp, size_t _Size>
friend struct array;
template <class _Tp>
friend class optional;
template <class _Tp, class>
friend struct __optional_iterator;
};

template <class _Iter1>
Expand Down
326 changes: 215 additions & 111 deletions libcxx/include/optional

Large diffs are not rendered by default.

5 changes: 4 additions & 1 deletion libcxx/include/version
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,8 @@ __cpp_lib_nonmember_container_access 201411L <array> <deque>
__cpp_lib_not_fn 202306L <functional>
201603L // C++17
__cpp_lib_null_iterators 201304L <iterator>
__cpp_lib_optional 202110L <optional>
__cpp_lib_optional 202506L <optional>
202110L // C++23
202106L // C++20
201606L // C++17
__cpp_lib_optional_range_support 202406L <optional>
Expand Down Expand Up @@ -588,6 +589,8 @@ __cpp_lib_void_t 201411L <type_traits>
# define __cpp_lib_mdspan 202406L
# undef __cpp_lib_not_fn
# define __cpp_lib_not_fn 202306L
# undef __cpp_lib_optional
# define __cpp_lib_optional 202506L
# define __cpp_lib_optional_range_support 202406L
# undef __cpp_lib_out_ptr
# define __cpp_lib_out_ptr 202311L
Expand Down
3 changes: 2 additions & 1 deletion libcxx/modules/std/optional.inc
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ export namespace std {
#if _LIBCPP_STD_VER >= 26
// [optional.iterators], iterator support
namespace ranges {
using std::ranges::enable_borrowed_range;
using std::ranges::enable_view;
}
} // namespace ranges
#endif
// [optional.nullopt], no-value state indicator
using std::nullopt;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@ concept has_iterator_aliases = requires {

static_assert(has_iterator_aliases<std::optional<int>>);
static_assert(has_iterator_aliases<std::optional<const int>>);

// TODO: Uncomment these once P2988R12 is implemented, as they would be testing optional<T&>

// static_assert(!has_iterator_aliases<std::optional<int (&)[]>>);
// static_assert(!has_iterator_aliases<std::optional<void (&)(int, char)>>);
static_assert(has_iterator_aliases<std::optional<int&>>);
static_assert(has_iterator_aliases<std::optional<const int&>>);
static_assert(!has_iterator_aliases<std::optional<int (&)[1]>>);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like that we should achieve the following results, per the current resolution of LWG4308.

Suggested change
static_assert(!has_iterator_aliases<std::optional<int (&)[1]>>);
static_assert(has_iterator_aliases<std::optional<int (&)[1]>>);
static_assert(!has_iterator_aliases<std::optional<int (&)[]>>);

The product code needs to be correspondingly adjusted. We can do this in a following-up PR as LWG4308 is not formally accepted yet (but it will be soon).

static_assert(!has_iterator_aliases<std::optional<int (&)()>>);
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@

//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//

// REQUIRES: std-at-least-c++26

// <optional>

// template <class U> T optional<T>::value_or(U&&);

#include <concepts>
#include <optional>

template <typename Opt, typename T>
concept has_value_or = requires(Opt opt, T&& t) {
{ opt.value_or(t) } -> std::same_as<T>;
};

static_assert(has_value_or<std::optional<int>, int>);
static_assert(has_value_or<std::optional<int&>, int&>);
static_assert(has_value_or<std::optional<const int&>, const int&>);
static_assert(!has_value_or<std::optional<int (&)[1]>&&, int (&)[1]>);
static_assert(!has_value_or<std::optional<int (&)()>&&, int (&)()>);
Original file line number Diff line number Diff line change
Expand Up @@ -142,8 +142,8 @@
# ifndef __cpp_lib_optional
# error "__cpp_lib_optional should be defined in c++26"
# endif
# if __cpp_lib_optional != 202110L
# error "__cpp_lib_optional should have the value 202110L in c++26"
# if __cpp_lib_optional != 202506L
# error "__cpp_lib_optional should have the value 202506L in c++26"
# endif

# ifndef __cpp_lib_optional_range_support
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7455,8 +7455,8 @@
# ifndef __cpp_lib_optional
# error "__cpp_lib_optional should be defined in c++26"
# endif
# if __cpp_lib_optional != 202110L
# error "__cpp_lib_optional should have the value 202110L in c++26"
# if __cpp_lib_optional != 202506L
# error "__cpp_lib_optional should have the value 202506L in c++26"
# endif

# ifndef __cpp_lib_optional_range_support
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@

template <typename T>
constexpr bool test() {
std::optional<T> opt{T{}};
std::remove_reference_t<T> t = std::remove_reference_t<T>{};
std::optional<T> opt{t};

{ // begin() is marked noexcept
static_assert(noexcept(opt.begin()));
Expand Down Expand Up @@ -53,6 +54,10 @@ constexpr bool tests() {
assert(test<char>());
assert(test<const int>());
assert(test<const char>());
assert(test<int&>());
assert(test<char&>());
assert(test<const int&>());
assert(test<const char&>());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto, if you think it's better not to delay implementing LWG4308, cases for int (&)[42] and its friends should also be added.

return true;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
//===----------------------------------------------------------------------===//
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why isn't this a .compile.pass.cpp? We don't test anything that related to runtime behavior here.

//
// 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
//
//===----------------------------------------------------------------------===//

// REQUIRES: std-at-least-c++26

// <optional>

// template <class T> class optional<T&>::iterator;
// template <class T> class optional<T&>::const_iterator;
// template <class T>
// constexpr bool ranges::enable_borrowed_range<optional<T&>> = true;

#include <cassert>
#include <optional>
#include <ranges>

template <typename T>
constexpr bool enable_borrowed_range() {
{
assert(std::ranges::enable_borrowed_range<std::optional<T&>>);
}
return true;
}
Comment on lines +22 to +28
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
template <typename T>
constexpr bool enable_borrowed_range() {
{
assert(std::ranges::enable_borrowed_range<std::optional<T&>>);
}
return true;
}
template <typename T>
void enable_borrowed_range() {
static_assert(std::ranges::enable_borrowed_range<std::optional<T&>>);
}

Ditto below, I think we should only use static_assert in this test file.


template <typename T>
constexpr bool borrowed_range() {
if (std::ranges::range<std::optional<T&>>) {
assert(std::ranges::borrowed_range<std::optional<T&>>);
} else {
assert(!std::ranges::borrowed_range<std::optional<T&>>);
return false;
}

return true;
}
Comment on lines +30 to +40
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
template <typename T>
constexpr bool borrowed_range() {
if (std::ranges::range<std::optional<T&>>) {
assert(std::ranges::borrowed_range<std::optional<T&>>);
} else {
assert(!std::ranges::borrowed_range<std::optional<T&>>);
return false;
}
return true;
}
template <typename T>
void borrowed_range() {
static_assert(std::ranges::borrowed_range<std::optional<T&>> == std::ranges::range<std::optional<T&>>);
}

So that we won't need to touch this file again when implementing LWG4308, as we've been testing range-ness in iterator.pass.cpp.


constexpr bool test_enable_borrowed_range() {
assert(enable_borrowed_range<int>());
assert(enable_borrowed_range<const int>());
assert(enable_borrowed_range<int[]>());
assert(enable_borrowed_range<int[10]>());
assert(enable_borrowed_range<int()>());

return true;
}

constexpr bool test_borrowed_range() {
assert(borrowed_range<int>());
assert(borrowed_range<const int>());
assert(!borrowed_range<int[]>());
assert(!borrowed_range<int[10]>());
assert(!borrowed_range<int()>());

return true;
}

int main(int, char**) {
{
static_assert(test_enable_borrowed_range());
assert(test_enable_borrowed_range());
}

{
static_assert(test_borrowed_range());
assert(test_enable_borrowed_range());
}

return 0;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, conventional empty line at end of the file.

Suggested change
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include <iterator>
#include <optional>
#include <ranges>
#include <type_traits>
#include <utility>

template <typename T>
Expand All @@ -41,7 +42,8 @@ constexpr bool test() {
assert(it2 == std::as_const(disengaged).end());
}

std::optional<T> engaged{T{}};
std::remove_reference_t<T> t = std::remove_reference_t<T>{};
std::optional<T> engaged{t};

{ // end() != begin() if the optional is engaged
auto it = engaged.end();
Expand All @@ -62,6 +64,10 @@ constexpr bool tests() {
assert(test<char>());
assert(test<const int>());
assert(test<const char>());
assert(test<int&>());
assert(test<char&>());
assert(test<const int&>());
assert(test<const char&>());

return true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,23 @@
// template <class T> class optional::const_iterator;

#include <cassert>
#include <iterator>
#include <optional>
#include <ranges>
#include <type_traits>
#include <utility>

template <typename T, T __val>
template <typename T>
constexpr bool test_range_concept() {
return std::ranges::range<std::optional<T>>;
}

template <typename T, std::remove_reference_t<T> __val>
constexpr bool test() {
std::optional<T> opt{__val};
std::remove_reference_t<T> v{__val};
std::optional<T> opt{v};
{
assert(test_range_concept<T>());
}

{ // Dereferencing an iterator of an engaged optional will return the same value that the optional holds.
auto it = opt.begin();
Expand All @@ -41,13 +49,14 @@ constexpr bool test() {
assert(std::random_access_iterator<decltype(it2)>);
}

{ // const_iterator::value_type == std::remove_cv_t<T>, const_iterator::reference == const T&, iterator::value_type = std::remove_cv_t<T>, iterator::reference == T&
{ // const_iterator::value_type == std::remove_cvref_t<T>, const_iterator::reference == const T&, iterator::value_type = std::remove_cvref_t<T>, iterator::reference == T&
// std::remove_cv_t is impossible for optional<T&>
auto it = opt.begin();
auto it2 = std::as_const(opt).begin();
assert((std::is_same_v<typename decltype(it)::value_type, std::remove_cv_t<T>>));
assert((std::is_same_v<typename decltype(it)::reference, T&>));
assert((std::is_same_v<typename decltype(it2)::value_type, std::remove_cv_t<T>>));
assert((std::is_same_v<typename decltype(it2)::reference, const T&>));
assert((std::is_same_v<typename decltype(it)::value_type, std::remove_cvref_t<T>>));
assert((std::is_same_v<typename decltype(it)::reference, std::remove_reference_t<T>&>));
assert((std::is_same_v<typename decltype(it2)::value_type, std::remove_cvref_t<T>>));
assert((std::is_same_v<typename decltype(it2)::reference, const std::remove_reference_t<T>&>));
}

{ // std::ranges::size for an engaged optional<T> == 1, disengaged optional<T> == 0
Expand All @@ -68,13 +77,13 @@ constexpr bool test() {
// An optional with value that is reset will have a begin() == end(), then when it is reassigned a value,
// begin() != end(), and *begin() will contain the new value.
{
std::optional<T> val{__val};
std::optional<T> val{v};
assert(val.begin() != val.end());
val.reset();
assert(val.begin() == val.end());
val.emplace(__val);
val.emplace(v);
assert(val.begin() != val.end());
assert(*(val.begin()) == __val);
assert(*(val.begin()) == v);
}

return true;
Expand All @@ -86,6 +95,15 @@ constexpr bool tests() {
assert((test<bool, true>()));
assert((test<const int, 2>()));
assert((test<const char, 'b'>()));
assert((test<int&, 1>()));
assert((test<char&, 'a'>()));
assert((test<bool&, true>()));
assert((test<const int&, 2>()));
assert((test<const char&, 'b'>()));

assert(!test_range_concept<int (&)()>());
assert(!test_range_concept<int (&)[]>());
assert(!test_range_concept<int (&)[42]>());

return true;
}
Expand Down
Loading
Loading