From efa6d803c624f9251d0ab7881122501bb9d27368 Mon Sep 17 00:00:00 2001 From: Marshall Clow Date: Mon, 1 Apr 2019 16:38:02 +0000 Subject: [PATCH] Fix PR41130 - 'operator/ of std::chrono::duration and custom type'. Thanks to Zulan for the report, and Howard for the direction of the fix. llvm-svn: 357410 --- libcxx/include/chrono | 38 +++++++--------------- libcxx/test/std/utilities/time/rep.h | 36 ++++++++++++++++++++ .../time.duration.arithmetic/op_divide=.pass.cpp | 10 ++++++ .../op_mod=duration.pass.cpp | 10 ++++++ .../time.duration.arithmetic/op_mod=rep.pass.cpp | 15 +++++++++ .../time.duration.arithmetic/op_times=.pass.cpp | 10 ++++++ .../time.duration.nonmember/op_divide_rep.pass.cpp | 16 ++++++++- .../time.duration.nonmember/op_mod_rep.pass.cpp | 16 ++++++++- .../time.duration.nonmember/op_times_rep.pass.cpp | 16 +++++++++ 9 files changed, 139 insertions(+), 28 deletions(-) diff --git a/libcxx/include/chrono b/libcxx/include/chrono index 686faab..0ee8c32 100644 --- a/libcxx/include/chrono +++ b/libcxx/include/chrono @@ -1262,34 +1262,15 @@ operator*(const _Rep1& __s, const duration<_Rep2, _Period>& __d) // Duration / -template ::value> -struct __duration_divide_result -{ -}; - -template ::type>::value> -struct __duration_divide_imp -{ -}; - -template -struct __duration_divide_imp, _Rep2, true> -{ - typedef duration::type, _Period> type; -}; - -template -struct __duration_divide_result, _Rep2, false> - : __duration_divide_imp, _Rep2> -{ -}; - template inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR -typename __duration_divide_result, _Rep2>::type +typename enable_if +< + !__is_duration<_Rep2>::value && + is_convertible<_Rep2, typename common_type<_Rep1, _Rep2>::type>::value, + duration::type, _Period> +>::type operator/(const duration<_Rep1, _Period>& __d, const _Rep2& __s) { typedef typename common_type<_Rep1, _Rep2>::type _Cr; @@ -1312,7 +1293,12 @@ operator/(const duration<_Rep1, _Period1>& __lhs, const duration<_Rep2, _Period2 template inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR -typename __duration_divide_result, _Rep2>::type +typename enable_if +< + !__is_duration<_Rep2>::value && + is_convertible<_Rep2, typename common_type<_Rep1, _Rep2>::type>::value, + duration::type, _Period> +>::type operator%(const duration<_Rep1, _Period>& __d, const _Rep2& __s) { typedef typename common_type<_Rep1, _Rep2>::type _Cr; diff --git a/libcxx/test/std/utilities/time/rep.h b/libcxx/test/std/utilities/time/rep.h index f54eef3..45a7b1d 100644 --- a/libcxx/test/std/utilities/time/rep.h +++ b/libcxx/test/std/utilities/time/rep.h @@ -25,4 +25,40 @@ public: Rep& operator/=(Rep x) {data_ /= x.data_; return *this;} }; +// This is PR#41130 + +struct NotARep {}; + +// std::chrono:::duration has only '*', '/' and '%' taking a "Rep" parameter + +// Multiplication is commutative, division is not. +template +std::chrono::duration +operator*(std::chrono::duration d, NotARep) { return d; } + +template +std::chrono::duration +operator*(NotARep, std::chrono::duration d) { return d; } + +template +std::chrono::duration +operator/(std::chrono::duration d, NotARep) { return d; } + +template +std::chrono::duration +operator%(std::chrono::duration d, NotARep) { return d; } + +// op= is not commutative. +template +std::chrono::duration& +operator*=(std::chrono::duration& d, NotARep) { return d; } + +template +std::chrono::duration& +operator/=(std::chrono::duration& d, NotARep) { return d; } + +template +std::chrono::duration& +operator%=(std::chrono::duration& d, NotARep) { return d; } + #endif // REP_H diff --git a/libcxx/test/std/utilities/time/time.duration/time.duration.arithmetic/op_divide=.pass.cpp b/libcxx/test/std/utilities/time/time.duration/time.duration.arithmetic/op_divide=.pass.cpp index 753ea5a..64b08a8 100644 --- a/libcxx/test/std/utilities/time/time.duration/time.duration.arithmetic/op_divide=.pass.cpp +++ b/libcxx/test/std/utilities/time/time.duration/time.duration.arithmetic/op_divide=.pass.cpp @@ -16,6 +16,7 @@ #include #include "test_macros.h" +#include "../../rep.h" #if TEST_STD_VER > 14 constexpr bool test_constexpr() @@ -38,5 +39,14 @@ int main(int, char**) static_assert(test_constexpr(), ""); #endif +#if TEST_STD_VER >= 11 + { // This is PR#41130 + std::chrono::nanoseconds d(5); + NotARep n; + d /= n; + assert(d.count() == 5); + } +#endif + return 0; } diff --git a/libcxx/test/std/utilities/time/time.duration/time.duration.arithmetic/op_mod=duration.pass.cpp b/libcxx/test/std/utilities/time/time.duration/time.duration.arithmetic/op_mod=duration.pass.cpp index 649f4aa..499a06e 100644 --- a/libcxx/test/std/utilities/time/time.duration/time.duration.arithmetic/op_mod=duration.pass.cpp +++ b/libcxx/test/std/utilities/time/time.duration/time.duration.arithmetic/op_mod=duration.pass.cpp @@ -16,6 +16,7 @@ #include #include "test_macros.h" +#include "../../rep.h" #if TEST_STD_VER > 14 constexpr bool test_constexpr() @@ -42,5 +43,14 @@ int main(int, char**) static_assert(test_constexpr(), ""); #endif +#if TEST_STD_VER >= 11 + { // This is PR#41130 + std::chrono::nanoseconds d(5); + NotARep n; + d %= n; + assert(d.count() == 5); + } +#endif + return 0; } diff --git a/libcxx/test/std/utilities/time/time.duration/time.duration.arithmetic/op_mod=rep.pass.cpp b/libcxx/test/std/utilities/time/time.duration/time.duration.arithmetic/op_mod=rep.pass.cpp index 0eb73ee..f5ef512 100644 --- a/libcxx/test/std/utilities/time/time.duration/time.duration.arithmetic/op_mod=rep.pass.cpp +++ b/libcxx/test/std/utilities/time/time.duration/time.duration.arithmetic/op_mod=rep.pass.cpp @@ -17,6 +17,11 @@ #include "test_macros.h" +class NotARep {}; + +typedef std::chrono::seconds Duration; +Duration operator%=(Duration d, NotARep) { return d; } + #if TEST_STD_VER > 14 constexpr bool test_constexpr() { @@ -38,5 +43,15 @@ int main(int, char**) static_assert(test_constexpr(), ""); #endif +#if TEST_STD_VER >= 11 + { // This is PR#41130 + Duration d(5); + NotARep n; + d %= n; + assert(d.count() == 5); + } +#endif + + return 0; } diff --git a/libcxx/test/std/utilities/time/time.duration/time.duration.arithmetic/op_times=.pass.cpp b/libcxx/test/std/utilities/time/time.duration/time.duration.arithmetic/op_times=.pass.cpp index 51c20c5..06c9855 100644 --- a/libcxx/test/std/utilities/time/time.duration/time.duration.arithmetic/op_times=.pass.cpp +++ b/libcxx/test/std/utilities/time/time.duration/time.duration.arithmetic/op_times=.pass.cpp @@ -16,6 +16,7 @@ #include #include "test_macros.h" +#include "../../rep.h" #if TEST_STD_VER > 14 constexpr bool test_constexpr() @@ -38,5 +39,14 @@ int main(int, char**) static_assert(test_constexpr(), ""); #endif +#if TEST_STD_VER >= 11 + { // This is PR#41130 + std::chrono::nanoseconds d(5); + NotARep n; + d *= n; + assert(d.count() == 5); + } +#endif + return 0; } diff --git a/libcxx/test/std/utilities/time/time.duration/time.duration.nonmember/op_divide_rep.pass.cpp b/libcxx/test/std/utilities/time/time.duration/time.duration.nonmember/op_divide_rep.pass.cpp index 94da113..8b623aa 100644 --- a/libcxx/test/std/utilities/time/time.duration/time.duration.nonmember/op_divide_rep.pass.cpp +++ b/libcxx/test/std/utilities/time/time.duration/time.duration.nonmember/op_divide_rep.pass.cpp @@ -19,11 +19,14 @@ #include #include "test_macros.h" +#include "../../rep.h" int main(int, char**) { { - std::chrono::nanoseconds ns(15); + typedef std::chrono::nanoseconds Dur; + Dur ns(15); + ASSERT_SAME_TYPE(Dur, decltype(ns / 5)); ns = ns / 5; assert(ns.count() == 3); } @@ -35,5 +38,16 @@ int main(int, char**) } #endif +#if TEST_STD_VER >= 11 + { // This is PR#41130 + typedef std::chrono::nanoseconds Duration; + Duration d(5); + NotARep n; + ASSERT_SAME_TYPE(Duration, decltype(d / n)); + d = d / n; + assert(d.count() == 5); + } +#endif + return 0; } diff --git a/libcxx/test/std/utilities/time/time.duration/time.duration.nonmember/op_mod_rep.pass.cpp b/libcxx/test/std/utilities/time/time.duration/time.duration.nonmember/op_mod_rep.pass.cpp index 754b980..0878aff 100644 --- a/libcxx/test/std/utilities/time/time.duration/time.duration.nonmember/op_mod_rep.pass.cpp +++ b/libcxx/test/std/utilities/time/time.duration/time.duration.nonmember/op_mod_rep.pass.cpp @@ -19,11 +19,14 @@ #include #include "test_macros.h" +#include "../../rep.h" int main(int, char**) { { - std::chrono::nanoseconds ns(15); + typedef std::chrono::nanoseconds Dur; + Dur ns(15); + ASSERT_SAME_TYPE(Dur, decltype(ns % 6)); ns = ns % 6; assert(ns.count() == 3); } @@ -35,5 +38,16 @@ int main(int, char**) } #endif +#if TEST_STD_VER >= 11 + { // This is PR#41130 + typedef std::chrono::seconds Duration; + Duration d(5); + NotARep n; + ASSERT_SAME_TYPE(Duration, decltype(d % n)); + d = d % n; + assert(d.count() == 5); + } +#endif + return 0; } diff --git a/libcxx/test/std/utilities/time/time.duration/time.duration.nonmember/op_times_rep.pass.cpp b/libcxx/test/std/utilities/time/time.duration/time.duration.nonmember/op_times_rep.pass.cpp index c3e4996..c331032 100644 --- a/libcxx/test/std/utilities/time/time.duration/time.duration.nonmember/op_times_rep.pass.cpp +++ b/libcxx/test/std/utilities/time/time.duration/time.duration.nonmember/op_times_rep.pass.cpp @@ -24,6 +24,7 @@ #include #include "test_macros.h" +#include "../../rep.h" int main(int, char**) { @@ -34,6 +35,7 @@ int main(int, char**) ns = 6 * ns; assert(ns.count() == 90); } + #if TEST_STD_VER >= 11 { constexpr std::chrono::nanoseconds ns(3); @@ -44,5 +46,19 @@ int main(int, char**) } #endif +#if TEST_STD_VER >= 11 + { // This is related to PR#41130 + typedef std::chrono::nanoseconds Duration; + Duration d(5); + NotARep n; + ASSERT_SAME_TYPE(Duration, decltype(d * n)); + ASSERT_SAME_TYPE(Duration, decltype(n * d)); + d = d * n; + assert(d.count() == 5); + d = n * d; + assert(d.count() == 5); + } +#endif + return 0; } -- 2.7.4