From patchwork Fri Mar 9 00:41:04 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jonathan Wakely X-Patchwork-Id: 883419 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=gcc.gnu.org (client-ip=209.132.180.131; helo=sourceware.org; envelope-from=gcc-patches-return-474473-incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=gcc.gnu.org header.i=@gcc.gnu.org header.b="AI3V09bl"; dkim-atps=neutral Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3zy7qD2S7Hz9sWN for ; Fri, 9 Mar 2018 11:41:27 +1100 (AEDT) DomainKey-Signature: a=rsa-sha1; c=nofws; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender :mime-version:from:date:message-id:subject:to:cc:content-type; q=dns; s=default; b=ixos94b6Imrnr3GqjbML68oZm40/hYwvAOXFlveiOam qXBx26WaGWAZwAAitZWo7Vh9mtTmjzKLsry2Yf064kwmQqRnt0/19XR2VO/FpRJU K/y1ySUWC7om6ANN+rHua5/yRmKz8kfZjYdg7QFg50OTGlXzYmkr9D25bdmF0FR8 = DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender :mime-version:from:date:message-id:subject:to:cc:content-type; s=default; bh=EczcMriKxb/93rwj330cTws+P9E=; b=AI3V09blpu7Gz22/U IhCWKdtylB534W4JajMR/YaE99p08pu6K34QgIo9UOyCuT16uDnyLowjx8tyxY4a h9mpxEsQvkbR/EOhCNdQb8LDnkoXbA0Fq7gENGjqahYodP6nWLUHDVVEIrIjDJ0d aIGHDlBA3pBrYVWBIu6xxV3tv0= Received: (qmail 72387 invoked by alias); 9 Mar 2018 00:41:12 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Received: (qmail 72366 invoked by uid 89); 9 Mar 2018 00:41:11 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-24.5 required=5.0 tests=AWL, BAYES_00, FREEMAIL_FROM, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, KAM_SHORT, RCVD_IN_DNSWL_NONE, SPF_PASS autolearn=ham version=3.3.2 spammy=__t, 2034 X-Spam-User: qpsmtpd, 2 recipients X-HELO: mail-it0-f48.google.com Received: from mail-it0-f48.google.com (HELO mail-it0-f48.google.com) (209.85.214.48) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Fri, 09 Mar 2018 00:41:08 +0000 Received: by mail-it0-f48.google.com with SMTP id k79-v6so832782ita.2; Thu, 08 Mar 2018 16:41:08 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:from:date:message-id:subject:to:cc; bh=IfmxsiKHwTnrXF6izPySKemdbX6D6Xllv5MR9lX5aDE=; b=k03KV7trnYjqoLXql5OLBulw2vGCm0p0sq0kh6HcY7uFKASCQ/6nJ6QSjOKHT+KiK+ 8cbE3Vn6FryAg5wqHer+3hqy++WA1PBLPhOBCid0M0f4qec7CbxlgwYvLCeI9QpSzHsz 6EV3PkZG2SO8oyqih0RMJHLJGON5XDYy1ITwwXvPb524ujc65c5eHsVag/uC+FexM99h mI9+mezYcDJUchUR8rzeVs4W1R7+1xHdKkInX2c1UipnXapb3Ac9UwKmVMgIgUuSaFci 3Uq7nCMYLGigoNNDHm5O6XSPCECbJ3OyuV6GQ78i6LH0Rcc1frtnmdtLCkd2VSwHUk0f mEVQ== X-Gm-Message-State: AElRT7FhtA6nU+BHW/U77sbWhk/4lFPj63+N61QhHVbfYXeszQNKoRSL sQ4ID5pNLuL26LdkDSq9hGqMxrGKKzm6X0wNx0RLm/yo X-Google-Smtp-Source: AG47ELsqbwAXdJNGHQ3VMizEKNgXxhYcoacDUUYGxxd94chsSJi9hLzaBXB8WJfDAIKy2yUotsFfek7fg3r+Vu1b594= X-Received: by 2002:a24:cf46:: with SMTP id y67-v6mr1050635itf.64.1520556066654; Thu, 08 Mar 2018 16:41:06 -0800 (PST) MIME-Version: 1.0 Received: by 10.107.50.83 with HTTP; Thu, 8 Mar 2018 16:41:04 -0800 (PST) From: Jonathan Wakely Date: Fri, 9 Mar 2018 00:41:04 +0000 Message-ID: Subject: PR libstdc++/78420 Make std::less etc. yield total order for pointers To: "libstdc++" , gcc-patches Cc: Jason Merrill , Jakub Jelinek In order to meet the total order requirements of [comparisons] p2 we need to cast unrelated pointers to uintptr_t before comparing them. Those casts aren't allowed in constant expressions, so only cast when __builtin_constant_p says the result of the comparison is not a compile-time constant (because the arguments are not constants, or the result of the comparison is unspecified). When the result is constant just compare the pointers directly without casting. This ensures that the function can be called in constant expressions with suitable arguments, but still yields a total order even for otherwise unspecified pointer comparisons. PR libstdc++/78420 * include/bits/stl_function.h (__ptr_rel_ops): New struct providing relational operators defining total order on pointers. (greater<_Tp*>, less<_Tp*>, greater_equal<_Tp*>, less_equal<_Tp>*): New partial specializations for pointers, using __ptr_rel_ops. (greater, less, greater_equal, less_equal Date: Thu Mar 8 20:27:04 2018 +0000 PR libstdc++/78420 Make std::less etc. yield total order for pointers In order to meet the total order requirements of [comparisons] p2 we need to cast unrelated pointers to uintptr_t before comparing them. Those casts aren't allowed in constant expressions, so only cast when __builtin_constant_p says the result of the comparison is not a compile-time constant (because the arguments are not constants, or the result of the comparison is unspecified). When the result is constant just compare the pointers directly without casting. This ensures that the function can be called in constant expressions with suitable arguments, but still yields a total order even for otherwise unspecified pointer comparisons. PR libstdc++/78420 * include/bits/stl_function.h (__ptr_rel_ops): New struct providing relational operators defining total order on pointers. (greater<_Tp*>, less<_Tp*>, greater_equal<_Tp*>, less_equal<_Tp>*): New partial specializations for pointers, using __ptr_rel_ops. (greater, less, greater_equal, less_equal 201103L + struct __ptr_rel_ops + { + __ptr_rel_ops() = delete; + ~__ptr_rel_ops() = delete; + + template + static _GLIBCXX14_CONSTEXPR bool + _S_greater(_Tp* __x, _Up* __y) + { + if (__builtin_constant_p (__x > __y)) + return __x > __y; + return (__UINTPTR_TYPE__)__x > (__UINTPTR_TYPE__)__y; + } + + template + static _GLIBCXX14_CONSTEXPR bool + _S_less(_Tp* __x, _Up* __y) + { + if (__builtin_constant_p (__x < __y)) + return __x < __y; + return (__UINTPTR_TYPE__)__x < (__UINTPTR_TYPE__)__y; + } + + template + static _GLIBCXX14_CONSTEXPR bool + _S_greater_equal(_Tp* __x, _Up* __y) + { + if (__builtin_constant_p (__x >= __y)) + return __x >= __y; + return (__UINTPTR_TYPE__)__x >= (__UINTPTR_TYPE__)__y; + } + + template + static _GLIBCXX14_CONSTEXPR bool + _S_less_equal(_Tp* __x, _Up* __y) + { + if (__builtin_constant_p (__x <= __y)) + return __x <= __y; + return (__UINTPTR_TYPE__)__x <= (__UINTPTR_TYPE__)__y; + } + }; + + // Partial specialization of std::greater for pointers. + template + struct greater<_Tp*> : public binary_function<_Tp*, _Tp*, bool> + { + _GLIBCXX14_CONSTEXPR bool + operator()(_Tp* const& __x, _Tp* const& __y) const _GLIBCXX_NOTHROW + { return __ptr_rel_ops::_S_greater(__x, __y); } + }; + + // Partial specialization of std::less for pointers. + template + struct less<_Tp*> : public binary_function<_Tp*, _Tp*, bool> + { + _GLIBCXX14_CONSTEXPR bool + operator()(_Tp* const& __x, _Tp* const& __y) const _GLIBCXX_NOTHROW + { return __ptr_rel_ops::_S_less(__x, __y); } + }; + + // Partial specialization of std::greater_equal for pointers. + template + struct greater_equal<_Tp*> : public binary_function<_Tp*, _Tp*, bool> + { + _GLIBCXX14_CONSTEXPR bool + operator()(_Tp* const& __x, _Tp* const& __y) const _GLIBCXX_NOTHROW + { return __ptr_rel_ops::_S_greater_equal(__x, __y); } + }; + + // Partial specialization of std::less_equal for pointers. + template + struct less_equal<_Tp*> : public binary_function<_Tp*, _Tp*, bool> + { + _GLIBCXX14_CONSTEXPR bool + operator()(_Tp* const& __x, _Tp* const& __y) const _GLIBCXX_NOTHROW + { return __ptr_rel_ops::_S_less_equal(__x, __y); } + }; + +#if __cplusplus >= 201402L /// One of the @link comparison_functors comparison functors@endlink. template<> struct equal_to { template - _GLIBCXX14_CONSTEXPR - auto + constexpr auto operator()(_Tp&& __t, _Up&& __u) const noexcept(noexcept(std::forward<_Tp>(__t) == std::forward<_Up>(__u))) -> decltype(std::forward<_Tp>(__t) == std::forward<_Up>(__u)) @@ -427,8 +504,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION struct not_equal_to { template - _GLIBCXX14_CONSTEXPR - auto + constexpr auto operator()(_Tp&& __t, _Up&& __u) const noexcept(noexcept(std::forward<_Tp>(__t) != std::forward<_Up>(__u))) -> decltype(std::forward<_Tp>(__t) != std::forward<_Up>(__u)) @@ -442,14 +518,27 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION struct greater { template - _GLIBCXX14_CONSTEXPR - auto + constexpr auto operator()(_Tp&& __t, _Up&& __u) const noexcept(noexcept(std::forward<_Tp>(__t) > std::forward<_Up>(__u))) -> decltype(std::forward<_Tp>(__t) > std::forward<_Up>(__u)) - { return std::forward<_Tp>(__t) > std::forward<_Up>(__u); } + { + using _Ptrs = __and_>, is_pointer>>; + return _S_cmp(std::forward<_Tp>(__t), std::forward<_Up>(__u), _Ptrs()); + } typedef __is_transparent is_transparent; + + private: + template + static constexpr decltype(auto) + _S_cmp(_Tp&& __t, _Up&& __u, false_type) + { return std::forward<_Tp>(__t) > std::forward<_Up>(__u); } + + template + static constexpr bool + _S_cmp(_Tp* __t, _Up* __u, true_type) noexcept + { return __ptr_rel_ops::_S_greater(__t, __u); } }; /// One of the @link comparison_functors comparison functors@endlink. @@ -457,14 +546,27 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION struct less { template - _GLIBCXX14_CONSTEXPR - auto + constexpr auto operator()(_Tp&& __t, _Up&& __u) const noexcept(noexcept(std::forward<_Tp>(__t) < std::forward<_Up>(__u))) -> decltype(std::forward<_Tp>(__t) < std::forward<_Up>(__u)) - { return std::forward<_Tp>(__t) < std::forward<_Up>(__u); } + { + using _Ptrs = __and_>, is_pointer>>; + return _S_cmp(std::forward<_Tp>(__t), std::forward<_Up>(__u), _Ptrs()); + } typedef __is_transparent is_transparent; + + private: + template + static constexpr decltype(auto) + _S_cmp(_Tp&& __t, _Up&& __u, false_type) + { return std::forward<_Tp>(__t) < std::forward<_Up>(__u); } + + template + static constexpr bool + _S_cmp(_Tp* __t, _Up* __u, true_type) noexcept + { return __ptr_rel_ops::_S_less(__t, __u); } }; /// One of the @link comparison_functors comparison functors@endlink. @@ -472,14 +574,27 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION struct greater_equal { template - _GLIBCXX14_CONSTEXPR - auto + constexpr auto operator()(_Tp&& __t, _Up&& __u) const noexcept(noexcept(std::forward<_Tp>(__t) >= std::forward<_Up>(__u))) -> decltype(std::forward<_Tp>(__t) >= std::forward<_Up>(__u)) - { return std::forward<_Tp>(__t) >= std::forward<_Up>(__u); } + { + using _Ptrs = __and_>, is_pointer>>; + return _S_cmp(std::forward<_Tp>(__t), std::forward<_Up>(__u), _Ptrs()); + } typedef __is_transparent is_transparent; + + private: + template + static constexpr decltype(auto) + _S_cmp(_Tp&& __t, _Up&& __u, false_type) + { return std::forward<_Tp>(__t) >= std::forward<_Up>(__u); } + + template + static constexpr bool + _S_cmp(_Tp* __t, _Up* __u, true_type) noexcept + { return __ptr_rel_ops::_S_greater_equal(__t, __u); } }; /// One of the @link comparison_functors comparison functors@endlink. @@ -487,16 +602,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION struct less_equal { template - _GLIBCXX14_CONSTEXPR - auto + constexpr auto operator()(_Tp&& __t, _Up&& __u) const noexcept(noexcept(std::forward<_Tp>(__t) <= std::forward<_Up>(__u))) -> decltype(std::forward<_Tp>(__t) <= std::forward<_Up>(__u)) - { return std::forward<_Tp>(__t) <= std::forward<_Up>(__u); } + { + using _Ptrs = __and_>, is_pointer>>; + return _S_cmp(std::forward<_Tp>(__t), std::forward<_Up>(__u), _Ptrs()); + } typedef __is_transparent is_transparent; + + private: + template + static constexpr decltype(auto) + _S_cmp(_Tp&& __t, _Up&& __u, false_type) + { return std::forward<_Tp>(__t) <= std::forward<_Up>(__u); } + + template + static constexpr bool + _S_cmp(_Tp* __t, _Up* __u, true_type) noexcept + { return __ptr_rel_ops::_S_less_equal(__t, __u); } }; -#endif +#endif // C++14 /** @} */ // 20.3.4 logical operations diff --git a/libstdc++-v3/testsuite/20_util/function_objects/comparisons_pointer.cc b/libstdc++-v3/testsuite/20_util/function_objects/comparisons_pointer.cc new file mode 100644 index 00000000000..317e42d8f11 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/function_objects/comparisons_pointer.cc @@ -0,0 +1,166 @@ +// Copyright (C) 2018 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-do run } + +#include +#include +#include + +int b[8]; +int a[8]; + +void +test01() +{ + auto p = a + 8; + std::greater gt; + + std::stringstream ss; + ss << gt(p, b) << ' ' << gt(b, p) << ' ' << (!gt(p, b) && !gt(b, p)); + int sum = 0, n = 0; + while (ss >> n) + sum += n; + VERIFY( sum == 1 ); + +#if __cplusplus >= 201402L + static_assert( gt(a+1, a), "constexpr greater" ); + static_assert( !gt(a, a+1), "constexpr greater" ); + + ss.str(""); + ss.clear(); + sum = 0; + auto p2 = a + 8; + std::greater<> gt2; + ss << gt2(p2, b) << ' ' << gt2(b, p2) << ' ' << (!gt2(p2, b) && !gt2(b, p2)); + while (ss >> n) + sum += n; + VERIFY( sum == 1 ); + + static_assert( gt2(a+1, a), "constexpr greater<>" ); + static_assert( !gt2(a, a+1), "constexpr greater<>" ); +#endif +} + +void +test02() +{ + auto p = a + 8; + std::less lt; + + std::stringstream ss; + ss << lt(p, b) << ' ' << lt(b, p) << ' ' << (!lt(p, b) && !lt(b, p)); + int sum = 0, n = 0; + while (ss >> n) + sum += n; + VERIFY( sum == 1 ); + +#if __cplusplus >= 201402L + static_assert( lt(a, a+1), "constexpr less" ); + static_assert( !lt(a+1, a), "constexpr less" ); + + ss.str(""); + ss.clear(); + sum = 0; + auto p2 = a + 8; + std::less<> lt2; + ss << lt2(p2, b) << ' ' << lt2(b, p2) << ' ' << (!lt2(p2, b) && !lt2(b, p2)); + while (ss >> n) + sum += n; + VERIFY( sum == 1 ); + + static_assert( lt2(a, a+1), "constexpr less<>" ); + static_assert( !lt2(a+1, a), "constexpr less<>" ); +#endif +} + +void +test03() +{ + auto p = a + 8; + std::greater_equal ge; + + std::stringstream ss; + ss << !ge(p, b) << ' ' << !ge(b, p) << ' ' << (ge(p, b) && ge(b, p)); + int sum = 0, n = 0; + while (ss >> n) + sum += n; + VERIFY( sum == 1 ); + +#if __cplusplus >= 201402L + static_assert( !ge(a, a+1), "constexpr greater_equal" ); + static_assert( ge(a, a), "constexpr greater_equal" ); + static_assert( ge(a+1, a), "constexpr greater_equal" ); + + ss.str(""); + ss.clear(); + sum = 0; + auto p2 = a + 8; + std::greater_equal<> ge2; + ss << !ge2(p2, b) << ' ' << !ge2(b, p2) << ' ' << (ge2(p2, b) && ge2(b, p2)); + while (ss >> n) + sum += n; + VERIFY( sum == 1 ); + + static_assert( !ge2(a, a+1), "constexpr greater_equal<>" ); + static_assert( ge2(a, a), "constexpr greater_equal<>" ); + static_assert( ge2(a+1, a), "constexpr greater_equal<>" ); +#endif +} + +void +test04() +{ + auto p = a + 8; + std::less_equal le; + + std::stringstream ss; + ss << !le(p, b) << ' ' << !le(b, p) << ' ' << (le(p, b) && le(b, p)); + int sum = 0, n = 0; + while (ss >> n) + sum += n; + VERIFY( sum == 1 ); + +#if __cplusplus >= 201402L + static_assert( !le(a+1, a), "constexpr less_equal" ); + static_assert( le(a, a), "constexpr less_equal" ); + static_assert( le(a, a+1), "constexpr less_equal" ); + + ss.str(""); + ss.clear(); + sum = 0; + auto p2 = a + 8; + std::less_equal<> le2; + ss << !le2(p2, b) << ' ' << !le2(b, p2) << ' ' << (le2(p2, b) && le2(b, p2)); + while (ss >> n) + sum += n; + VERIFY( sum == 1 ); + + static_assert( !le2(a+1, a), "constexpr less_equal<>" ); + static_assert( le2(a, a), "constexpr less_equal<>" ); + static_assert( le2(a, a+1), "constexpr less_equal<>" ); +#endif +} + +int +main() +{ + test01(); + test02(); + test03(); + test04(); +}