CaseyCarter updated this revision to Diff 97188.
CaseyCarter retitled this revision from "[libcxx] Implement LWG 2900 "The copy
and move constructors of optional are not constexpr"" to "[libcxx] optional:
Implement LWG 2900 and P0602".
CaseyCarter edited the summary of this revision.
CaseyCarter added a comment.
- Define new macro `_MSVC_STL_VER` in msvc_stdlib_force_include.hpp to indicate
that the VC++ standard library is being tested.
- Implement test coverage for P0602 "variant and optional should propagate
copy/move triviality".
https://reviews.llvm.org/D32385
Files:
include/optional
test/std/utilities/optional/optional.object/optional.object.assign/copy.pass.cpp
test/std/utilities/optional/optional.object/optional.object.assign/move.pass.cpp
test/std/utilities/optional/optional.object/optional.object.ctor/copy.pass.cpp
test/std/utilities/optional/optional.object/optional.object.ctor/move.pass.cpp
test/support/msvc_stdlib_force_include.hpp
Index: test/support/msvc_stdlib_force_include.hpp
===================================================================
--- test/support/msvc_stdlib_force_include.hpp
+++ test/support/msvc_stdlib_force_include.hpp
@@ -26,6 +26,11 @@
#error This header may not be used when targeting libc++
#endif
+// Indicates that we are using the MSVC standard library.
+#ifndef _MSVC_STL_VER
+#define _MSVC_STL_VER 42
+#endif
+
struct AssertionDialogAvoider {
AssertionDialogAvoider() {
_CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE);
Index: test/std/utilities/optional/optional.object/optional.object.ctor/move.pass.cpp
===================================================================
--- test/std/utilities/optional/optional.object/optional.object.ctor/move.pass.cpp
+++ test/std/utilities/optional/optional.object/optional.object.ctor/move.pass.cpp
@@ -10,7 +10,10 @@
// UNSUPPORTED: c++98, c++03, c++11, c++14
// <optional>
-// optional(optional<T>&& rhs);
+// constexpr optional(const optional<T>&& rhs);
+// If is_trivially_move_constructible_v<T> is true,
+// this constructor shall be a constexpr constructor.
+
#include <optional>
#include <type_traits>
@@ -36,10 +39,10 @@
void test_throwing_ctor() {
#ifndef TEST_HAS_NO_EXCEPTIONS
struct Z {
- Z() : count(0) {}
- Z(Z&& o) : count(o.count + 1)
- { if (count == 2) throw 6; }
- int count;
+ Z() : count(0) {}
+ Z(Z&& o) : count(o.count + 1)
+ { if (count == 2) throw 6; }
+ int count;
};
Z z;
optional<Z> rhs(std::move(z));
@@ -131,6 +134,48 @@
#endif
}
+constexpr bool test_constexpr()
+{
+ {
+ using T = int;
+ optional<T> o1{};
+ optional<T> o2 = std::move(o1);
+ static_cast<void>(o2);
+ optional<T> o3{T{42}};
+ optional<T> o4 = std::move(o3);
+ static_cast<void>(o4);
+ }
+ {
+ struct T {
+ constexpr T(int) {}
+ T(T&&) = default;
+ };
+ optional<T> o1{};
+ optional<T> o2 = std::move(o1);
+ static_cast<void>(o2);
+ optional<T> o3{T{42}};
+ optional<T> o4 = std::move(o3);
+ static_cast<void>(o4);
+ }
+ return true;
+}
+static_assert(test_constexpr(), "");
+
+template<class T>
+constexpr bool triviality_test =
+ std::is_trivially_move_constructible<optional<T>>::value ==
+ std::is_trivially_move_constructible<T>::value;
+
+void test_triviality_extension() {
+#if defined(_LIBCPP_VER) || defined(_MSVC_STL_VER)
+ static_assert(triviality_test<int>, "");
+ static_assert(triviality_test<optional<int>>, "");
+ static_assert(triviality_test<TestTypes::TestType>, "");
+ static_assert(triviality_test<ConstexprTestTypes::TestType>, "");
+ static_assert(triviality_test<NonTrivialTypes::AllCtors>, "");
+ static_assert(triviality_test<TrivialTestTypes::TestType>, "");
+#endif
+}
int main()
{
@@ -178,9 +223,9 @@
test<TestType>();
test<TestType>(42);
}
- {
- test_throwing_ctor();
- }
+
+ test_throwing_ctor();
+
{
struct ThrowsMove {
ThrowsMove() noexcept(false) {}
@@ -195,7 +240,7 @@
};
static_assert(std::is_nothrow_move_constructible<optional<NoThrowMove>>::value, "");
}
- {
- test_reference_extension();
- }
+
+ test_reference_extension();
+ test_triviality_extension();
}
Index: test/std/utilities/optional/optional.object/optional.object.ctor/copy.pass.cpp
===================================================================
--- test/std/utilities/optional/optional.object/optional.object.ctor/copy.pass.cpp
+++ test/std/utilities/optional/optional.object/optional.object.ctor/copy.pass.cpp
@@ -10,7 +10,9 @@
// UNSUPPORTED: c++98, c++03, c++11, c++14
// <optional>
-// optional(const optional<T>& rhs);
+// constexpr optional(const optional<T>& rhs);
+// If is_trivially_copy_constructible_v<T> is true,
+// this constructor shall be a constexpr constructor.
#include <optional>
#include <type_traits>
@@ -35,10 +37,10 @@
void test_throwing_ctor() {
#ifndef TEST_HAS_NO_EXCEPTIONS
struct Z {
- Z() : count(0) {}
- Z(Z const& o) : count(o.count + 1)
- { if (count == 2) throw 6; }
- int count;
+ Z() : count(0) {}
+ Z(Z const& o) : count(o.count + 1)
+ { if (count == 2) throw 6; }
+ int count;
};
const Z z;
const optional<Z> rhs(z);
@@ -104,6 +106,48 @@
#endif
}
+constexpr bool test_constexpr()
+{
+ {
+ using T = int;
+ optional<T> o1{};
+ optional<T> o2 = o1;
+ static_cast<void>(o2);
+ optional<T> o3{T{42}};
+ optional<T> o4 = o3;
+ static_cast<void>(o4);
+ }
+ {
+ struct T {
+ constexpr T(int) {}
+ };
+ optional<T> o1{};
+ optional<T> o2 = o1;
+ static_cast<void>(o2);
+ optional<T> o3{T{42}};
+ optional<T> o4 = o3;
+ static_cast<void>(o4);
+ }
+ return true;
+}
+static_assert(test_constexpr(), "");
+
+template<class T>
+constexpr bool triviality_test =
+ std::is_trivially_copy_constructible<optional<T>>::value ==
+ std::is_trivially_copy_constructible<T>::value;
+
+void test_triviality_extension() {
+#if defined(_LIBCPP_VER) || defined(_MSVC_STL_VER)
+ static_assert(triviality_test<int>, "");
+ static_assert(triviality_test<optional<int>>, "");
+ static_assert(triviality_test<TestTypes::TestType>, "");
+ static_assert(triviality_test<ConstexprTestTypes::TestType>, "");
+ static_assert(triviality_test<NonTrivialTypes::AllCtors>, "");
+ static_assert(triviality_test<TrivialTestTypes::TestType>, "");
+#endif
+}
+
int main()
{
test<int>();
@@ -146,10 +190,7 @@
test<TestType>();
test<TestType>(42);
}
- {
- test_throwing_ctor();
- }
- {
- test_reference_extension();
- }
+ test_throwing_ctor();
+ test_reference_extension();
+ test_triviality_extension();
}
Index: test/std/utilities/optional/optional.object/optional.object.assign/move.pass.cpp
===================================================================
--- test/std/utilities/optional/optional.object/optional.object.assign/move.pass.cpp
+++ test/std/utilities/optional/optional.object/optional.object.assign/move.pass.cpp
@@ -51,6 +51,24 @@
bool X::throw_now = false;
int X::alive = 0;
+template<class T>
+constexpr bool triviality_test =
+ std::is_trivially_move_assignable<optional<T>>::value ==
+ (std::is_trivially_destructible<T>::value &&
+ std::is_trivially_move_constructible<T>::value &&
+ std::is_trivially_move_assignable<T>::value);
+
+void test_triviality_extension() {
+#if defined(_LIBCPP_VER) || defined(_MSVC_STL_VER)
+ static_assert(triviality_test<int>, "");
+ static_assert(triviality_test<optional<int>>, "");
+ static_assert(triviality_test<TrivialTestTypes::TestType>, "");
+ static_assert(triviality_test<TestTypes::TestType>, "");
+ static_assert(triviality_test<X>, "");
+ static_assert(triviality_test<Y>, "");
+#endif
+}
+
int main()
{
{
@@ -147,28 +165,30 @@
}
{
struct ThrowsMove {
- ThrowsMove() noexcept {}
- ThrowsMove(ThrowsMove const&) noexcept {}
- ThrowsMove(ThrowsMove &&) noexcept(false) {}
- ThrowsMove& operator=(ThrowsMove const&) noexcept { return *this; }
- ThrowsMove& operator=(ThrowsMove &&) noexcept { return *this; }
+ ThrowsMove() noexcept {}
+ ThrowsMove(ThrowsMove const&) noexcept {}
+ ThrowsMove(ThrowsMove &&) noexcept(false) {}
+ ThrowsMove& operator=(ThrowsMove const&) noexcept { return *this; }
+ ThrowsMove& operator=(ThrowsMove &&) noexcept { return *this; }
};
static_assert(!std::is_nothrow_move_assignable<optional<ThrowsMove>>::value, "");
struct ThrowsMoveAssign {
- ThrowsMoveAssign() noexcept {}
- ThrowsMoveAssign(ThrowsMoveAssign const&) noexcept {}
- ThrowsMoveAssign(ThrowsMoveAssign &&) noexcept {}
- ThrowsMoveAssign& operator=(ThrowsMoveAssign const&) noexcept { return *this; }
- ThrowsMoveAssign& operator=(ThrowsMoveAssign &&) noexcept(false) { return *this; }
+ ThrowsMoveAssign() noexcept {}
+ ThrowsMoveAssign(ThrowsMoveAssign const&) noexcept {}
+ ThrowsMoveAssign(ThrowsMoveAssign &&) noexcept {}
+ ThrowsMoveAssign& operator=(ThrowsMoveAssign const&) noexcept { return *this; }
+ ThrowsMoveAssign& operator=(ThrowsMoveAssign &&) noexcept(false) { return *this; }
};
static_assert(!std::is_nothrow_move_assignable<optional<ThrowsMoveAssign>>::value, "");
struct NoThrowMove {
- NoThrowMove() noexcept(false) {}
- NoThrowMove(NoThrowMove const&) noexcept(false) {}
- NoThrowMove(NoThrowMove &&) noexcept {}
- NoThrowMove& operator=(NoThrowMove const&) noexcept { return *this; }
- NoThrowMove& operator=(NoThrowMove&&) noexcept { return *this; }
+ NoThrowMove() noexcept(false) {}
+ NoThrowMove(NoThrowMove const&) noexcept(false) {}
+ NoThrowMove(NoThrowMove &&) noexcept {}
+ NoThrowMove& operator=(NoThrowMove const&) noexcept { return *this; }
+ NoThrowMove& operator=(NoThrowMove&&) noexcept { return *this; }
};
static_assert(std::is_nothrow_move_assignable<optional<NoThrowMove>>::value, "");
}
+
+ test_triviality_extension();
}
Index: test/std/utilities/optional/optional.object/optional.object.assign/copy.pass.cpp
===================================================================
--- test/std/utilities/optional/optional.object/optional.object.assign/copy.pass.cpp
+++ test/std/utilities/optional/optional.object/optional.object.assign/copy.pass.cpp
@@ -49,6 +49,24 @@
return lhs.has_value() && rhs.has_value() && *lhs == *rhs;
}
+
+template<class T>
+constexpr bool triviality_test =
+ std::is_trivially_copy_assignable<optional<T>>::value ==
+ (std::is_trivially_destructible<T>::value &&
+ std::is_trivially_copy_constructible<T>::value &&
+ std::is_trivially_copy_assignable<T>::value);
+
+void test_triviality_extension() {
+#if defined(_LIBCPP_VER) || defined(_MSVC_STL_VER)
+ static_assert(triviality_test<int>, "");
+ static_assert(triviality_test<optional<int>>, "");
+ static_assert(triviality_test<TrivialTestTypes::TestType>, "");
+ static_assert(triviality_test<TestTypes::TestType>, "");
+ static_assert(triviality_test<X>, "");
+#endif
+}
+
int main()
{
{
@@ -99,4 +117,6 @@
}
}
#endif
+
+ test_triviality_extension();
}
Index: include/optional
===================================================================
--- include/optional
+++ include/optional
@@ -436,46 +436,122 @@
}
};
-template <class _Tp, bool = is_trivially_copyable<_Tp>::value>
-struct __optional_storage;
-
-template <class _Tp>
-struct __optional_storage<_Tp, true> : __optional_storage_base<_Tp>
+template <class _Tp, bool = is_trivially_copy_constructible<_Tp>::value>
+struct __optional_copy_base : __optional_storage_base<_Tp>
{
using __optional_storage_base<_Tp>::__optional_storage_base;
};
template <class _Tp>
-struct __optional_storage<_Tp, false> : __optional_storage_base<_Tp>
+struct __optional_copy_base<_Tp, false> : __optional_storage_base<_Tp>
{
- using value_type = _Tp;
using __optional_storage_base<_Tp>::__optional_storage_base;
_LIBCPP_INLINE_VISIBILITY
- __optional_storage() = default;
+ __optional_copy_base() = default;
_LIBCPP_INLINE_VISIBILITY
- __optional_storage(const __optional_storage& __opt)
+ __optional_copy_base(const __optional_copy_base& __opt)
{
this->__construct_from(__opt);
}
_LIBCPP_INLINE_VISIBILITY
- __optional_storage(__optional_storage&& __opt)
+ __optional_copy_base(__optional_copy_base&&) = default;
+ _LIBCPP_INLINE_VISIBILITY
+ __optional_copy_base& operator=(const __optional_copy_base&) = default;
+ _LIBCPP_INLINE_VISIBILITY
+ __optional_copy_base& operator=(__optional_copy_base&&) = default;
+};
+
+template <class _Tp, bool = is_trivially_move_constructible<_Tp>::value>
+struct __optional_move_base : __optional_copy_base<_Tp>
+{
+ using __optional_copy_base<_Tp>::__optional_copy_base;
+};
+
+template <class _Tp>
+struct __optional_move_base<_Tp, false> : __optional_copy_base<_Tp>
+{
+ using value_type = _Tp;
+ using __optional_copy_base<_Tp>::__optional_copy_base;
+
+ _LIBCPP_INLINE_VISIBILITY
+ __optional_move_base() = default;
+ _LIBCPP_INLINE_VISIBILITY
+ __optional_move_base(const __optional_move_base&) = default;
+
+ _LIBCPP_INLINE_VISIBILITY
+ __optional_move_base(__optional_move_base&& __opt)
noexcept(is_nothrow_move_constructible_v<value_type>)
{
this->__construct_from(_VSTD::move(__opt));
}
_LIBCPP_INLINE_VISIBILITY
- __optional_storage& operator=(const __optional_storage& __opt)
+ __optional_move_base& operator=(const __optional_move_base&) = default;
+ _LIBCPP_INLINE_VISIBILITY
+ __optional_move_base& operator=(__optional_move_base&&) = default;
+};
+
+template <class _Tp, bool =
+ is_trivially_destructible<_Tp>::value &&
+ is_trivially_copy_constructible<_Tp>::value &&
+ is_trivially_copy_assignable<_Tp>::value>
+struct __optional_copy_assign_base : __optional_move_base<_Tp>
+{
+ using __optional_move_base<_Tp>::__optional_move_base;
+};
+
+template <class _Tp>
+struct __optional_copy_assign_base<_Tp, false> : __optional_move_base<_Tp>
+{
+ using __optional_move_base<_Tp>::__optional_move_base;
+
+ _LIBCPP_INLINE_VISIBILITY
+ __optional_copy_assign_base() = default;
+ _LIBCPP_INLINE_VISIBILITY
+ __optional_copy_assign_base(const __optional_copy_assign_base&) = default;
+ _LIBCPP_INLINE_VISIBILITY
+ __optional_copy_assign_base(__optional_copy_assign_base&&) = default;
+
+ _LIBCPP_INLINE_VISIBILITY
+ __optional_copy_assign_base& operator=(const __optional_copy_assign_base& __opt)
{
this->__assign_from(__opt);
return *this;
}
_LIBCPP_INLINE_VISIBILITY
- __optional_storage& operator=(__optional_storage&& __opt)
+ __optional_copy_assign_base& operator=(__optional_copy_assign_base&&) = default;
+};
+
+template <class _Tp, bool =
+ is_trivially_destructible<_Tp>::value &&
+ is_trivially_move_constructible<_Tp>::value &&
+ is_trivially_move_assignable<_Tp>::value>
+struct __optional_move_assign_base : __optional_copy_assign_base<_Tp>
+{
+ using __optional_copy_assign_base<_Tp>::__optional_copy_assign_base;
+};
+
+template <class _Tp>
+struct __optional_move_assign_base<_Tp, false> : __optional_copy_assign_base<_Tp>
+{
+ using value_type = _Tp;
+ using __optional_copy_assign_base<_Tp>::__optional_copy_assign_base;
+
+ _LIBCPP_INLINE_VISIBILITY
+ __optional_move_assign_base() = default;
+ _LIBCPP_INLINE_VISIBILITY
+ __optional_move_assign_base(const __optional_move_assign_base& __opt) = default;
+ _LIBCPP_INLINE_VISIBILITY
+ __optional_move_assign_base(__optional_move_assign_base&&) = default;
+ _LIBCPP_INLINE_VISIBILITY
+ __optional_move_assign_base& operator=(const __optional_move_assign_base&) = default;
+
+ _LIBCPP_INLINE_VISIBILITY
+ __optional_move_assign_base& operator=(__optional_move_assign_base&& __opt)
noexcept(is_nothrow_move_assignable_v<value_type> &&
is_nothrow_move_constructible_v<value_type>)
{
@@ -498,11 +574,11 @@
template <class _Tp>
class optional
- : private __optional_storage<_Tp>
+ : private __optional_move_assign_base<_Tp>
, private __optional_sfinae_ctor_base_t<_Tp>
, private __optional_sfinae_assign_base_t<_Tp>
{
- using __base = __optional_storage<_Tp>;
+ using __base = __optional_move_assign_base<_Tp>;
public:
using value_type = _Tp;
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits