public inbox for libstdc++-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc/devel/c++-coroutines] libstdc++: Constrain chrono::duration conversions [LWG 2094]
@ 2020-09-01 19:26 Iain D Sandoe
  0 siblings, 0 replies; only message in thread
From: Iain D Sandoe @ 2020-09-01 19:26 UTC (permalink / raw)
  To: gcc-cvs, libstdc++-cvs

https://gcc.gnu.org/g:b1850c617b14eedaf60b358f3b7d4707cff73b8a

commit b1850c617b14eedaf60b358f3b7d4707cff73b8a
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Tue Sep 1 18:18:26 2020 +0100

    libstdc++: Constrain chrono::duration conversions [LWG 2094]
    
    The chrono::duration constructor that converts from another duration
    type is meant to be constrained so that it doesn't participate in
    overload resolution if the ratio of the periods cannot be represented as
    a std::ratio.
    
    Because our std::ratio_divide is not SFINAE-friendly the evaluation of
    __is_harmonic results in an error outside the immediate context when an
    overflow occurs. I intend to make ratio_divide (and ratio_multiply)
    SFINAE-friendly in a future patch, but for now this patch just
    introduces a new SFINAE-friendly alias template for the division.
    
    The standard doesn't require it, but it also seems right to constrain
    the constructor with std::is_convertible_v<_Rep2, rep>.
    
    libstdc++-v3/ChangeLog:
    
            * include/std/chrono (duration::_S_gcd(intmax_t, intmax_t)):
            New helper function for finding GCD of two positive intmax_t
            values.
            (duration::__divide): New helper alias for dividing one period
            by another.
            (duration::__is_harmonic): Use __divide not ratio_divide.
            (duration(const duration<R2, P2>&)): Require the duration rep
            types to be convertible.
            * testsuite/20_util/duration/cons/dr2094.cc: New test.
            * testsuite/20_util/duration/requirements/reduced_period.cc:
            Fix definition of unused member functions in test type.
            * testsuite/20_util/duration/requirements/typedefs_neg2.cc:
            Adjust expected errors.

Diff:
---
 libstdc++-v3/include/std/chrono                    | 21 ++++++-
 .../testsuite/20_util/duration/cons/dr2094.cc      | 64 ++++++++++++++++++++++
 .../duration/requirements/reduced_period.cc        | 10 ++--
 .../20_util/duration/requirements/typedefs_neg2.cc |  1 +
 4 files changed, 90 insertions(+), 6 deletions(-)

diff --git a/libstdc++-v3/include/std/chrono b/libstdc++-v3/include/std/chrono
index 524d23ea6a7..1682263fd9f 100644
--- a/libstdc++-v3/include/std/chrono
+++ b/libstdc++-v3/include/std/chrono
@@ -424,10 +424,28 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	template<typename _Rep2>
 	  using __is_float = treat_as_floating_point<_Rep2>;
 
+	static constexpr intmax_t
+	_S_gcd(intmax_t __m, intmax_t __n) noexcept
+	{
+	  // Duration only allows positive periods so we don't need to
+	  // support negative values here (unlike __static_gcd and std::gcd).
+	  return (__m == 0) ? __n : (__n == 0) ? __m : _S_gcd(__n, __m % __n);
+	}
+
+	// _GLIBCXX_RESOLVE_LIB_DEFECTS
+	// 2094. overflow shouldn't participate in overload resolution
+	// 3090. What is [2094] intended to mean?
+	// This only produces a valid type if no overflow occurs.
+	template<typename _R1, typename _R2,
+		 intmax_t __gcd1 = _S_gcd(_R1::num, _R2::num),
+		 intmax_t __gcd2 = _S_gcd(_R1::den, _R2::den)>
+	  using __divide = ratio<(_R1::num / __gcd1) * (_R2::den / __gcd2),
+				 (_R1::den / __gcd2) * (_R2::num / __gcd1)>;
+
 	// _Period2 is an exact multiple of _Period
 	template<typename _Period2>
 	  using __is_harmonic
-	    = __bool_constant<ratio_divide<_Period2, _Period>::den == 1>;
+	    = __bool_constant<__divide<_Period2, _Period>::den == 1>;
 
       public:
 
@@ -453,6 +471,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  : __r(static_cast<rep>(__rep)) { }
 
 	template<typename _Rep2, typename _Period2, typename = _Require<
+		 is_convertible<const _Rep2&, rep>,
 		 __or_<__is_float<rep>,
 		       __and_<__is_harmonic<_Period2>,
 			      __not_<__is_float<_Rep2>>>>>>
diff --git a/libstdc++-v3/testsuite/20_util/duration/cons/dr2094.cc b/libstdc++-v3/testsuite/20_util/duration/cons/dr2094.cc
new file mode 100644
index 00000000000..859c14fe693
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/duration/cons/dr2094.cc
@@ -0,0 +1,64 @@
+// 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
+// <http://www.gnu.org/licenses/>.
+
+// { dg-do compile { target c++11 } }
+
+#include <chrono>
+
+void
+test01()
+{
+  using namespace std::chrono;
+  using std::exa;
+
+  // LWG 2094
+  // duration conversion overflow shouldn't participate in overload resolution
+  bool f(milliseconds);
+  void f(seconds);
+  duration<int,exa> r(1);
+  f(r);
+}
+
+void
+test02()
+{
+  struct Number
+  {
+    explicit
+    Number(int t = 0) : i(t)
+    { }
+
+    int i = 0;
+
+    Number& operator+=(Number n) { i += n.i; return *this; }
+    Number& operator-=(Number n) { i -= n.i; return *this; }
+    Number& operator*=(Number n) { i *= n.i; return *this; }
+    Number& operator/=(Number n) { i /= n.i; return *this; }
+    Number& operator%=(Number n) { i %= n.i; return *this; }
+
+    Number operator+(Number n) { return Number{ i + n.i }; }
+    Number operator-(Number n) { return Number{ i - n.i }; }
+    Number operator*(Number n) { return Number{ i * n.i }; }
+    Number operator/(Number n) { return Number{ i / n.i }; }
+    Number operator%(Number n) { return Number{ i % n.i }; }
+  };
+
+  using std::chrono::duration;
+
+  static_assert( ! std::is_constructible<duration<int>, duration<Number>>(),
+      "duration(const duration<R2, P2>&) constrained on R2 -> R conversion" );
+}
diff --git a/libstdc++-v3/testsuite/20_util/duration/requirements/reduced_period.cc b/libstdc++-v3/testsuite/20_util/duration/requirements/reduced_period.cc
index 4c7472699d2..87124e526b3 100644
--- a/libstdc++-v3/testsuite/20_util/duration/requirements/reduced_period.cc
+++ b/libstdc++-v3/testsuite/20_util/duration/requirements/reduced_period.cc
@@ -151,11 +151,11 @@ struct Number
   Number& operator/=(Number n) { i /= n.i; return *this; }
   Number& operator%=(Number n) { i %= n.i; return *this; }
 
-  Number operator+(Number n) { return { i + n.i }; }
-  Number operator-(Number n) { return { i - n.i }; }
-  Number operator*(Number n) { return { i * n.i }; }
-  Number operator/(Number n) { return { i / n.i }; }
-  Number operator%(Number n) { return { i % n.i }; }
+  Number operator+(Number n) { return Number{ i + n.i }; }
+  Number operator-(Number n) { return Number{ i - n.i }; }
+  Number operator*(Number n) { return Number{ i * n.i }; }
+  Number operator/(Number n) { return Number{ i / n.i }; }
+  Number operator%(Number n) { return Number{ i % n.i }; }
 };
 
 namespace std
diff --git a/libstdc++-v3/testsuite/20_util/duration/requirements/typedefs_neg2.cc b/libstdc++-v3/testsuite/20_util/duration/requirements/typedefs_neg2.cc
index 02dab73b0eb..e44b01c4c82 100644
--- a/libstdc++-v3/testsuite/20_util/duration/requirements/typedefs_neg2.cc
+++ b/libstdc++-v3/testsuite/20_util/duration/requirements/typedefs_neg2.cc
@@ -32,4 +32,5 @@ void test01()
 
 // { dg-error "must be a specialization of ratio" "" { target *-*-* } 0 }
 // { dg-prune-output "'num' is not a member of 'int'" }
+// { dg-prune-output "'den' is not a member of 'int'" }
 // { dg-prune-output "'int' is not a class, struct, or union type" }


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2020-09-01 19:26 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-09-01 19:26 [gcc/devel/c++-coroutines] libstdc++: Constrain chrono::duration conversions [LWG 2094] Iain D Sandoe

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).