[v3 PATCH] PR libstdc++/77288 and the newest proposed resolution for LWG 2756

Ville Voutilainen ville.voutilainen@gmail.com
Tue Aug 23 14:17:00 GMT 2016


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.voutilainen@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.
-------------- next part --------------
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));


More information about the Gcc-patches mailing list