From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [63.128.21.124]) by sourceware.org (Postfix) with ESMTP id 3C90D3858C2B for ; Thu, 5 Nov 2020 19:09:47 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.3.2 sourceware.org 3C90D3858C2B Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-362-FMf_EUmxOwSCFfqXL0As9w-1; Thu, 05 Nov 2020 14:09:44 -0500 X-MC-Unique: FMf_EUmxOwSCFfqXL0As9w-1 Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.phx2.redhat.com [10.5.11.13]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id A11CA1016CE1; Thu, 5 Nov 2020 19:09:43 +0000 (UTC) Received: from localhost (unknown [10.33.36.7]) by smtp.corp.redhat.com (Postfix) with ESMTP id D90907367C; Thu, 5 Nov 2020 19:09:42 +0000 (UTC) Date: Thu, 5 Nov 2020 19:09:41 +0000 From: Jonathan Wakely To: libstdc++@gcc.gnu.org, gcc-patches@gcc.gnu.org Subject: [committed] libstdc++: Fix constraints on std::optional comparisons [PR 96269] Message-ID: <20201105190941.GA3537038@redhat.com> MIME-Version: 1.0 X-Clacks-Overhead: GNU Terry Pratchett X-Scanned-By: MIMEDefang 2.79 on 10.5.11.13 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Type: multipart/mixed; boundary="IJpNTDwzlM2Ie8A6" Content-Disposition: inline X-Spam-Status: No, score=-14.1 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H5, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=unavailable autolearn_force=no version=3.4.2 X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on server2.sourceware.org X-BeenThere: libstdc++@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Libstdc++ mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 05 Nov 2020 19:09:49 -0000 --IJpNTDwzlM2Ie8A6 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline The relational operators for std::optional were using the wrong types in the declval expressions used to constrain them. Instead of using const lvalues they were using non-const rvalues, which meant that a type might satisfy the constraints but then give an error when the function body was instantiated. libstdc++-v3/ChangeLog: PR libstdc++/96269 * include/std/optional (operator==, operator!=, operator<) (operator>, operator<=, operator>=): Fix types used in SFINAE constraints. * testsuite/20_util/optional/relops/96269.cc: New test. Tested powerpc64le-linux. Committed to trunk. I'll backport to gcc-10 too. --IJpNTDwzlM2Ie8A6 Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="patch.txt" commit cdd2d448d8200ed5ebcb232163954367b553291e Author: Jonathan Wakely Date: Thu Nov 5 18:36:19 2020 libstdc++: Fix constraints on std::optional comparisons [PR 96269] The relational operators for std::optional were using the wrong types in the declval expressions used to constrain them. Instead of using const lvalues they were using non-const rvalues, which meant that a type might satisfy the constraints but then give an error when the function body was instantiated. libstdc++-v3/ChangeLog: PR libstdc++/96269 * include/std/optional (operator==, operator!=, operator<) (operator>, operator<=, operator>=): Fix types used in SFINAE constraints. * testsuite/20_util/optional/relops/96269.cc: New test. diff --git a/libstdc++-v3/include/std/optional b/libstdc++-v3/include/std/optional index f9f42efe09ce..5ea5b39d0e69 100644 --- a/libstdc++-v3/include/std/optional +++ b/libstdc++-v3/include/std/optional @@ -1002,11 +1002,41 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION using __optional_relop_t = enable_if_t::value, bool>; + template + using __optional_eq_t = __optional_relop_t< + decltype(std::declval() == std::declval()) + >; + + template + using __optional_ne_t = __optional_relop_t< + decltype(std::declval() != std::declval()) + >; + + template + using __optional_lt_t = __optional_relop_t< + decltype(std::declval() < std::declval()) + >; + + template + using __optional_gt_t = __optional_relop_t< + decltype(std::declval() > std::declval()) + >; + + template + using __optional_le_t = __optional_relop_t< + decltype(std::declval() <= std::declval()) + >; + + template + using __optional_ge_t = __optional_relop_t< + decltype(std::declval() >= std::declval()) + >; + // Comparisons between optional values. template constexpr auto operator==(const optional<_Tp>& __lhs, const optional<_Up>& __rhs) - -> __optional_relop_t() == declval<_Up>())> + -> __optional_eq_t<_Tp, _Up> { return static_cast(__lhs) == static_cast(__rhs) && (!__lhs || *__lhs == *__rhs); @@ -1015,7 +1045,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template constexpr auto operator!=(const optional<_Tp>& __lhs, const optional<_Up>& __rhs) - -> __optional_relop_t() != declval<_Up>())> + -> __optional_ne_t<_Tp, _Up> { return static_cast(__lhs) != static_cast(__rhs) || (static_cast(__lhs) && *__lhs != *__rhs); @@ -1024,7 +1054,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template constexpr auto operator<(const optional<_Tp>& __lhs, const optional<_Up>& __rhs) - -> __optional_relop_t() < declval<_Up>())> + -> __optional_lt_t<_Tp, _Up> { return static_cast(__rhs) && (!__lhs || *__lhs < *__rhs); } @@ -1032,7 +1062,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template constexpr auto operator>(const optional<_Tp>& __lhs, const optional<_Up>& __rhs) - -> __optional_relop_t() > declval<_Up>())> + -> __optional_gt_t<_Tp, _Up> { return static_cast(__lhs) && (!__rhs || *__lhs > *__rhs); } @@ -1040,7 +1070,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template constexpr auto operator<=(const optional<_Tp>& __lhs, const optional<_Up>& __rhs) - -> __optional_relop_t() <= declval<_Up>())> + -> __optional_le_t<_Tp, _Up> { return !__lhs || (static_cast(__rhs) && *__lhs <= *__rhs); } @@ -1048,7 +1078,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template constexpr auto operator>=(const optional<_Tp>& __lhs, const optional<_Up>& __rhs) - -> __optional_relop_t() >= declval<_Up>())> + -> __optional_ge_t<_Tp, _Up> { return !__rhs || (static_cast(__lhs) && *__lhs >= *__rhs); } @@ -1134,73 +1164,73 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template constexpr auto operator==(const optional<_Tp>& __lhs, const _Up& __rhs) - -> __optional_relop_t() == declval<_Up>())> + -> __optional_eq_t<_Tp, _Up> { return __lhs && *__lhs == __rhs; } template constexpr auto operator==(const _Up& __lhs, const optional<_Tp>& __rhs) - -> __optional_relop_t() == declval<_Tp>())> + -> __optional_eq_t<_Up, _Tp> { return __rhs && __lhs == *__rhs; } template constexpr auto operator!=(const optional<_Tp>& __lhs, const _Up& __rhs) - -> __optional_relop_t() != declval<_Up>())> + -> __optional_ne_t<_Tp, _Up> { return !__lhs || *__lhs != __rhs; } template constexpr auto operator!=(const _Up& __lhs, const optional<_Tp>& __rhs) - -> __optional_relop_t() != declval<_Tp>())> + -> __optional_ne_t<_Up, _Tp> { return !__rhs || __lhs != *__rhs; } template constexpr auto operator<(const optional<_Tp>& __lhs, const _Up& __rhs) - -> __optional_relop_t() < declval<_Up>())> + -> __optional_lt_t<_Tp, _Up> { return !__lhs || *__lhs < __rhs; } template constexpr auto operator<(const _Up& __lhs, const optional<_Tp>& __rhs) - -> __optional_relop_t() < declval<_Tp>())> + -> __optional_lt_t<_Up, _Tp> { return __rhs && __lhs < *__rhs; } template constexpr auto operator>(const optional<_Tp>& __lhs, const _Up& __rhs) - -> __optional_relop_t() > declval<_Up>())> + -> __optional_gt_t<_Tp, _Up> { return __lhs && *__lhs > __rhs; } template constexpr auto operator>(const _Up& __lhs, const optional<_Tp>& __rhs) - -> __optional_relop_t() > declval<_Tp>())> + -> __optional_gt_t<_Up, _Tp> { return !__rhs || __lhs > *__rhs; } template constexpr auto operator<=(const optional<_Tp>& __lhs, const _Up& __rhs) - -> __optional_relop_t() <= declval<_Up>())> + -> __optional_le_t<_Tp, _Up> { return !__lhs || *__lhs <= __rhs; } template constexpr auto operator<=(const _Up& __lhs, const optional<_Tp>& __rhs) - -> __optional_relop_t() <= declval<_Tp>())> + -> __optional_le_t<_Up, _Tp> { return __rhs && __lhs <= *__rhs; } template constexpr auto operator>=(const optional<_Tp>& __lhs, const _Up& __rhs) - -> __optional_relop_t() >= declval<_Up>())> + -> __optional_ge_t<_Tp, _Up> { return __lhs && *__lhs >= __rhs; } template constexpr auto operator>=(const _Up& __lhs, const optional<_Tp>& __rhs) - -> __optional_relop_t() >= declval<_Tp>())> + -> __optional_ge_t<_Up, _Tp> { return !__rhs || __lhs >= *__rhs; } #ifdef __cpp_lib_three_way_comparison diff --git a/libstdc++-v3/testsuite/20_util/optional/relops/96269.cc b/libstdc++-v3/testsuite/20_util/optional/relops/96269.cc new file mode 100644 index 000000000000..2054d3643c66 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/optional/relops/96269.cc @@ -0,0 +1,76 @@ +// Copyright (C) 2020 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// . + +// { dg-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include + +struct X +{ + template + bool operator==(const T&) /* not const */ { return false; } + + template + bool operator!=(const T&) /* not const */ { return false; } + + template + bool operator<(const T&) /* not const */ { return false; } + + template + bool operator>(const T&) /* not const */ { return false; } + + template + bool operator<=(const T&) /* not const */ { return false; } + + template + bool operator>=(const T&) /* not const */ { return false; } +}; + +void test01() +{ + // PR 96269 optional comparison with nullopt fails + std::optional x; + bool eq [[maybe_unused]] = std::nullopt == x; + + bool ne [[maybe_unused]] = std::nullopt != x; + bool lt [[maybe_unused]] = std::nullopt < x; + bool gt [[maybe_unused]] = std::nullopt > x; + bool le [[maybe_unused]] = std::nullopt <= x; + bool ge [[maybe_unused]] = std::nullopt >= x; +} + +template + concept optional_lt_cmp + = requires(std::optional o, T t) { { o < t } -> std::same_as; }; + +template + concept optional_gt_cmp + = requires(std::optional o, T t) { { o > t } -> std::same_as; }; + +template + concept optional_le_cmp + = requires(std::optional o, T t) { { o <= t } -> std::same_as; }; + +template + concept optional_ge_cmp + = requires(std::optional o, T t) { { o >= t } -> std::same_as; }; + +static_assert( ! optional_lt_cmp ); +static_assert( ! optional_gt_cmp ); +static_assert( ! optional_le_cmp ); +static_assert( ! optional_ge_cmp ); --IJpNTDwzlM2Ie8A6--