public inbox for libstdc++@gcc.gnu.org
 help / color / mirror / Atom feed
From: Jonathan Wakely <jwakely@redhat.com>
To: libstdc++@gcc.gnu.org, gcc-patches@gcc.gnu.org
Subject: [committed] libstdc++: Do not use SFINAE for propagate_const conversions [PR107525]
Date: Sat,  5 Nov 2022 14:01:55 +0000	[thread overview]
Message-ID: <20221105140155.1206577-1-jwakely@redhat.com> (raw)

Tested x86_64-linux. Pushed to trunk.

-- >8 --

As the PR notes, the current conversion operators are defined as
function templates so that we can use SFINAE. But this changes how they
are considered for overload resolution. This moves those operators into
base classes that can be specialized so the operators are obsent unless
the constraints are satisfied.

libstdc++-v3/ChangeLog:

	PR libstdc++/107525
	* include/experimental/propagate_const (operator element_type*()):
	Move into base class that can be partially specilized to iompose
	constraints.
	(operator const element_type*()): Likewise.
	* testsuite/experimental/propagate_const/observers/107525.cc: New test.
---
 .../include/experimental/propagate_const      | 88 ++++++++++++++-----
 .../propagate_const/observers/107525.cc       | 47 ++++++++++
 2 files changed, 113 insertions(+), 22 deletions(-)
 create mode 100644 libstdc++-v3/testsuite/experimental/propagate_const/observers/107525.cc

diff --git a/libstdc++-v3/include/experimental/propagate_const b/libstdc++-v3/include/experimental/propagate_const
index 258ef6fdd44..12b0f27462a 100644
--- a/libstdc++-v3/include/experimental/propagate_const
+++ b/libstdc++-v3/include/experimental/propagate_const
@@ -50,6 +50,48 @@ namespace experimental
 {
 inline namespace fundamentals_v2
 {
+  template<typename _Tp>
+    using __propagate_const_elem_type
+      = remove_reference_t<decltype(*std::declval<_Tp&>())>;
+
+  template<typename _Tp,
+	   typename _Elem = __propagate_const_elem_type<_Tp>,
+	   bool = is_convertible<const _Tp, const _Elem*>::value>
+    struct __propagate_const_conversion_c
+    { };
+
+  template<typename _Tp, typename _Elem>
+    struct __propagate_const_conversion_c<_Tp, _Elem, true>
+    {
+      constexpr operator const _Elem*() const;
+    };
+
+  template<typename _Tp,
+	   typename _Elem = __propagate_const_elem_type<_Tp>,
+	   bool = is_convertible<_Tp, _Elem*>::value>
+    struct __propagate_const_conversion_nc
+    { };
+
+  template<typename _Tp, typename _Elem>
+    struct __propagate_const_conversion_nc<_Tp, _Elem, true>
+    {
+      constexpr operator _Elem*();
+    };
+
+  // Base class of propagate_const<T> when T is a class type.
+  template <typename _Tp>
+    struct __propagate_const_conversions
+    : __propagate_const_conversion_c<_Tp>, __propagate_const_conversion_nc<_Tp>
+    { };
+
+  // Base class of propagate_const<T> when T is a pointer type.
+  template<typename _Tp>
+    struct __propagate_const_conversions<_Tp*>
+    {
+      constexpr operator const _Tp*() const noexcept;
+      constexpr operator _Tp*() noexcept;
+    };
+
   /**
    * @defgroup propagate_const Const-propagating wrapper
    * @ingroup libfund-ts
@@ -63,10 +105,10 @@ inline namespace fundamentals_v2
 
   /// Const-propagating wrapper.
   template <typename _Tp>
-    class propagate_const
+    class propagate_const : public __propagate_const_conversions<_Tp>
     {
     public:
-      typedef remove_reference_t<decltype(*std::declval<_Tp&>())> element_type;
+      using element_type = __propagate_const_elem_type<_Tp>;
 
     private:
       template <typename _Up>
@@ -186,16 +228,6 @@ inline namespace fundamentals_v2
 	return get();
       }
 
-      template <typename _Up = _Tp,
-		typename enable_if<__or_<is_pointer<_Up>,
-					 is_convertible<_Up,
-							const element_type*>
-					 >::value, bool>::type = true>
-      constexpr operator const element_type*() const
-      {
-	return get();
-      }
-
       constexpr const element_type& operator*() const
       {
 	return *get();
@@ -212,16 +244,6 @@ inline namespace fundamentals_v2
 	return get();
       }
 
-      template <typename _Up = _Tp,
-		typename enable_if<__or_<is_pointer<_Up>,
-					 is_convertible<_Up,
-						        const element_type*>
-					 >::value, bool>::type = true>
-      constexpr operator element_type*()
-      {
-	return get();
-      }
-
       constexpr element_type& operator*()
       {
 	return *get();
@@ -430,6 +452,28 @@ inline namespace fundamentals_v2
       return __pt._M_t;
     }
 
+  template<typename _Tp>
+    constexpr
+    __propagate_const_conversions<_Tp*>::operator const _Tp*() const noexcept
+    { return static_cast<const propagate_const<_Tp*>*>(this)->get(); }
+
+  template<typename _Tp>
+    constexpr
+    __propagate_const_conversions<_Tp*>::operator _Tp*() noexcept
+    { return static_cast<propagate_const<_Tp*>*>(this)->get(); }
+
+  template<typename _Tp, typename _Elem>
+    constexpr
+    __propagate_const_conversion_c<_Tp, _Elem, true>::
+    operator const _Elem*() const
+    { return static_cast<const propagate_const<_Tp>*>(this)->get(); }
+
+  template<typename _Tp, typename _Elem>
+    constexpr
+    __propagate_const_conversion_nc<_Tp, _Elem, true>::
+    operator _Elem*()
+    { return static_cast<propagate_const<_Tp>*>(this)->get(); }
+
   /// @} group propagate_const
 } // namespace fundamentals_v2
 } // namespace experimental
diff --git a/libstdc++-v3/testsuite/experimental/propagate_const/observers/107525.cc b/libstdc++-v3/testsuite/experimental/propagate_const/observers/107525.cc
new file mode 100644
index 00000000000..e7ecff73c1a
--- /dev/null
+++ b/libstdc++-v3/testsuite/experimental/propagate_const/observers/107525.cc
@@ -0,0 +1,47 @@
+// { dg-do run { target c++14 } }
+
+#include <experimental/propagate_const>
+#include <testsuite_hooks.h>
+
+using std::experimental::propagate_const;
+
+void
+test_base_conversion()
+{
+  struct Base { };
+  struct Derived : Base { };
+
+  static_assert(std::is_convertible<propagate_const<Derived*>, Base*>::value,
+      "PR libstdc++/107525 - SFINAE breaks conversion operators");
+  static_assert(std::is_convertible<const propagate_const<Derived*>, const Base*>::value,
+      "PR libstdc++/107525 - SFINAE breaks conversion operators");
+}
+
+void
+test_const_conversion()
+{
+  struct X
+  {
+    int* p = nullptr;
+
+    int& operator*() const { return *p; }
+    int* operator->() const { return p; }
+    int* get() const { return p; }
+
+    operator int*() { return p; }
+    operator const int*() const = delete;
+  };
+
+  static_assert(!std::is_convertible_v<const X, const int*>,
+		"Cannot convert const X to const int*");
+  // So should not be able to convert const propagate_const<X> to const int*.
+  static_assert(!std::is_convertible_v<const propagate_const<X>, const int*>,
+		"So should not be able to convert const propagate_const<X> to "
+		"const int* (although this is not what LFTSv3 says)");
+}
+
+int main()
+{
+  test_base_conversion();
+  test_const_conversion();
+}
-- 
2.38.1


                 reply	other threads:[~2022-11-05 14:02 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20221105140155.1206577-1-jwakely@redhat.com \
    --to=jwakely@redhat.com \
    --cc=gcc-patches@gcc.gnu.org \
    --cc=libstdc++@gcc.gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).