From patchwork Wed Mar 29 23:40:00 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jonathan Wakely X-Patchwork-Id: 67107 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id CD03F384F032 for ; Wed, 29 Mar 2023 23:41:19 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org CD03F384F032 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1680133279; bh=UzjtgeGxHmWcWIzyPm9Vc1o2n843+ruUBSi7xpEu7dI=; h=To:Subject:Date:List-Id:List-Unsubscribe:List-Archive:List-Post: List-Help:List-Subscribe:From:Reply-To:From; b=N2Mwy9XulAy5fTjXvveq+dJ0BQldxzJqyaq0329T45zjEN4USKeB7ZbW1Ids4FYL/ NYV+XU4n9EFkcVgVzlPd/4AgSyYp2UXGz0Bb1KF6uxUT130lcld0FGqv6mhtTtAnCL FPgRl/kQzD21gi0kRCk7c9BEYgcE31o4VxAVkiOk= X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTPS id A740C388201E for ; Wed, 29 Mar 2023 23:40:04 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org A740C388201E Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-556-ypa92v6qNZy0fUgPvTHwxQ-1; Wed, 29 Mar 2023 19:40:01 -0400 X-MC-Unique: ypa92v6qNZy0fUgPvTHwxQ-1 Received: from smtp.corp.redhat.com (int-mx07.intmail.prod.int.rdu2.redhat.com [10.11.54.7]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id E8566889047; Wed, 29 Mar 2023 23:40:00 +0000 (UTC) Received: from localhost (unknown [10.33.36.149]) by smtp.corp.redhat.com (Postfix) with ESMTP id 8F2BD14171BB; Wed, 29 Mar 2023 23:40:00 +0000 (UTC) To: libstdc++@gcc.gnu.org, gcc-patches@gcc.gnu.org Subject: [committed] libstdc++: Fix constexpr functions in Date: Thu, 30 Mar 2023 00:40:00 +0100 Message-Id: <20230329234000.1405216-1-jwakely@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.1 on 10.11.54.7 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-11.8 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H2, SPF_HELO_NONE, SPF_NONE, TXREP autolearn=unavailable autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Jonathan Wakely via Gcc-patches From: Jonathan Wakely Reply-To: Jonathan Wakely Errors-To: gcc-patches-bounces+patchwork=sourceware.org@gcc.gnu.org Sender: "Gcc-patches" Tested powerpc64le-linux. Pushed to trunk. -- >8 -- 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. --- libstdc++-v3/include/experimental/internet | 103 +++++++++++++----- .../net/internet/address/v4/cons.cc | 16 ++- .../net/internet/address/v4/creation.cc | 14 ++- .../net/internet/endpoint/cons.cc | 29 +++-- .../net/internet/endpoint/extensible.cc | 47 ++++++++ .../net/internet/network/v4/cons.cc | 17 ++- .../net/internet/network/v4/members.cc | 20 ++-- 7 files changed, 185 insertions(+), 61 deletions(-) create mode 100644 libstdc++-v3/testsuite/experimental/net/internet/endpoint/extensible.cc 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 +#include +#include + +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" ); }