From patchwork Thu Sep 7 07:11:47 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jonathan Wakely X-Patchwork-Id: 1830725 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=gcc.gnu.org header.i=@gcc.gnu.org header.a=rsa-sha256 header.s=default header.b=EthnVlBs; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=2620:52:3:1:0:246e:9693:128c; helo=server2.sourceware.org; envelope-from=gcc-patches-bounces+incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=patchwork.ozlabs.org) Received: from server2.sourceware.org (server2.sourceware.org [IPv6:2620:52:3:1:0:246e:9693:128c]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4Rh9VC2Vtrz1yh8 for ; Thu, 7 Sep 2023 17:14:43 +1000 (AEST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 426CD3896C18 for ; Thu, 7 Sep 2023 07:14:41 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 426CD3896C18 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1694070881; bh=UCYVnoWcM9Hjp/0/3P6e6CPQGHwRN9F+CmTQEsm/6qc=; h=To:Subject:Date:List-Id:List-Unsubscribe:List-Archive:List-Post: List-Help:List-Subscribe:From:Reply-To:From; b=EthnVlBs6X4fO+06MKv+vd0UgcCZGQygzp06W6Zq8RhIXm+oxHonIjzi0oMdJWjVL nEyI+gsdDHhvrEmneafVuNtdJv3Jv/8CG+/qu8uSTtuuODAYzjx6YMDj+Qr5aiDTbM ja89maQ2vAkUYdZUMYP3d2VfVhxprijVcZPpSCTc= 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.129.124]) by sourceware.org (Postfix) with ESMTPS id 787463857806 for ; Thu, 7 Sep 2023 07:11:55 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 787463857806 Received: from mimecast-mx02.redhat.com (mx-ext.redhat.com [66.187.233.73]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-15-9zErRiNiMemjTeDQo5wqDQ-1; Thu, 07 Sep 2023 03:11:52 -0400 X-MC-Unique: 9zErRiNiMemjTeDQo5wqDQ-1 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.rdu2.redhat.com [10.11.54.5]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 8BB31280FED5; Thu, 7 Sep 2023 07:11:51 +0000 (UTC) Received: from localhost (unknown [10.42.28.200]) by smtp.corp.redhat.com (Postfix) with ESMTP id 33E6A63F7A; Thu, 7 Sep 2023 07:11:51 +0000 (UTC) To: libstdc++@gcc.gnu.org, gcc-patches@gcc.gnu.org Subject: [committed] libstdc++: Fix tests that fail in C++23 Date: Thu, 7 Sep 2023 08:11:47 +0100 Message-ID: <20230907071150.866908-1-jwakely@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.1 on 10.11.54.5 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, KAM_NUMSUBJECT, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H4, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NONE, TXREP autolearn=ham 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.30 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+incoming=patchwork.ozlabs.org@gcc.gnu.org Sender: "Gcc-patches" Tested x86_64-linux. Pushed to trunk. -- >8 -- The tests for the std::ranges access CPOs (ranges::begin etc) use pathological types with ridiculous overload sets for begin/end/data members, to exercise all the corner cases in the specification. Since P2278R4 "cbegin should always return a constant iterator" was implemented for C++23 mode, some of the range access CPOs now require the argument to satisfy the range concept, which was not previously required. The behaviour of the CPO also changes for corner cases where the type is a range R for which constant_range is satisfied in addition to constant_range (meaning there's no need to wrap its iterators in const_iterator). Adjust the expected results for those pathological types that changed meaning in C++23, and add some new types to verify other corner cases. Some other range adaptor tests fail for C++20 because they assert that ranges::end and ranges::cend return different types, which is not true when the type satisfies constant_range. This fixes the tests to PASS for both C++20 and C++23 (and later). libstdc++-v3/ChangeLog: * testsuite/std/ranges/access/cbegin.cc: Adjust for C++23 compatibility. * testsuite/std/ranges/access/cdata.cc: Likewise. * testsuite/std/ranges/access/cend.cc: Likewise. * testsuite/std/ranges/access/crbegin.cc: Likewise. * testsuite/std/ranges/access/crend.cc: Likewise. * testsuite/std/ranges/adaptors/take.cc: Likewise. * testsuite/std/ranges/adaptors/take_while.cc: Likewise. * testsuite/std/ranges/adaptors/transform.cc: Likewise. --- .../testsuite/std/ranges/access/cbegin.cc | 13 ++++++ .../testsuite/std/ranges/access/cdata.cc | 38 +++++++++++++++++- .../testsuite/std/ranges/access/cend.cc | 29 +++++++++++++- .../testsuite/std/ranges/access/crbegin.cc | 40 ++++++++++++++++++- .../testsuite/std/ranges/access/crend.cc | 33 ++++++++++++++- .../testsuite/std/ranges/adaptors/take.cc | 2 + .../std/ranges/adaptors/take_while.cc | 2 + .../std/ranges/adaptors/transform.cc | 4 ++ 8 files changed, 154 insertions(+), 7 deletions(-) diff --git a/libstdc++-v3/testsuite/std/ranges/access/cbegin.cc b/libstdc++-v3/testsuite/std/ranges/access/cbegin.cc index 10376040c16..3667b0d021f 100644 --- a/libstdc++-v3/testsuite/std/ranges/access/cbegin.cc +++ b/libstdc++-v3/testsuite/std/ranges/access/cbegin.cc @@ -48,6 +48,10 @@ struct R friend int* begin(R&&); // this function is not defined friend const int* begin(const R& r) noexcept { return r.a + 2; } friend const int* begin(const R&&); // this function is not defined + +#if __cpp_lib_ranges_as_const + friend const int* end(const R&) noexcept; // C++23 requires this. +#endif }; struct RV // view on an R @@ -56,6 +60,10 @@ struct RV // view on an R friend int* begin(RV&); // this function is not defined friend const int* begin(const RV& rv) noexcept { return begin(std::as_const(rv.r)); } + +#if __cpp_lib_ranges_as_const + friend const int* end(const RV&) noexcept; // C++23 requires this. +#endif }; // Allow ranges::begin to work with RV&& @@ -88,6 +96,11 @@ struct RR friend int* begin(RR&& r) { return r.a + 1; } friend const int* begin(const RR& r) { return r.a + 2; } friend const int* begin(const RR&& r) noexcept { return r.a + 3; } + +#if __cpp_lib_ranges_as_const + short* end() noexcept { return &s + 1; } // C++23 requires this. + const long* end() const { return &l + 1; } // C++23 requires this. +#endif }; // N.B. this is a lie, cbegin on an RR rvalue will return a dangling pointer. diff --git a/libstdc++-v3/testsuite/std/ranges/access/cdata.cc b/libstdc++-v3/testsuite/std/ranges/access/cdata.cc index f0f45eeb6bf..d69b04c8f74 100644 --- a/libstdc++-v3/testsuite/std/ranges/access/cdata.cc +++ b/libstdc++-v3/testsuite/std/ranges/access/cdata.cc @@ -41,15 +41,43 @@ test01() static_assert( has_cdata ); static_assert( has_cdata ); R r; - const R& c = r; +#if ! __cpp_lib_ranges_as_const VERIFY( std::ranges::cdata(r) == (R*)nullptr ); static_assert( noexcept(std::ranges::cdata(r)) ); +#else + // constant_range is not satisfied, so cdata(r) == data(r). + VERIFY( std::ranges::cdata(r) == &r.j ); + static_assert( ! noexcept(std::ranges::cdata(r)) ); +#endif + const R& c = r; VERIFY( std::ranges::cdata(c) == (R*)nullptr ); static_assert( noexcept(std::ranges::cdata(c)) ); // not lvalues and not borrowed ranges static_assert( !has_cdata ); static_assert( !has_cdata ); + + struct R2 + { + // These overloads mean that range and range are satisfied. + int* begin(); + int* end(); + const int* begin() const; + const int* end() const; + + int i = 0; + int j = 0; + int* data() { return &j; } + const R2* data() const noexcept { return nullptr; } + }; + static_assert( has_cdata ); + static_assert( has_cdata ); + R2 r2; + VERIFY( std::ranges::cdata(r2) == (R2*)nullptr ); + static_assert( noexcept(std::ranges::cdata(r2)) ); + const R2& c2 = r2; + VERIFY( std::ranges::cdata(c2) == (R2*)nullptr ); + static_assert( noexcept(std::ranges::cdata(c2)) ); } void @@ -71,6 +99,14 @@ struct R3 friend long* begin(R3&& r); // not defined friend const long* begin(const R3& r) { return &r.l; } friend const short* begin(const R3&&); // not defined + +#if __cpp_lib_ranges_as_const + // C++23 needs these so that range is satisfied and so that + // possibly-const-range is not the same type as R3. + friend long* begin(R3&); + friend long* end(R3&); + friend const long* end(const R3& r); +#endif }; template<> constexpr bool std::ranges::enable_borrowed_range = true; diff --git a/libstdc++-v3/testsuite/std/ranges/access/cend.cc b/libstdc++-v3/testsuite/std/ranges/access/cend.cc index f47bd9d9135..3726ebbf118 100644 --- a/libstdc++-v3/testsuite/std/ranges/access/cend.cc +++ b/libstdc++-v3/testsuite/std/ranges/access/cend.cc @@ -42,7 +42,7 @@ struct R int a[4] = { 0, 1, 2, 3 }; const int* begin() const { return nullptr; } - friend const int* begin(const R&& r) noexcept { return nullptr; } + friend const int* begin(const R&&) noexcept { return nullptr; } // Should be ignored because it doesn't return a sentinel for int* const long* end() const { return nullptr; } @@ -53,6 +53,15 @@ struct R friend const int* end(const R&& r) noexcept { return r.a + 3; } }; +#if __cpp_lib_ranges_as_const +struct R2 : R +{ + // This overload means constant_range will be satisfied: + friend const int* begin(const R2&) noexcept; + friend const int* end(const R2& r2) noexcept { return r2.a + 2; } +}; +#endif + struct RV // view on an R { R& r; @@ -71,12 +80,28 @@ test03() { R r; const R& c = r; +#if ! __cpp_lib_ranges_as_const VERIFY( std::ranges::cend(r) == std::ranges::end(c) ); +#else + // constant_range is not satisfied, so cend(r) == end(r) instead. + VERIFY( std::ranges::cend(r) == std::ranges::end(r) ); + R2 r2; + const R& c2 = r2; + // But constant_range is satisfied, so cend(r2) == end(c2). + VERIFY( std::ranges::cend(r2) == std::ranges::end(c2) ); + VERIFY( std::ranges::cend(r2) == std::ranges::end((const R&)c2) ); +#endif VERIFY( std::ranges::cend(c) == std::ranges::end(c) ); RV v{r}; - const RV cv{r}; +#if ! __cpp_lib_ranges_as_const VERIFY( std::ranges::cend(std::move(v)) == std::ranges::end(c) ); +#else + // constant_range is already satisfied, so cend(v) == end(r) instead. + VERIFY( std::ranges::cend(std::move(v)) == std::ranges::end(r) ); +#endif + + const RV cv{r}; VERIFY( std::ranges::cend(std::move(cv)) == std::ranges::end(c) ); } diff --git a/libstdc++-v3/testsuite/std/ranges/access/crbegin.cc b/libstdc++-v3/testsuite/std/ranges/access/crbegin.cc index db58d9577d9..95b4607fdf1 100644 --- a/libstdc++-v3/testsuite/std/ranges/access/crbegin.cc +++ b/libstdc++-v3/testsuite/std/ranges/access/crbegin.cc @@ -37,13 +37,33 @@ struct R1V // view on an R1 { R1& r; - friend const long* rbegin(R1V&); // this is not defined + friend const long* rbegin(R1V&) { return nullptr; } friend const int* rbegin(const R1V& rv) noexcept { return rv.r.rbegin(); } }; // Allow ranges::end to work with R1V&& template<> constexpr bool std::ranges::enable_borrowed_range = true; +#if __cpp_lib_ranges_as_const +struct R1VC // view on an R1 +{ + R1& r; + + friend const long* rbegin(R1VC&); // this is not defined + friend const int* rbegin(const R1VC& rv) noexcept { return rv.r.rbegin(); } + + // The following ensure that the following are satisfied: + // constant_range && ! constant_range + friend int* begin(R1VC&); + friend int* end(R1VC&); + friend const int* begin(const R1VC&); + friend const int* end(const R1VC&); +}; + +// Allow ranges::end to work with R1VC&& +template<> constexpr bool std::ranges::enable_borrowed_range = true; +#endif + void test01() { @@ -53,8 +73,24 @@ test01() VERIFY( std::ranges::crbegin(c) == std::ranges::rbegin(c) ); R1V v{r}; - const R1V cv{r}; +#if ! __cpp_lib_ranges_as_const + VERIFY( std::ranges::crbegin(v) == std::ranges::rbegin(c) ); VERIFY( std::ranges::crbegin(std::move(v)) == std::ranges::rbegin(c) ); +#else + // constant_range is not satisfied, so crbegin(v) == rbegin(v). + VERIFY( std::ranges::crbegin(v) == (long*)nullptr ); + VERIFY( std::ranges::crbegin(std::move(v)) == (long*)nullptr ); + R1VC v2{r}; + // But constant_range is satisfied: + VERIFY( std::ranges::crbegin(v2) == std::ranges::rbegin(c) ); + VERIFY( std::ranges::crbegin(std::move(v2)) == std::ranges::rbegin(c) ); + const R1VC cv2{r}; + VERIFY( std::ranges::crbegin(cv2) == std::ranges::rbegin(c) ); + VERIFY( std::ranges::crbegin(std::move(cv2)) == std::ranges::rbegin(c) ); +#endif + + const R1V cv{r}; + VERIFY( std::ranges::crbegin(cv) == std::ranges::rbegin(c) ); VERIFY( std::ranges::crbegin(std::move(cv)) == std::ranges::rbegin(c) ); } diff --git a/libstdc++-v3/testsuite/std/ranges/access/crend.cc b/libstdc++-v3/testsuite/std/ranges/access/crend.cc index a7e033fc317..2e4c0f3197a 100644 --- a/libstdc++-v3/testsuite/std/ranges/access/crend.cc +++ b/libstdc++-v3/testsuite/std/ranges/access/crend.cc @@ -86,18 +86,47 @@ struct R3 friend const int* rend(const R3& r) { return &r.i; } }; -// N.B. this is a lie, rend on an R3 rvalue will return a dangling pointer. -template<> constexpr bool std::ranges::enable_borrowed_range = true; +struct R4 +{ + int i = 0; + + // These members mean that range and range are satisfied. + const short* begin() const { return 0; } + const short* end() const { return 0; } + + const int* rbegin() const noexcept { return &i + 1; } + const long* rend() const noexcept { return nullptr; } // not a sentinel for rbegin() + + friend const long* rbegin(const R4&) noexcept { return nullptr; } + friend const int* rend(const R4& r) { return &r.i; } +}; void test03() { R3 r; const R3& c = r; +#if ! __cpp_lib_ranges_as_const VERIFY( std::ranges::crend(r) == std::ranges::rend(c) ); static_assert( !noexcept(std::ranges::crend(r)) ); +#else + // constant_range is not satisfied, so crend(r) is equivalent + // to const_sentinel{rend(r)}, which is ill-formed because range + // is not satisfied. + static_assert( not std::ranges::range ); + static_assert( not std::ranges::range ); +#endif VERIFY( std::ranges::crend(c) == std::ranges::rend(c) ); static_assert( !noexcept(std::ranges::crend(c)) ); + + R4 r4; + const R4& c4 = r4; + auto b = std::ranges::rbegin(r4); + auto s0 = std::ranges::rend(r4); + static_assert( std::ranges::__access::__adl_rend ); + auto s = std::ranges::crend(r4); + auto s2 = std::ranges::rend(c4); + // VERIFY( std::ranges::crend(r4) == std::ranges::rend(c4) ); } void diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/take.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/take.cc index 290b5c2bba5..a1f6068bc09 100644 --- a/libstdc++-v3/testsuite/std/ranges/adaptors/take.cc +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/take.cc @@ -94,8 +94,10 @@ test05() // Verify that _Sentinel is implicitly convertible to _Sentinel. static_assert(!ranges::common_range); +#if ! __cpp_lib_ranges_as_const static_assert(!std::same_as); +#endif auto b = ranges::cend(v); b = ranges::end(v); } diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/take_while.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/take_while.cc index 46060771483..098091edc8b 100644 --- a/libstdc++-v3/testsuite/std/ranges/adaptors/take_while.cc +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/take_while.cc @@ -64,8 +64,10 @@ test03() // Verify that _Sentinel is implicitly convertible to _Sentinel. static_assert(!ranges::common_range); +#if ! __cpp_lib_ranges_as_const static_assert(!std::same_as); +#endif auto b = ranges::cend(v); b = ranges::end(v); } diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc index 9d52cb01dad..d9801d5abb3 100644 --- a/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc @@ -108,16 +108,20 @@ test05() auto r = ranges::subrange{i, std::default_sentinel}; auto v = r | views::transform(std::negate{}); +#if ! __cpp_lib_ranges_as_const // Verify that _Iterator is implicitly convertible to _Iterator. static_assert(!std::same_as); +#endif auto a = ranges::cbegin(v); a = ranges::begin(v); +#if ! __cpp_lib_ranges_as_const // Verify that _Sentinel is implicitly convertible to _Sentinel. static_assert(!ranges::common_range); static_assert(!std::same_as); +#endif auto b = ranges::cend(v); b = ranges::end(v); }