public inbox for libstdc++-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc r11-9317] libstdc++: Make std::pointer_traits SFINAE-friendly [PR96416]
@ 2021-11-26 17:45 Jonathan Wakely
  0 siblings, 0 replies; only message in thread
From: Jonathan Wakely @ 2021-11-26 17:45 UTC (permalink / raw)
  To: gcc-cvs, libstdc++-cvs

https://gcc.gnu.org/g:8d3391d64799d490117ad48432a9ad2cf38b0091

commit r11-9317-g8d3391d64799d490117ad48432a9ad2cf38b0091
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Thu Nov 25 23:29:08 2021 +0000

    libstdc++: Make std::pointer_traits SFINAE-friendly [PR96416]
    
    This is a simplified version of r12-5532 for the release branches.  It
    still removes the problematic static_assert, but rather than making
    std::pointer_traits completely empty when the element_type can't be
    deduced, it just disables element_type and pointer_to. Additionally, the
    pointer_to member is not completely absent when element_type is cv void,
    it just has an unusable signature.
    
    This is sufficient to avoid errors outside the immediate context when
    trying to use std::to_address.
    
    libstdc++-v3/ChangeLog:
    
            PR libstdc++/96416
            * include/bits/ptr_traits.h (pointer_traits): Remove
            static_assert checking for valid element_type.
            (pointer_traits::element_type, pointer_traits::pointer_to):
            Do not define when element type cannot be deduced.
            * testsuite/20_util/pointer_traits/lwg3545.cc: New test.
            * testsuite/20_util/to_address/1_neg.cc: Adjust dg-error line.
            * testsuite/20_util/to_address/lwg3545.cc: New test.
    
    (cherry picked from commit b8018e5c5ec0e9b6948182f13fba47c67b758d8a)

Diff:
---
 libstdc++-v3/include/bits/ptr_traits.h             | 60 +++++++-------
 .../testsuite/20_util/pointer_traits/lwg3545.cc    | 96 ++++++++++++++++++++++
 libstdc++-v3/testsuite/20_util/to_address/1_neg.cc |  2 +-
 .../testsuite/20_util/to_address/lwg3545.cc        | 37 +++++++++
 4 files changed, 166 insertions(+), 29 deletions(-)

diff --git a/libstdc++-v3/include/bits/ptr_traits.h b/libstdc++-v3/include/bits/ptr_traits.h
index 653a3580117..ba9dfbc8ab2 100644
--- a/libstdc++-v3/include/bits/ptr_traits.h
+++ b/libstdc++-v3/include/bits/ptr_traits.h
@@ -45,19 +45,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   class __undefined;
 
-  // Given Template<T, ...> return T, otherwise invalid.
-  template<typename _Tp>
-    struct __get_first_arg
-    { using type = __undefined; };
-
-  template<template<typename, typename...> class _Template, typename _Tp,
-           typename... _Types>
-    struct __get_first_arg<_Template<_Tp, _Types...>>
-    { using type = _Tp; };
-
-  template<typename _Tp>
-    using __get_first_arg_t = typename __get_first_arg<_Tp>::type;
-
   // Given Template<T, ...> and U return Template<U, ...>, otherwise invalid.
   template<typename _Tp, typename _Up>
     struct __replace_first_arg
@@ -75,17 +62,44 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     using __make_not_void
       = typename conditional<is_void<_Tp>::value, __undefined, _Tp>::type;
 
+  template<typename _Ptr>
+    struct __ptr_traits_elem_1
+    { };
+
+  template<template<typename, typename...> class _SomePointer, typename _Tp,
+	   typename... _Args>
+    struct __ptr_traits_elem_1<_SomePointer<_Tp, _Args...>>
+    {
+      using element_type = _Tp;
+      using pointer = _SomePointer<_Tp, _Args...>;
+
+      static pointer
+      pointer_to(__make_not_void<element_type>& __e)
+      { return pointer::pointer_to(__e); }
+    };
+
+  template<typename _Ptr, typename = void>
+    struct __ptr_traits_elem : __ptr_traits_elem_1<_Ptr>
+    { };
+
+  template<typename _Ptr>
+    struct __ptr_traits_elem<_Ptr, __void_t<typename _Ptr::element_type>>
+    {
+      using element_type = typename _Ptr::element_type;
+
+      static _Ptr
+      pointer_to(__make_not_void<element_type>& __e)
+      { return _Ptr::pointer_to(__e); }
+    };
+
   /**
    * @brief  Uniform interface to all pointer-like types
    * @ingroup pointer_abstractions
   */
   template<typename _Ptr>
-    struct pointer_traits
+    struct pointer_traits : __ptr_traits_elem<_Ptr>
     {
     private:
-      template<typename _Tp>
-	using __element_type = typename _Tp::element_type;
-
       template<typename _Tp>
 	using __difference_type = typename _Tp::difference_type;
 
@@ -100,10 +114,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       /// The pointer type.
       using pointer = _Ptr;
 
-      /// The type pointed to.
-      using element_type
-	= __detected_or_t<__get_first_arg_t<_Ptr>, __element_type, _Ptr>;
-
       /// The type used to represent the difference between two pointers.
       using difference_type
 	= __detected_or_t<ptrdiff_t, __difference_type, _Ptr>;
@@ -111,13 +121,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       /// A pointer to a different type.
       template<typename _Up>
         using rebind = typename __rebind<_Ptr, _Up>::type;
-
-      static _Ptr
-      pointer_to(__make_not_void<element_type>& __e)
-      { return _Ptr::pointer_to(__e); }
-
-      static_assert(!is_same<element_type, __undefined>::value,
-	  "pointer type defines element_type or is like SomePointer<T, Args>");
     };
 
   /**
@@ -165,6 +168,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     __to_address(const _Ptr& __ptr)
     { return std::__to_address(__ptr.operator->()); }
 #else
+
   template<typename _Ptr>
     constexpr auto
     __to_address(const _Ptr& __ptr) noexcept
diff --git a/libstdc++-v3/testsuite/20_util/pointer_traits/lwg3545.cc b/libstdc++-v3/testsuite/20_util/pointer_traits/lwg3545.cc
new file mode 100644
index 00000000000..f34c7e28eaf
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/pointer_traits/lwg3545.cc
@@ -0,0 +1,96 @@
+// { dg-do compile { target c++11 } }
+
+// LWG 3545. std::pointer_traits should be SFINAE-friendly
+
+#include <memory>
+
+using std::is_same;
+
+template<typename> using void_t = void;
+
+template<template<typename> class Probe, typename T, typename = void>
+  struct has_member
+  : std::false_type { };
+
+template<template<typename> class Probe, typename T>
+  struct has_member<Probe, T, void_t<Probe<T>>>
+  : std::true_type { };
+
+template<typename T>
+  using element_type = typename T::element_type;
+template<typename T>
+  using pointer = typename T::pointer;
+template<typename T>
+  using difference_type = typename T::difference_type;
+template<typename T>
+  using rebind = typename T::template rebind<short>;
+template<typename T>
+  using pointer_to = decltype(T::pointer_to(std::declval<element_type<T>&>()));
+
+using invalid = std::pointer_traits<int>;
+invalid i; // invalid instantiation is not ill-formed
+
+static_assert( !has_member<element_type, invalid>::value, "" );
+// These members are defined unconditionally in this version of GCC.
+// static_assert( !has_member<pointer, invalid>::value, "" );
+// static_assert( !has_member<difference_type, invalid>::value, "" );
+static_assert( !has_member<rebind, invalid>::value, "" );
+static_assert( !has_member<pointer_to, invalid>::value, "" );
+
+struct I
+{
+  // These members should not be used by pointer_traits<P>::pointer.
+  using pointer = int;
+  using difference_type = int;
+  template<typename> using rebind = int;
+};
+
+using invalid2 = std::pointer_traits<I>;
+
+static_assert( !has_member<element_type, invalid2>::value, "" );
+// These members are defined unconditionally in this version of GCC.
+// static_assert( !has_member<pointer, invalid2>::value, "" );
+// static_assert( !has_member<difference_type, invalid2>::value, "" );
+// static_assert( !has_member<rebind, invalid2>::value, "" );
+static_assert( !has_member<pointer_to, invalid2>::value, "" );
+
+struct P
+{
+  using element_type = long;
+  struct pointer { }; // should not be used by pointer_traits<P>::pointer
+};
+using Ptraits = std::pointer_traits<P>;
+Ptraits p;
+
+static_assert( is_same<element_type<Ptraits>, long>::value, "" );
+static_assert( is_same<pointer<Ptraits>, P>::value, "" );
+static_assert( is_same<difference_type<Ptraits>, std::ptrdiff_t>::value, "" );
+static_assert( !has_member<rebind, Ptraits>::value, "" );
+static_assert( is_same<pointer_to<Ptraits>, P>::value, "" );
+
+struct V { using element_type = const void; };
+using Vtraits = std::pointer_traits<V>;
+Vtraits v;
+
+static_assert( is_same<element_type<Vtraits>, const void>::value, "" );
+static_assert( is_same<pointer<Vtraits>, V>::value, "" );
+static_assert( is_same<difference_type<Vtraits>, std::ptrdiff_t>::value, "" );
+static_assert( !has_member<rebind, Vtraits>::value, "" );
+static_assert( !has_member<pointer_to, Vtraits>::value, "" );
+
+template<typename T>
+struct clever_ptr
+{
+  static T obj;
+
+  static clever_ptr pointer_to(T&) { return {}; }
+  constexpr T* operator->() const { return &obj; }
+};
+
+using Ctraits = std::pointer_traits<clever_ptr<char>>;
+
+static_assert( is_same<element_type<Ctraits>, char>::value, "" );
+static_assert( is_same<pointer<Ctraits>, clever_ptr<char>>::value, "" );
+static_assert( is_same<difference_type<Ctraits>, std::ptrdiff_t>::value, "" );
+static_assert( is_same<rebind<Ctraits>, clever_ptr<short>>::value, "" );
+static_assert( is_same<pointer_to<Ctraits>, clever_ptr<char>>::value, "" );
diff --git a/libstdc++-v3/testsuite/20_util/to_address/1_neg.cc b/libstdc++-v3/testsuite/20_util/to_address/1_neg.cc
index ff3a1fbcb5d..aedff208763 100644
--- a/libstdc++-v3/testsuite/20_util/to_address/1_neg.cc
+++ b/libstdc++-v3/testsuite/20_util/to_address/1_neg.cc
@@ -17,7 +17,7 @@
 
 // { dg-options "-std=gnu++2a" }
 // { dg-do compile { target c++2a } }
-// { dg-error "not a function pointer" "" { target *-*-* } 158 }
+// { dg-error "not a function pointer" "" { target *-*-* } 0 }
 
 #include <memory>
 
diff --git a/libstdc++-v3/testsuite/20_util/to_address/lwg3545.cc b/libstdc++-v3/testsuite/20_util/to_address/lwg3545.cc
new file mode 100644
index 00000000000..a80ac29b2c9
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/to_address/lwg3545.cc
@@ -0,0 +1,37 @@
+// { dg-options "-std=gnu++20" }
+// { dg-do compile { target c++20 } }
+
+#include <memory>
+
+template<typename T, bool> struct nttp_ptr
+{
+  T* operator->() const { return nullptr; }
+};
+
+// This gives an error in C++20, which the LWG 3545 resolution should fix:
+auto x = std::to_address( nttp_ptr<int, true>() );
+
+template<typename T>
+struct clever_ptr
+{
+  static T obj;
+  constexpr T* operator->() const { return &obj; }
+};
+
+// pointer_traits specialization is valid, but to_address uses operator->
+static_assert( std::to_address(clever_ptr<char>{}) == &clever_ptr<char>::obj );
+
+int the_int;
+
+template<>
+struct std::pointer_traits<clever_ptr<int>>
+{
+  using element_type = int;
+  using difference_type = std::ptrdiff_t;
+  using pointer = clever_ptr<int>;
+
+  static constexpr int* to_address(pointer p) { return &the_int; }
+};
+
+// Uses pointer_traits<clever_ptr<int>>::to_address
+static_assert( std::to_address(clever_ptr<int>{}) == &the_int );


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2021-11-26 17:45 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-11-26 17:45 [gcc r11-9317] libstdc++: Make std::pointer_traits SFINAE-friendly [PR96416] Jonathan Wakely

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).