Tested on Linux-x64. N.B. I know that the assignment operators have constraints in template parameters, I want to move them to the return type separately.
N.B2. This fixes the bug for std::optional only; I plan to port the fix to std::experimental::optional in a separate patch, and then backport that separate fix to 6.x. N.B3. The newest p/r for LWG 2756 is not up yet in the issue list, and hasn't been looked at by LEWG/LWG, but neither was the previous one. :) Nevertheless, this new p/r and the patch are a massive improvement, and keeps existing code and expectations working, makes all sorts of interesting types work, and keeps the conversion functionality. 2016-08-23 Ville Voutilainen <ville.voutilai...@gmail.com> PR libstdc++/77288 * include/std/optional (__is_optional_impl, __is_optional): Remove. (__converts_from_optional, __assigns_from_optional): New. (optional(_Up&&)): Use is_same instead of __is_optional. (optional(const optional<_Up>&)): Constrain with __converts_from_optional. (optional(optional<_Up>&&)): Likewise. (operator=(_Up&&)): Use is_same instead of __is_optional. (operator=(const optional<_Up>&)): Constrain with __converts_from_optional and __assigns_from_optional. (operator=(optional<_Up>&&)): Likewise. * testsuite/20_util/optional/77288.cc: New. * testsuite/20_util/optional/cons/value.cc: Adjust.
diff --git a/libstdc++-v3/include/std/optional b/libstdc++-v3/include/std/optional index 2ff75ea..af716f7 100644 --- a/libstdc++-v3/include/std/optional +++ b/libstdc++-v3/include/std/optional @@ -423,19 +423,23 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template<typename _Tp> class optional; - template<typename> - struct __is_optional_impl : false_type - { }; - - template<typename _Tp> - struct __is_optional_impl<optional<_Tp>> : true_type - { }; - - template<typename _Tp> - struct __is_optional - : public __is_optional_impl<std::remove_cv_t<std::remove_reference_t<_Tp>>> - { }; - + template<typename _Tp, typename _Up> + using __converts_from_optional = + __or_<is_constructible<_Tp, const optional<_Up>&>, + is_constructible<_Tp, optional<_Up>&>, + is_constructible<_Tp, const optional<_Up>&&>, + is_constructible<_Tp, optional<_Up>&&>, + is_convertible<const optional<_Up>&, _Tp>, + is_convertible<optional<_Up>&, _Tp>, + is_convertible<const optional<_Up>&&, _Tp>, + is_convertible<optional<_Up>&&, _Tp>>; + + template<typename _Tp, typename _Up> + using __assigns_from_optional = + __or_<is_assignable<_Tp&, const optional<_Up>&>, + is_assignable<_Tp&, optional<_Up>&>, + is_assignable<_Tp&, const optional<_Up>&&>, + is_assignable<_Tp&, optional<_Up>&&>>; /** * @brief Class template for optional values. @@ -474,7 +478,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION // Converting constructors for engaged optionals. template <typename _Up = _Tp, enable_if_t<__and_< - __not_<__is_optional<decay_t<_Up>>>, + __not_<is_same<optional<_Tp>, decay_t<_Up>>>, is_constructible<_Tp, _Up&&>, is_convertible<_Up&&, _Tp> >::value, bool> = true> @@ -483,7 +487,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template <typename _Up = _Tp, enable_if_t<__and_< - __not_<__is_optional<decay_t<_Up>>>, + __not_<is_same<optional<_Tp>, decay_t<_Up>>>, is_constructible<_Tp, _Up&&>, __not_<is_convertible<_Up&&, _Tp>> >::value, bool> = false> @@ -494,7 +498,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION enable_if_t<__and_< __not_<is_same<_Tp, _Up>>, is_constructible<_Tp, const _Up&>, - is_convertible<const _Up&, _Tp> + is_convertible<const _Up&, _Tp>, + __not_<__converts_from_optional<_Tp, _Up>> >::value, bool> = true> constexpr optional(const optional<_Up>& __t) : _Base(__t ? _Base(std::in_place, *__t) : _Base()) { } @@ -503,7 +508,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION enable_if_t<__and_< __not_<is_same<_Tp, _Up>>, is_constructible<_Tp, const _Up&>, - __not_<is_convertible<const _Up&, _Tp>> + __not_<is_convertible<const _Up&, _Tp>>, + __not_<__converts_from_optional<_Tp, _Up>> >::value, bool> = false> explicit constexpr optional(const optional<_Up>& __t) : _Base(__t ? _Base(std::in_place, *__t) : _Base()) { } @@ -512,7 +518,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION enable_if_t<__and_< __not_<is_same<_Tp, _Up>>, is_constructible<_Tp, _Up&&>, - is_convertible<_Up&&, _Tp> + is_convertible<_Up&&, _Tp>, + __not_<__converts_from_optional<_Tp, _Up>> >::value, bool> = true> constexpr optional(optional<_Up>&& __t) : _Base(__t ? _Base(std::in_place, std::move(*__t)) : _Base()) { } @@ -521,7 +528,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION enable_if_t<__and_< __not_<is_same<_Tp, _Up>>, is_constructible<_Tp, _Up&&>, - __not_<is_convertible<_Up&&, _Tp>> + __not_<is_convertible<_Up&&, _Tp>>, + __not_<__converts_from_optional<_Tp, _Up>> >::value, bool> = false> explicit constexpr optional(optional<_Up>&& __t) : _Base(__t ? _Base(std::in_place, std::move(*__t)) : _Base()) { } @@ -550,7 +558,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template<typename _Up = _Tp, enable_if_t<__and_< - __not_<__is_optional<decay_t<_Up>>>, + __not_<is_same<optional<_Tp>, decay_t<_Up>>>, is_constructible<_Tp, _Up>, is_assignable<_Tp&, _Up>>::value, bool> = true> @@ -568,8 +576,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template<typename _Up, enable_if_t<__and_< __not_<is_same<_Tp, _Up>>, - is_constructible<_Tp, _Up>, - is_assignable<_Tp&, _Up>>::value, + is_constructible<_Tp, const _Up&>, + is_assignable<_Tp&, _Up>, + __not_<__converts_from_optional<_Tp, _Up>>, + __not_<__assigns_from_optional<_Tp, _Up>> + >::value, bool> = true> optional& operator=(const optional<_Up>& __u) @@ -592,7 +603,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION enable_if_t<__and_< __not_<is_same<_Tp, _Up>>, is_constructible<_Tp, _Up>, - is_assignable<_Tp&, _Up>>::value, + is_assignable<_Tp&, _Up>, + __not_<__converts_from_optional<_Tp, _Up>>, + __not_<__assigns_from_optional<_Tp, _Up>> + >::value, bool> = true> optional& operator=(optional<_Up>&& __u) diff --git a/libstdc++-v3/testsuite/20_util/optional/77288.cc b/libstdc++-v3/testsuite/20_util/optional/77288.cc new file mode 100644 index 0000000..54d7cd8 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/optional/77288.cc @@ -0,0 +1,391 @@ +// { dg-options "-std=gnu++17" } +// { dg-do run } + +// Copyright (C) 2016 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a moved_to of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// <http://www.gnu.org/licenses/>. + +#include <exception> +#include <optional> +#include <any> + +using std::optional; + +#include <testsuite_hooks.h> + +void test01() +{ + optional<optional<std::exception_ptr>> nested_element; + optional<std::exception_ptr> element = {}; + nested_element = element; + VERIFY(nested_element); +} + +template <class T> +struct service_result +{ + static optional<T> get_result() + { + T sr; + return sr; + } + + static optional<T> get_result_with_cond(bool cond) + { + if (cond) + return T{}; + return {}; + } +}; + +void test02() +{ + VERIFY(service_result<int>::get_result()); + VERIFY(service_result<optional<int>>::get_result()); + VERIFY(service_result<int>::get_result_with_cond(true)); + VERIFY(service_result<optional<int>>::get_result_with_cond(true)); + VERIFY(!service_result<int>::get_result_with_cond(false)); + VERIFY(!service_result<optional<int>>::get_result_with_cond(false)); +} + +struct Widget +{ + Widget(int) {} + Widget(optional<int>) {} +}; + + +void test03() +{ + optional<Widget> w; + w = optional<int>(); + VERIFY(w); + static_assert(!std::is_assignable_v<optional<Widget>&, + optional<short>>);; + w = optional<optional<int>>(); + VERIFY(!w); + static_assert(!std::is_assignable_v<optional<Widget>&, + optional<optional<short>>>);; + + optional<Widget> w2{optional<int>()}; + VERIFY(w2); + optional<Widget> w3 = optional<int>(); + VERIFY(w3); + optional<Widget> w4{optional<short>()}; + VERIFY(w4); + static_assert(!std::is_convertible_v<optional<short>&&, optional<Widget>>); + + optional<Widget> w6{optional<optional<int>>()}; + VERIFY(!w6); + optional<Widget> w7 = optional<optional<int>>(); + VERIFY(!w7); + optional<Widget> w8{optional<optional<short>>()}; + VERIFY(!w8); + static_assert(!std::is_convertible_v<optional<optional<short>>&&, + optional<Widget>>); + optional<Widget> w10{optional<optional<short>>(10)}; + VERIFY(w10); + optional<Widget> w11 = std::nullopt; + VERIFY(!w11); + optional<Widget> w12 = {}; + VERIFY(!w12); + optional<Widget> w13{std::nullopt}; + VERIFY(!w13); + optional<Widget> w14; + w14 = {}; + VERIFY(!w14); +} + +struct Widget2 +{ + Widget2(int) {} + Widget2(optional<int>) {} + Widget2& operator=(int) {return *this;} + Widget2& operator=(optional<int>) {return *this;} +}; + +void test04() +{ + optional<Widget2> w; + w = optional<int>(); + VERIFY(w); + w = optional<short>(); + VERIFY(w); + w = optional<optional<int>>(); + VERIFY(!w); + w = optional<optional<short>>(); + VERIFY(!w); + w = optional<optional<short>>(10); + optional<Widget2> w2 = std::nullopt; + VERIFY(!w2); + optional<Widget2> w3 = {}; + VERIFY(!w3); + optional<Widget2> w4{std::nullopt}; + VERIFY(!w4); + optional<Widget2> w5; + w5 = {}; + VERIFY(!w5); +} + +struct Thingy +{ + Thingy(int) {} + Thingy(Widget) {} +}; + +void test05() +{ + optional<Thingy> ot; + + static_assert(!std::is_assignable_v<optional<Thingy>&, + optional<int>>); + static_assert(std::is_assignable_v<optional<Thingy>&, + optional<short>>); + static_assert(!std::is_assignable_v<optional<Thingy>&, + optional<optional<int>>>); + ot = optional<Widget>(); + VERIFY(!ot); + optional<Thingy> ot2{optional<int>()}; + VERIFY(ot2); + static_assert(!std::is_convertible_v<optional<int>&&, + optional<Thingy>>); + optional<Thingy> ot3{optional<short>()}; + VERIFY(!ot3); + optional<Thingy> ot4 = optional<short>(); + VERIFY(!ot4); + + optional<Thingy> ot5{optional<optional<int>>()}; + VERIFY(!ot5); + static_assert(!std::is_convertible_v<optional<optional<int>>&&, + optional<Thingy>>); + + optional<Thingy> ot7{optional<Widget>()}; + VERIFY(!ot7); + optional<Thingy> ot8 = optional<Widget>(); + VERIFY(!ot8); + static_assert(!std::is_constructible_v<optional<Thingy>, + optional<optional<short>>>); + static_assert(!std::is_convertible_v<optional<optional<short>>, + optional<Thingy>>); + static_assert(!std::is_assignable_v<optional<Thingy>&, + optional<optional<short>>>); + optional<Thingy> ot9 = std::nullopt; + VERIFY(!ot9); + optional<Thingy> ot10 = {}; + VERIFY(!ot10); + optional<Thingy> ot11{std::nullopt}; + VERIFY(!ot11); + optional<Thingy> ot12; + ot12 = {}; + VERIFY(!ot12); +} + +struct RvalueConstructible +{ + RvalueConstructible(int) {} + RvalueConstructible(optional<int>&&) {} +}; + +void test06() +{ + optional<int> oi; + optional<RvalueConstructible> ori; + static_assert(!std::is_assignable_v<optional<RvalueConstructible>&, + optional<int>&>); + ori = std::move(oi); + VERIFY(ori); + + optional<optional<int>> ooi; + static_assert(!std::is_assignable_v<optional<RvalueConstructible>&, + optional<optional<int>>&>); + ori = std::move(ooi); + VERIFY(!ori); + + static_assert(!std::is_constructible_v<optional<RvalueConstructible>, + optional<int>&>); + static_assert(!std::is_convertible_v<optional<int>&, + optional<RvalueConstructible>>); + + optional<RvalueConstructible> ori2(std::move(oi)); + VERIFY(ori2); + optional<RvalueConstructible> ori3 = std::move(oi); + VERIFY(ori3); + + static_assert(!std::is_constructible_v<optional<RvalueConstructible>, + optional<optional<int>>&>); + static_assert(!std::is_convertible_v<optional<optional<int>>&, + optional<RvalueConstructible>>); + optional<RvalueConstructible> ori6(std::move(ooi)); + VERIFY(!ori6); + optional<RvalueConstructible> ori7 = std::move(ooi); + VERIFY(!ori7); + optional<RvalueConstructible> ori8 = std::nullopt; + VERIFY(!ori8); + optional<RvalueConstructible> ori9 = {}; + VERIFY(!ori9); + optional<RvalueConstructible> ori10{std::nullopt}; + VERIFY(!ori10); + optional<RvalueConstructible> ori11; + ori11 = {}; + VERIFY(!ori11); +} + +struct Thingy2 +{ + Thingy2(int) {} + explicit Thingy2(optional<int>) {} + Thingy2(Widget) {} +}; + +void test07() +{ + optional<Thingy2> ot{optional<int>{}}; + VERIFY(ot); + static_assert(!std::is_convertible_v<optional<int>, + optional<Thingy2>>); + optional<Thingy2> ot2{optional<short>{}}; + VERIFY(ot2); + static_assert(!std::is_convertible_v<optional<short>, + optional<Thingy2>>); + optional<Thingy2> ot3{optional<optional<int>>{}}; + VERIFY(!ot3); + static_assert(!std::is_convertible_v<optional<optional<int>>, + optional<Thingy2>>); + optional<Thingy2> ot4{optional<optional<short>>{}}; + VERIFY(!ot4); + static_assert(!std::is_convertible_v<optional<optional<short>>, + optional<Thingy2>>); + + optional<Thingy2> ot5{optional<Widget>{}}; + VERIFY(!ot5); + optional<Thingy2> ot6 = optional<Widget>(); + VERIFY(!ot6); + + static_assert(!std::is_assignable_v<optional<Thingy2>&, + optional<int>>); + static_assert(!std::is_assignable_v<optional<Thingy2>&, + optional<short>>); + static_assert(!std::is_assignable_v<optional<Thingy2>&, + optional<optional<int>>>); + static_assert(!std::is_assignable_v<optional<Thingy2>&, + optional<optional<short>>>); + optional<Thingy2> ot7; + ot = optional<Widget>(); + VERIFY(!ot7); + optional<Thingy2> ot8 = std::nullopt; + VERIFY(!ot8); + optional<Thingy2> ot9 = {}; + VERIFY(!ot9); + optional<Thingy2> ot10{std::nullopt}; + VERIFY(!ot10); + optional<Thingy2> ot11; + ot11 = {}; + VERIFY(!ot11); +} + +struct Thingy3 +{ + Thingy3(int) {} + template<class... Args, + std::enable_if_t<std::is_constructible_v<Widget, Args&&...>, + bool> = true> + explicit Thingy3(Args&&... args) {} + Thingy3(Widget) {} +}; + +void test08() +{ + optional<Thingy3> ot{optional<int>{}}; + VERIFY(ot); + static_assert(!std::is_convertible_v<optional<int>, + optional<Thingy3>>); + optional<Thingy3> ot2{optional<short>{}}; + VERIFY(ot2); + static_assert(!std::is_convertible_v<optional<short>, + optional<Thingy3>>); + optional<Thingy3> ot3{optional<optional<int>>{}}; + VERIFY(!ot3); + static_assert(!std::is_convertible_v<optional<optional<int>>, + optional<Thingy3>>); + optional<Thingy3> ot4{optional<optional<short>>{}}; + VERIFY(!ot4); + static_assert(!std::is_convertible_v<optional<optional<short>>, + optional<Thingy3>>); + + optional<Thingy3> ot5{optional<Widget>{}}; + VERIFY(!ot5); + optional<Thingy3> ot6 = optional<Widget>(); + VERIFY(!ot6); + + static_assert(!std::is_assignable_v<optional<Thingy3>&, + optional<int>>); + static_assert(!std::is_assignable_v<optional<Thingy3>&, + optional<short>>); + static_assert(!std::is_assignable_v<optional<Thingy3>&, + optional<optional<int>>>); + static_assert(!std::is_assignable_v<optional<Thingy3>&, + optional<optional<short>>>); + optional<Thingy3> ot7; + ot = optional<Widget>(); + VERIFY(!ot7); + optional<Thingy3> ot8 = std::nullopt; + VERIFY(!ot8); + optional<Thingy3> ot9 = {}; + VERIFY(!ot9); + optional<Thingy3> ot10{std::nullopt}; + VERIFY(!ot10); + optional<Thingy3> ot11; + ot11 = {}; + VERIFY(!ot11); +} + +void test09() +{ + std::any a = 42; + optional<std::any> oa2 = a; + VERIFY(oa2); + VERIFY(std::any_cast<int>(*oa2) == 42); + optional<std::any> oa3 = oa2; + VERIFY(oa3); + VERIFY(std::any_cast<int>(*oa3) == 42); + optional<std::any> oa4{oa2}; + VERIFY(oa4); + VERIFY(std::any_cast<int>(*oa4) == 42); + optional<std::any> oa5(oa2); + VERIFY(oa5); + VERIFY(std::any_cast<int>(*oa5) == 42); + optional<std::any> oa6; + VERIFY(!oa6); + optional<std::any> oa7 = oa6; + VERIFY(!oa7); + optional<std::any> oa8{oa6}; + VERIFY(!oa8); + optional<std::any> oa9(oa6); + VERIFY(!oa9); +} + +int main() +{ + test01(); + test02(); + test03(); + test04(); + test05(); + test06(); + test07(); + test08(); + test09(); +} diff --git a/libstdc++-v3/testsuite/20_util/optional/cons/value.cc b/libstdc++-v3/testsuite/20_util/optional/cons/value.cc index 6a2d871..141293c 100644 --- a/libstdc++-v3/testsuite/20_util/optional/cons/value.cc +++ b/libstdc++-v3/testsuite/20_util/optional/cons/value.cc @@ -256,17 +256,21 @@ int main() ox4 = oi; } { + std::optional<int> oi = std::optional<short>(); + VERIFY(!bool(oi)); + std::optional<std::string> os = std::optional<const char*>(); + VERIFY(!bool(os)); std::optional<std::optional<int>> ooi = std::optional<int>(); - VERIFY(!bool(ooi)); + VERIFY(bool(ooi)); ooi = std::optional<int>(); - VERIFY(!bool(ooi)); + VERIFY(bool(ooi)); ooi = std::optional<int>(42); VERIFY(bool(ooi)); VERIFY(bool(*ooi)); std::optional<std::optional<int>> ooi2 = std::optional<short>(); - VERIFY(!bool(ooi2)); + VERIFY(bool(ooi2)); ooi2 = std::optional<short>(); - VERIFY(!bool(ooi2)); + VERIFY(bool(ooi2)); ooi2 = std::optional<short>(6); VERIFY(bool(ooi2)); VERIFY(bool(*ooi2));