Skip to content
Open
Show file tree
Hide file tree
Changes from 14 commits
Commits
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 @@ -480,6 +480,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>`__)
- P3168R2: Give ``std::optional`` Range Support (`Github <https://llvm.org/PR105430>`__)

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
135 changes: 92 additions & 43 deletions libcxx/include/optional
Original file line number Diff line number Diff line change
Expand Up @@ -412,9 +412,6 @@ struct __optional_storage_base : __optional_destruct_base<_Tp> {
}
};

// optional<T&> is currently required to be ill-formed. However, it may
// be allowed in the future. For this reason, it has already been implemented
// to ensure we can make the change in an ABI-compatible manner.
template <class _Tp>
struct __optional_storage_base<_Tp, true> {
using value_type = _Tp;
Expand Down Expand Up @@ -607,19 +604,19 @@ struct __is_std_optional : false_type {};
template <class _Tp>
struct __is_std_optional<optional<_Tp>> : true_type {};

template <class _Tp>
class _LIBCPP_DECLSPEC_EMPTY_BASES optional
: private __optional_move_assign_base<_Tp>,
private __optional_sfinae_ctor_base_t<_Tp>,
private __optional_sfinae_assign_base_t<_Tp> {
using __base _LIBCPP_NODEBUG = __optional_move_assign_base<_Tp>;
template <class _Tp, class = void>
struct __optional_iterator {};

using __pointer _LIBCPP_NODEBUG = std::add_pointer_t<_Tp>;
using __const_pointer _LIBCPP_NODEBUG = std::add_pointer_t<const _Tp>;
template <class _Tp>
struct __optional_iterator<
_Tp,
enable_if_t<!(is_lvalue_reference_v<_Tp> && is_function_v<__libcpp_remove_reference_t<_Tp>>) &&
!(is_lvalue_reference_v<_Tp> && is_array_v<__libcpp_remove_reference_t<_Tp>>)> > {
private:
using __pointer _LIBCPP_NODEBUG = std::add_pointer_t<remove_reference_t<_Tp>>;
using __const_pointer _LIBCPP_NODEBUG = std::add_pointer_t<const remove_reference_t<_Tp>>;

public:
using value_type = _Tp;

# if _LIBCPP_STD_VER >= 26
# ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
using iterator = __bounded_iter<__wrap_iter<__pointer>>;
Expand All @@ -628,18 +625,80 @@ public:
using iterator = __wrap_iter<__pointer>;
using const_iterator = __wrap_iter<__const_pointer>;
# endif

// [optional.iterators], iterator support
_LIBCPP_HIDE_FROM_ABI constexpr iterator begin() noexcept {
auto& __derived_self = static_cast<optional<_Tp>&>(*this);
auto __ptr = [&__derived_self]() {
if constexpr (is_lvalue_reference_v<_Tp>) {
return __derived_self.has_value() ? std::addressof(__derived_self.__get()) : nullptr;
}
return std::addressof(__derived_self.__get());
}();

# ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
return std::__make_bounded_iter(
std::__wrap_iter<__pointer>(__ptr),
std::__wrap_iter<__pointer>(__ptr),
std::__wrap_iter<__pointer>(__ptr) + (__derived_self.has_value() ? 1 : 0));
# else
return iterator(__ptr);
# endif
}

_LIBCPP_HIDE_FROM_ABI constexpr const_iterator begin() const noexcept {
auto& __derived_self = static_cast<const optional<_Tp>&>(*this);
auto* __ptr = [&__derived_self]() {
if constexpr (is_lvalue_reference_v<_Tp>) {
return __derived_self.has_value() ? std::addressof(__derived_self.__get()) : nullptr;
}
return std::addressof(__derived_self.__get());
}();

# ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
return std::__make_bounded_iter(
std::__wrap_iter<__const_pointer>(__ptr),
std::__wrap_iter<__const_pointer>(__ptr),
std::__wrap_iter<__const_pointer>(__ptr) + (__derived_self.has_value() ? 1 : 0));
# else
return const_iterator(__ptr);
# endif
}

_LIBCPP_HIDE_FROM_ABI constexpr iterator end() noexcept {
return begin() + (static_cast<optional<_Tp>&>(*this).has_value() ? 1 : 0);
}
_LIBCPP_HIDE_FROM_ABI constexpr const_iterator end() const noexcept {
return begin() + (static_cast<const optional<_Tp>&>(*this).has_value() ? 1 : 0);
}
# endif
};

template <class _Tp>
class _LIBCPP_DECLSPEC_EMPTY_BASES optional
: private __optional_move_assign_base<_Tp>,
private __optional_sfinae_ctor_base_t<_Tp>,
private __optional_sfinae_assign_base_t<_Tp>,
public __optional_iterator<_Tp> {
using __base _LIBCPP_NODEBUG = __optional_move_assign_base<_Tp>;

public:
using value_type = _Tp;

using __trivially_relocatable _LIBCPP_NODEBUG =
conditional_t<__libcpp_is_trivially_relocatable<_Tp>::value, optional, void>;
using __replaceable _LIBCPP_NODEBUG = conditional_t<__is_replaceable_v<_Tp>, optional, void>;

private:
// Disable the reference extension using this static assert.
static_assert(!is_same_v<__remove_cvref_t<value_type>, in_place_t>,
"instantiation of optional with in_place_t is ill-formed");
static_assert(!is_same_v<__remove_cvref_t<value_type>, nullopt_t>,
"instantiation of optional with nullopt_t is ill-formed");
static_assert(!is_reference_v<value_type>, "instantiation of optional with a reference type is ill-formed");
# if _LIBCPP_STD_VER >= 26
static_assert(!is_rvalue_reference_v<_Tp>, "instantiation of optional with an rvalue reference type is ill-formed");
# else
static_assert(!is_reference_v<_Tp>, "instantiation of optional with a reference type is ill-formed");
# endif
static_assert(is_destructible_v<value_type>, "instantiation of optional with a non-destructible type is ill-formed");
static_assert(!is_array_v<value_type>, "instantiation of optional with an array type is ill-formed");

Expand Down Expand Up @@ -833,34 +892,6 @@ public:
}
}

# if _LIBCPP_STD_VER >= 26
// [optional.iterators], iterator support
_LIBCPP_HIDE_FROM_ABI constexpr iterator begin() noexcept {
# ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
return std::__make_bounded_iter(
std::__wrap_iter<__pointer>(std::addressof(this->__get())),
std::__wrap_iter<__pointer>(std::addressof(this->__get())),
std::__wrap_iter<__pointer>(std::addressof(this->__get()) + (this->has_value() ? 1 : 0)));
# else
return iterator(std::addressof(this->__get()));
# endif
}

_LIBCPP_HIDE_FROM_ABI constexpr const_iterator begin() const noexcept {
# ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
return std::__make_bounded_iter(
std::__wrap_iter<__const_pointer>(std::addressof(this->__get())),
std::__wrap_iter<__const_pointer>(std::addressof(this->__get())),
std::__wrap_iter<__const_pointer>(std::addressof(this->__get()) + (this->has_value() ? 1 : 0)));
# else
return const_iterator(std::addressof(this->__get()));
# endif
}

_LIBCPP_HIDE_FROM_ABI constexpr iterator end() noexcept { return begin() + (this->has_value() ? 1 : 0); }
_LIBCPP_HIDE_FROM_ABI constexpr const_iterator end() const noexcept { return begin() + (this->has_value() ? 1 : 0); }
# endif

_LIBCPP_HIDE_FROM_ABI constexpr add_pointer_t<value_type const> operator->() const noexcept {
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator-> called on a disengaged value");
return std::addressof(this->__get());
Expand Down Expand Up @@ -921,19 +952,37 @@ public:
}

template <class _Up = remove_cv_t<_Tp>>
# if _LIBCPP_STD_VER >= 26
requires(!(is_lvalue_reference_v<_Tp> && is_function_v<__libcpp_remove_reference_t<_Tp>>) &&
!(is_lvalue_reference_v<_Tp> && is_array_v<__libcpp_remove_reference_t<_Tp>>))
# endif
_LIBCPP_HIDE_FROM_ABI constexpr value_type value_or(_Up&& __v) const& {
static_assert(is_copy_constructible_v<value_type>, "optional<T>::value_or: T must be copy constructible");
static_assert(is_convertible_v<_Up, value_type>, "optional<T>::value_or: U must be convertible to T");
return this->has_value() ? this->__get() : static_cast<value_type>(std::forward<_Up>(__v));
}

template <class _Up = remove_cv_t<_Tp>>
# if _LIBCPP_STD_VER >= 26
requires(!is_lvalue_reference_v<_Tp>)
# endif
_LIBCPP_HIDE_FROM_ABI constexpr value_type value_or(_Up&& __v) && {
static_assert(is_move_constructible_v<value_type>, "optional<T>::value_or: T must be move constructible");
static_assert(is_convertible_v<_Up, value_type>, "optional<T>::value_or: U must be convertible to T");
return this->has_value() ? std::move(this->__get()) : static_cast<value_type>(std::forward<_Up>(__v));
}

# if _LIBCPP_STD_VER >= 26
template <class _Up = remove_cv_t<_Tp>>
requires(is_lvalue_reference_v<_Tp> &&
!(is_function_v<__libcpp_remove_reference_t<_Tp>> || is_array_v<__libcpp_remove_reference_t<_Tp>>))
_LIBCPP_HIDE_FROM_ABI constexpr value_type value_or(_Up&& __v) && {
static_assert(is_move_constructible_v<value_type>, "optional<T>::value_or: T must be move constructible");
static_assert(is_convertible_v<_Up, value_type>, "optional<T>::value_or: U must be convertible to T");
return this->has_value() ? this->__get() : static_cast<value_type>(std::forward<_Up>(__v));
}
# endif

# if _LIBCPP_STD_VER >= 23
template <class _Func>
_LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) & {
Expand Down
5 changes: 4 additions & 1 deletion libcxx/include/version
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,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 @@ -586,6 +587,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
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]>>);
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 @@ -7453,8 +7453,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&>());
return true;
}

Expand Down
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
Loading
Loading