[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