[gcc r13-6936] libstdc++: Fix constexpr functions in <experimental/internet>

Jonathan Wakely redi@gcc.gnu.org
Wed Mar 29 23:38:46 GMT 2023


https://gcc.gnu.org/g:e0d77144aaa56ee6e00fd0ba520b0a5c7e052423

commit r13-6936-ge0d77144aaa56ee6e00fd0ba520b0a5c7e052423
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Wed Mar 22 17:00:11 2023 +0000

    libstdc++: Fix constexpr functions in <experimental/internet>
    
    Change ip::basic_endpoint to work in constant expressions, but only for
    C++20 and later (due to the use of a union, which cannot change active
    member in constexpr evaluation until C++20).
    
    During constant evaluation we cannot inspect the common initial sequence
    of basic_endpoint's union members to check whether sin_family == AF_INET
    or AF_INET6.  This means we need to store an additional boolean member
    that remembers whether we have a v4 or v6 address. The address type can
    change behind our backs if a user copies an address to the data()
    pointer and then calls resize(n), so we need to inspect the sa_family_t
    member in the union after a resize and update the boolean. POSIX only
    guarantees that the sa_family_t member of each protocol-specific address
    structure is at the same offset and of the same type, not that there is
    a common initial sequence. The check in resize is done using memcmp, so
    that we avoid accessing an inactive member of the union if the
    sockaddr_in and sockaddr_in6 structures do not have a common initial
    sequence that includes the sa_family_t member.
    
    libstdc++-v3/ChangeLog:
    
            * include/experimental/internet (ip::make_address): Implement
            missing overload.
            (ip::address_v4::broadcast()): Avoid undefined shift.
            (ip::basic_endpoint): Fix member functions for constexpr.
            (ip::basic_endpoint::_M_is_v6): Replace member function with
            data member, adjust member functions using it.
            (ip::basic_endpoint::resize): Update _M_is_v6 based on sockaddr
            content.
            * testsuite/experimental/net/internet/address/v4/cons.cc: Fix
            constexpr checks to work in C++14.
            * testsuite/experimental/net/internet/address/v4/creation.cc:
            Likewise.
            * testsuite/experimental/net/internet/endpoint/cons.cc:
            Likewise.
            * testsuite/experimental/net/internet/network/v4/cons.cc:
            Likewise.
            * testsuite/experimental/net/internet/network/v4/members.cc:
            Likewise.
            * testsuite/experimental/net/internet/endpoint/extensible.cc: New test.

Diff:
---
 libstdc++-v3/include/experimental/internet         | 103 +++++++++++++++------
 .../experimental/net/internet/address/v4/cons.cc   |  16 ++--
 .../net/internet/address/v4/creation.cc            |  14 ++-
 .../experimental/net/internet/endpoint/cons.cc     |  29 ++++--
 .../net/internet/endpoint/extensible.cc            |  47 ++++++++++
 .../experimental/net/internet/network/v4/cons.cc   |  17 ++--
 .../net/internet/network/v4/members.cc             |  20 ++--
 7 files changed, 185 insertions(+), 61 deletions(-)

diff --git a/libstdc++-v3/include/experimental/internet b/libstdc++-v3/include/experimental/internet
index cae07f466da..dff81b456ab 100644
--- a/libstdc++-v3/include/experimental/internet
+++ b/libstdc++-v3/include/experimental/internet
@@ -977,7 +977,8 @@ namespace ip
   { return make_address(__str, __throw_on_error{"make_address"}); }
 
   inline address
-  make_address(const string& __str, error_code& __ec) noexcept; // TODO
+  make_address(const string& __str, error_code& __ec) noexcept
+  { return make_address(__str.c_str(), __ec); }
 
   inline address
   make_address(const string& __str)
@@ -1275,7 +1276,12 @@ namespace ip
 
     constexpr address_v4
     broadcast() const noexcept
-    { return address_v4{_M_addr.to_uint() | (0xFFFFFFFFu >> _M_prefix_len)}; }
+    {
+      auto __b = _M_addr.to_uint();
+      if (_M_prefix_len < 32)
+	__b |= 0xFFFFFFFFu >> _M_prefix_len;
+      return address_v4{__b};
+    }
 
     address_v4_range
     hosts() const noexcept
@@ -1510,19 +1516,31 @@ namespace ip
       basic_endpoint() noexcept : _M_data()
       { _M_data._M_v4.sin_family = protocol_type::v4().family(); }
 
-      constexpr
+      _GLIBCXX20_CONSTEXPR
       basic_endpoint(const protocol_type& __proto,
 		     port_type __port_num) noexcept
       : _M_data()
       {
-	__glibcxx_assert(__proto == protocol_type::v4()
-			  || __proto == protocol_type::v6());
-
-	_M_data._M_v4.sin_family = __proto.family();
-	_M_data._M_v4.sin_port = address_v4::_S_hton_16(__port_num);
+	if (__proto == protocol_type::v4())
+	  {
+	    _M_data._M_v4.sin_family = __proto.family();
+	    _M_data._M_v4.sin_port = address_v4::_S_hton_16(__port_num);
+	  }
+	else if (__proto == protocol_type::v6())
+	  {
+	    std::_Construct(&_M_data._M_v6);
+	    _M_data._M_v6.sin6_family = __proto.family();
+	    _M_data._M_v6.sin6_port = address_v4::_S_hton_16(__port_num);
+	    _M_is_v6 = true;
+	  }
+	else
+	  {
+	    __glibcxx_assert(__proto == protocol_type::v4()
+			       || __proto == protocol_type::v6());
+	  }
       }
 
-      constexpr
+      _GLIBCXX20_CONSTEXPR
       basic_endpoint(const ip::address& __addr,
 		     port_type __port_num) noexcept
       : _M_data()
@@ -1535,37 +1553,42 @@ namespace ip
 	  }
 	else
 	  {
-	    _M_data._M_v6 = {};
+	    std::_Construct(&_M_data._M_v6);
 	    _M_data._M_v6.sin6_family = protocol_type::v6().family();
 	    _M_data._M_v6.sin6_port = address_v4::_S_hton_16(__port_num);
-	    __builtin_memcpy(_M_data._M_v6.sin6_addr.s6_addr,
-			     __addr._M_v6._M_bytes.data(), 16);
+	    uint8_t* __s6a = _M_data._M_v6.sin6_addr.s6_addr;
+	    for (int __i = 0; __i < 16; ++__i)
+	      __s6a[__i] = __addr._M_v6._M_bytes[__i];
 	    _M_data._M_v6.sin6_scope_id = __addr._M_v6._M_scope_id;
+	    _M_is_v6 = true;
 	  }
       }
 
       // members:
+
       constexpr protocol_type protocol() const noexcept
       {
-	return _M_is_v6() ? protocol_type::v6() : protocol_type::v4();
+	return _M_is_v6 ? protocol_type::v6() : protocol_type::v4();
       }
 
       constexpr ip::address
       address() const noexcept
       {
-	ip::address __addr;
-	if (_M_is_v6())
+	if (_M_is_v6)
 	  {
-	    __builtin_memcpy(&__addr._M_v6._M_bytes,
-			     _M_data._M_v6.sin6_addr.s6_addr, 16);
-	    __addr._M_is_v4 = false;
+	    address_v6 __v6;
+	    const uint8_t* __s6a = _M_data._M_v6.sin6_addr.s6_addr;
+	    for (int __i = 0; __i < 16; ++__i)
+	      __v6._M_bytes[__i] = __s6a[__i];
+	    __v6._M_scope_id = _M_data._M_v6.sin6_scope_id;
+	    return __v6;
 	  }
 	else
 	  {
-	    __builtin_memcpy(&__addr._M_v4._M_addr,
-			     &_M_data._M_v4.sin_addr.s_addr, 4);
+	    address_v4 __v4;
+	    __v4._M_addr = _M_data._M_v4.sin_addr.s_addr;
+	    return __v4;
 	  }
-	return __addr;
       }
 
       void
@@ -1573,37 +1596,62 @@ namespace ip
       {
 	if (__addr.is_v6())
 	  {
-	    _M_data._M_v6 = {};
+	    std::_Construct(&_M_data._M_v6);
 	    _M_data._M_v6.sin6_family = protocol_type::v6().family();
 	    __builtin_memcpy(_M_data._M_v6.sin6_addr.s6_addr,
 			     __addr._M_v6._M_bytes.data(), 16);
 	    _M_data._M_v6.sin6_scope_id = __addr._M_v6._M_scope_id;
+	    _M_is_v6 = true;
 	  }
 	else
 	  {
+	    std::_Construct(&_M_data._M_v4);
 	    _M_data._M_v4.sin_family = protocol_type::v4().family();
 	    _M_data._M_v4.sin_addr.s_addr = __addr._M_v4._M_addr;
+	    _M_is_v6 = false;
 	  }
       }
 
       constexpr port_type
       port() const noexcept
-      { return address_v4::_S_ntoh_16(_M_data._M_v4.sin_port); }
+      {
+	port_type __p = 0;
+	if (_M_is_v6)
+	  __p = _M_data._M_v6.sin6_port;
+	else
+	  __p = _M_data._M_v4.sin_port;
+	return address_v4::_S_ntoh_16(__p);
+      }
 
       void
       port(port_type __port_num) noexcept
-      { _M_data._M_v4.sin_port = address_v4::_S_hton_16(__port_num); }
+      {
+	__port_num = address_v4::_S_hton_16(__port_num);
+	if (_M_is_v6)
+	  _M_data._M_v6.sin6_port = __port_num;
+	else
+	  _M_data._M_v4.sin_port = __port_num;
+      }
 
       void* data() noexcept { return &_M_data; }
 
       const void* data() const noexcept { return &_M_data; }
 
-      constexpr size_t size() const noexcept
-      { return _M_is_v6() ? sizeof(sockaddr_in6) : sizeof(sockaddr_in); }
+      constexpr size_t
+      size() const noexcept
+      { return _M_is_v6 ? sizeof(sockaddr_in6) : sizeof(sockaddr_in); }
 
       void
       resize(size_t __s)
       {
+	__glibcxx_assert(__s >= 0);
+	static_assert(offsetof(sockaddr_in6, sin6_family)
+			== offsetof(sockaddr_in, sin_family),
+		      "sockaddr_in::sin_family and sockaddr_in6::sin6_family "
+		      "must be at the same offset");
+	const sa_family_t __in6 = AF_INET6;
+	const auto* __ptr = (char*)&_M_data + offsetof(sockaddr_in, sin_family);
+	_M_is_v6 = __builtin_memcmp(&__in6, __ptr, sizeof(__in6)) == 0;
 	if (__s != size())
 	  __throw_length_error("net::ip::basic_endpoint::resize");
       }
@@ -1617,8 +1665,7 @@ namespace ip
 	sockaddr_in6	_M_v6;
       } _M_data;
 
-      constexpr bool _M_is_v6() const noexcept
-      { return _M_data._M_v4.sin_family == AF_INET6; }
+      bool _M_is_v6 = false;
     };
 
   /** basic_endpoint comparisons
diff --git a/libstdc++-v3/testsuite/experimental/net/internet/address/v4/cons.cc b/libstdc++-v3/testsuite/experimental/net/internet/address/v4/cons.cc
index af9fef2215e..49bfb63baf6 100644
--- a/libstdc++-v3/testsuite/experimental/net/internet/address/v4/cons.cc
+++ b/libstdc++-v3/testsuite/experimental/net/internet/address/v4/cons.cc
@@ -68,6 +68,15 @@ test03()
   VERIFY( a1.to_bytes() == address_v4::bytes_type( 5, 6, 7, 8 ) );
 }
 
+constexpr bool
+test_constexpr()
+{
+  test01();
+  test02();
+  test03();
+  return true;
+}
+
 int
 main()
 {
@@ -75,10 +84,5 @@ main()
   test02();
   test03();
 
-  constexpr bool c = []{
-    test01();
-    test02();
-    test03();
-    return true;
-  };
+  static_assert( test_constexpr(), "valid in constant expressions" );
 }
diff --git a/libstdc++-v3/testsuite/experimental/net/internet/address/v4/creation.cc b/libstdc++-v3/testsuite/experimental/net/internet/address/v4/creation.cc
index 84aebbb7adc..8469b1267e1 100644
--- a/libstdc++-v3/testsuite/experimental/net/internet/address/v4/creation.cc
+++ b/libstdc++-v3/testsuite/experimental/net/internet/address/v4/creation.cc
@@ -88,6 +88,14 @@ test03()
   VERIFY( ec == std::errc::invalid_argument );
 }
 
+constexpr bool
+test_constexpr()
+{
+  test01();
+  test02();
+  return true;
+}
+
 int
 main()
 {
@@ -95,9 +103,5 @@ main()
   test02();
   test03();
 
-  constexpr bool c = []{
-    test01();
-    test02();
-    return true;
-  };
+  static_assert( test_constexpr(), "valid in constant expressions" );
 }
diff --git a/libstdc++-v3/testsuite/experimental/net/internet/endpoint/cons.cc b/libstdc++-v3/testsuite/experimental/net/internet/endpoint/cons.cc
index 1b5c92c0b58..b4bef88b4a3 100644
--- a/libstdc++-v3/testsuite/experimental/net/internet/endpoint/cons.cc
+++ b/libstdc++-v3/testsuite/experimental/net/internet/endpoint/cons.cc
@@ -21,7 +21,10 @@ test_default()
   VERIFY( t2.port() == 0 );
 }
 
-constexpr void
+#if __cplusplus >= 202002
+constexpr
+#endif
+void
 test_proto()
 {
   ip::tcp::endpoint t1(ip::tcp::v4(), 22);
@@ -35,7 +38,10 @@ test_proto()
   VERIFY( t2.port() == 80 );
 }
 
-constexpr void
+#if __cplusplus >= 202002
+constexpr
+#endif
+void
 test_addr()
 {
   ip::address_v4 a1(ip::address_v4::bytes_type(1, 2, 3, 4));
@@ -51,16 +57,23 @@ test_addr()
   VERIFY( t2.port() == 80 );
 }
 
+constexpr bool
+test_constexpr()
+{
+  test_default();
+#if __cplusplus >= 202002
+  // Non-default basic_endpoint constructors are only constexpr in C++20.
+  test_proto();
+  test_addr();
+#endif
+  return true;
+}
+
 int main()
 {
   test_default();
   test_proto();
   test_addr();
 
-  constexpr bool c = [] {
-    test_default();
-    test_proto();
-    test_addr();
-    return true;
-  };
+  static_assert( test_constexpr(), "valid in constant expressions" );
 }
diff --git a/libstdc++-v3/testsuite/experimental/net/internet/endpoint/extensible.cc b/libstdc++-v3/testsuite/experimental/net/internet/endpoint/extensible.cc
new file mode 100644
index 00000000000..d205024c799
--- /dev/null
+++ b/libstdc++-v3/testsuite/experimental/net/internet/endpoint/extensible.cc
@@ -0,0 +1,47 @@
+// { dg-do run { target c++14 } }
+// { dg-require-effective-target net_ts_ip }
+// { dg-add-options net_ts }
+
+#include <experimental/internet>
+#include <cstring>
+#include <testsuite_hooks.h>
+
+using namespace std::experimental::net;
+
+void
+test_extensible()
+{
+  static_assert(ip::tcp::endpoint().capacity() == sizeof(sockaddr_in6),
+		"ip::tcp::endpoint::capacity() can store a sockaddr_in6");
+
+  ip::tcp::endpoint t1(ip::tcp::v4(), 22);
+  VERIFY(t1.size() == sizeof(sockaddr_in));
+  t1.resize(sizeof(sockaddr_in));
+  try {
+    t1.resize(2 * sizeof(sockaddr_in));
+    VERIFY(false);
+  } catch (const std::length_error&) {
+  }
+
+  ip::tcp::endpoint t2(ip::tcp::v6(), 80);
+  VERIFY(t2.size() == sizeof(sockaddr_in6));
+  t2.resize(sizeof(sockaddr_in6));
+  try {
+    t2.resize(2 * sizeof(sockaddr_in6));
+    VERIFY(false);
+  } catch (const std::length_error&) {
+  }
+
+  ip::tcp::endpoint t3;
+  std::memcpy(t3.data(), t1.data(), t1.size());
+  t3.resize(t1.size());
+  VERIFY( t3 == t1 );
+  std::memcpy(t3.data(), t2.data(), t2.size());
+  t3.resize(t2.size());
+  VERIFY( t3 == t2 );
+}
+
+int main()
+{
+  test_extensible();
+}
diff --git a/libstdc++-v3/testsuite/experimental/net/internet/network/v4/cons.cc b/libstdc++-v3/testsuite/experimental/net/internet/network/v4/cons.cc
index 7784b6f6f58..c49f8435584 100644
--- a/libstdc++-v3/testsuite/experimental/net/internet/network/v4/cons.cc
+++ b/libstdc++-v3/testsuite/experimental/net/internet/network/v4/cons.cc
@@ -111,6 +111,16 @@ test03_errors()
   }
 }
 
+constexpr bool
+test_constexpr()
+{
+  test01();
+  test02();
+  test03();
+  return true;
+}
+
+
 int
 main()
 {
@@ -120,10 +130,5 @@ main()
   test03();
   test03_errors();
 
-  constexpr bool c = []{
-    test01();
-    test02();
-    test03();
-    return true;
-  };
+  static_assert( test_constexpr(), "valid in constant expressions" );
 }
diff --git a/libstdc++-v3/testsuite/experimental/net/internet/network/v4/members.cc b/libstdc++-v3/testsuite/experimental/net/internet/network/v4/members.cc
index 3ea65862649..7587eb1202b 100644
--- a/libstdc++-v3/testsuite/experimental/net/internet/network/v4/members.cc
+++ b/libstdc++-v3/testsuite/experimental/net/internet/network/v4/members.cc
@@ -166,6 +166,17 @@ test_to_string()
   VERIFY( str2 == "87.65.43.21/4" );
 }
 
+constexpr bool
+test_constexpr()
+{
+  test_netmask();
+  test_network();
+  test_broadcast();
+  test_canonical();
+  test_is_host();
+  return true;
+}
+
 int main()
 {
   test_netmask();
@@ -175,12 +186,5 @@ int main()
   test_is_host();
   test_to_string();
 
-  constexpr bool c = []{
-    test_netmask();
-    test_network();
-    test_broadcast();
-    test_canonical();
-    test_is_host();
-    return true;
-  };
+  static_assert( test_constexpr(), "valid in constant expressions" );
 }


More information about the Libstdc++-cvs mailing list