From 66b17f256b8ddb106741c06b3835f9709ba3dceb Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Fri, 26 Mar 2021 18:16:49 -0700 Subject: [PATCH 1/6] Cleanup: Use void_t for consistency and shorter mangled names. --- stl/inc/chrono | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stl/inc/chrono b/stl/inc/chrono index 584b8941a5..ed915a121d 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -2914,12 +2914,12 @@ namespace chrono { public: using duration = common_type_t<_Duration, seconds>; - template + template > zoned_time() : _Zone{_Traits::default_zone()} {} zoned_time(const zoned_time&) = default; zoned_time& operator=(const zoned_time&) = default; - template + template > zoned_time(const sys_time<_Duration>& _Sys) : _Zone{_Traits::default_zone()}, _Tp{_Sys} {} explicit zoned_time(_TimeZonePtr _Tz) noexcept /* strengthened */ : _Zone{_STD move(_Tz)} {} From 927ce20910da59c3d2195ff1af08391a432e8cca Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Fri, 26 Mar 2021 18:31:52 -0700 Subject: [PATCH 2/6] Cleanup: Update ICU/IANA comments again. --- .../P0355R7_calendars_and_time_zones_time_zones/test.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/tests/std/tests/P0355R7_calendars_and_time_zones_time_zones/test.cpp b/tests/std/tests/P0355R7_calendars_and_time_zones_time_zones/test.cpp index 78ca4ed52a..288a798f89 100644 --- a/tests/std/tests/P0355R7_calendars_and_time_zones_time_zones/test.cpp +++ b/tests/std/tests/P0355R7_calendars_and_time_zones_time_zones/test.cpp @@ -147,14 +147,13 @@ void timezone_names_test() { } // See GH-1786. These may change over time and might have to be removed from this test. - - // these are some examples in which the ICU.dll and IANA database diverge in what they consider a zone or a link + // These are some examples in which the ICU.dll and IANA database diverge in what they consider a zone or a link. assert(_Locate_zone_impl(my_tzdb.links, "Atlantic/Faroe") != nullptr); // is a time_zone in IANA assert(_Locate_zone_impl(my_tzdb.zones, "Africa/Addis_Ababa") != nullptr); // is a time_zone_link in IANA assert(_Locate_zone_impl(my_tzdb.links, "PST") != nullptr); // time_zone_link does not exist in IANA - assert(_Locate_zone_impl(my_tzdb.links, "Africa/Asmara") != nullptr); // matches IANA but target is wrong + assert(_Locate_zone_impl(my_tzdb.links, "Africa/Asmara") != nullptr); // matches IANA but target is different assert(_Locate_zone_impl(my_tzdb.links, "Africa/Asmara")->target() == "Africa/Asmera"); // target == Africa/Nairobi - assert(_Locate_zone_impl(my_tzdb.zones, "America/Nuuk") == nullptr); // does not exist in ICU (very rare) + assert(_Locate_zone_impl(my_tzdb.zones, "America/Nuuk") == nullptr); // added in ICU 68, update test when it arrives } void validate_timezone_transitions(const time_zone* tz, const Transition& transition) { From 3239b3632198d8bedbfb7c03d21da442f39cf59b Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Fri, 26 Mar 2021 17:08:20 -0700 Subject: [PATCH 3/6] hh_mm_ss can static_assert _Is_duration_v. --- stl/inc/chrono | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/stl/inc/chrono b/stl/inc/chrono index ed915a121d..c3fe7a9493 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -2128,8 +2128,11 @@ namespace chrono { } template - requires _Is_duration_v<_Duration> class hh_mm_ss { + class hh_mm_ss { public: + static_assert(_Is_duration_v<_Duration>, + "N4885 [time.hms.overview]/2 requires Duration to be a specialization of chrono::duration."); + static constexpr unsigned int fractional_width = [] { auto _Num = _Duration::period::num; constexpr auto _Den = _Duration::period::den; From 279c6fe2dc8a6a7ace9fdaf5148373401226b370 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Fri, 26 Mar 2021 18:14:30 -0700 Subject: [PATCH 4/6] Reimplement clock_time_conversion Constraints/Mandates. --- stl/inc/chrono | 71 +++++++++++++++++++------------------------------- 1 file changed, 27 insertions(+), 44 deletions(-) diff --git a/stl/inc/chrono b/stl/inc/chrono index c3fe7a9493..e2c2f874f6 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -3408,79 +3408,62 @@ namespace chrono { // [time.clock.cast.sys] - // TRANSITION, GH-395 workaround, is_same_v -> same_as - template - concept _Is_time_point = requires { - typename _Ty::duration; - requires is_same_v, _Ty>; - }; - - template - concept _Convertible_to_sys_time = - is_same_v<_Clock, system_clock> || requires(const time_point<_Clock, _Duration>& _Time) { - { _Clock::to_sys(_Time) } - ->_Is_time_point; - }; + template + inline constexpr bool _Is_time_point_for_clock = false; template - concept _Convertible_from_sys_time = is_same_v<_Clock, system_clock> || requires(const sys_time<_Duration>& _Time) { - { _Clock::from_sys(_Time) } - ->_Is_time_point<_Clock>; - }; + inline constexpr bool _Is_time_point_for_clock, _Clock> = true; template struct clock_time_conversion { - template + template &>()))>> _NODISCARD auto operator()(const time_point<_SourceClock, _Duration>& _Time) const - noexcept(noexcept(_SourceClock::to_sys(_Time))) /* strengthened */ - requires _Convertible_to_sys_time<_SourceClock, _Duration> { + noexcept(noexcept(_SourceClock::to_sys(_Time))) /* strengthened */ { + static_assert(_Is_time_point_for_clock, + "N4885 [time.clock.cast.sys]/2: Mandates: SourceClock::to_sys(t) returns a sys_time"); return _SourceClock::to_sys(_Time); } }; template struct clock_time_conversion<_DestClock, system_clock> { - template + template &>()))>> _NODISCARD auto operator()(const sys_time<_Duration>& _Time) const - noexcept(noexcept(_DestClock::from_sys(_Time))) /* strengthened */ - requires _Convertible_from_sys_time<_DestClock, _Duration> { + noexcept(noexcept(_DestClock::from_sys(_Time))) /* strengthened */ { + static_assert(_Is_time_point_for_clock, + "N4885 [time.clock.cast.sys]/5: Mandates: DestClock::from_sys(t) returns a " + "time_point"); return _DestClock::from_sys(_Time); } }; // [time.clock.cast.utc] - template - concept _Convertible_to_utc_time = - is_same_v<_Clock, - utc_clock> || is_same_v<_Clock, system_clock> || requires(const time_point<_Clock, _Duration>& _Time) { - { _Clock::to_utc(_Time) } - ->_Is_time_point; - }; - - template - concept _Convertible_from_utc_time = - is_same_v<_Clock, utc_clock> || is_same_v<_Clock, system_clock> || requires(const utc_time<_Duration>& _Time) { - { _Clock::from_utc(_Time) } - ->_Is_time_point<_Clock>; - }; - template struct clock_time_conversion { - template + template &>()))>> _NODISCARD auto operator()(const time_point<_SourceClock, _Duration>& _Time) const - noexcept(noexcept(_SourceClock::to_utc(_Time))) /* strengthened */ - requires _Convertible_to_utc_time<_SourceClock, _Duration> { + noexcept(noexcept(_SourceClock::to_utc(_Time))) /* strengthened */ { + static_assert(_Is_time_point_for_clock, + "N4885 [time.clock.cast.utc]/2: Mandates: SourceClock::to_utc(t) returns a utc_time"); return _SourceClock::to_utc(_Time); } }; template struct clock_time_conversion<_DestClock, utc_clock> { - template + template &>()))>> _NODISCARD auto operator()(const utc_time<_Duration>& _Time) const - noexcept(noexcept(_DestClock::from_utc(_Time))) /* strengthened */ - requires _Convertible_from_utc_time<_DestClock, _Duration> { + noexcept(noexcept(_DestClock::from_utc(_Time))) /* strengthened */ { + static_assert(_Is_time_point_for_clock, + "N4885 [time.clock.cast.utc]/5: Mandates: DestClock::from_utc(t) returns a " + "time_point"); return _DestClock::from_utc(_Time); } }; From d24e8a1293a06828a9c5f2bc17ac6663142df47a Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Fri, 26 Mar 2021 19:33:06 -0700 Subject: [PATCH 5/6] Rewrite clock_cast metaprogramming. --- stl/inc/chrono | 136 +++++++++++++++++++++++++++++++++++-------------- 1 file changed, 98 insertions(+), 38 deletions(-) diff --git a/stl/inc/chrono b/stl/inc/chrono index e2c2f874f6..e554cee520 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -3470,52 +3470,112 @@ namespace chrono { // [time.clock.cast.fn] - // FUNCTION TEMPLATE clock_cast + enum class _Clock_cast_strategy { + _Direct, + _Via_sys, + _Via_utc, + _Via_utc_from_sys, + _Via_sys_from_utc, + _Two_step_ambiguous, + _Three_step_ambiguous, + _None, + }; + + template + inline constexpr bool _Has_two_step_conversion = false; + + template + inline constexpr bool + _Has_two_step_conversion<_Conv1, _Conv2, _Tp, void_t())))>> = true; + + template + inline constexpr bool _Has_three_step_conversion = false; + + template + inline constexpr bool _Has_three_step_conversion<_Conv1, _Conv2, _Conv3, _Tp, + void_t()))))>> = true; + template + _NODISCARD _CONSTEVAL _Clock_cast_strategy _Choose_clock_cast() noexcept { + using _Tp = const time_point<_SourceClock, _Duration>&; + + if constexpr (is_invocable_v, _Tp>) { + return _Clock_cast_strategy::_Direct; + } else { + constexpr bool _Has_sys = _Has_two_step_conversion< // + clock_time_conversion<_DestClock, system_clock>, // + clock_time_conversion, _Tp>; + + constexpr bool _Has_utc = _Has_two_step_conversion< // + clock_time_conversion<_DestClock, utc_clock>, // + clock_time_conversion, _Tp>; + + if constexpr (_Has_sys && _Has_utc) { + return _Clock_cast_strategy::_Two_step_ambiguous; + } else if constexpr (_Has_sys) { + return _Clock_cast_strategy::_Via_sys; + } else if constexpr (_Has_utc) { + return _Clock_cast_strategy::_Via_utc; + } else { + constexpr bool _Has_utc_from_sys = _Has_three_step_conversion< // + clock_time_conversion<_DestClock, utc_clock>, // + clock_time_conversion, // + clock_time_conversion, _Tp>; + + constexpr bool _Has_sys_from_utc = _Has_three_step_conversion< // + clock_time_conversion<_DestClock, system_clock>, // + clock_time_conversion, // + clock_time_conversion, _Tp>; + + if constexpr (_Has_utc_from_sys && _Has_sys_from_utc) { + return _Clock_cast_strategy::_Three_step_ambiguous; + } else if constexpr (_Has_utc_from_sys) { + return _Clock_cast_strategy::_Via_utc_from_sys; + } else if constexpr (_Has_sys_from_utc) { + return _Clock_cast_strategy::_Via_sys_from_utc; + } else { + return _Clock_cast_strategy::_None; + } + } + } + } + + template + inline constexpr auto _Clock_cast_choice = _Choose_clock_cast<_DestClock, _SourceClock, _Duration>(); + + // FUNCTION TEMPLATE clock_cast + template != _Clock_cast_strategy::_None, int> = 0> _NODISCARD auto clock_cast(const time_point<_SourceClock, _Duration>& _Time) { - constexpr bool _Has_direct_conversion = - is_invocable_v, decltype(_Time)>; - - constexpr bool _Utc_from_src = _Convertible_to_utc_time<_SourceClock, _Duration>; - constexpr bool _Sys_from_src = _Convertible_to_sys_time<_SourceClock, _Duration>; - constexpr bool _Dest_from_utc = _Convertible_from_utc_time<_DestClock, _Duration>; - constexpr bool _Dest_from_sys = _Convertible_from_sys_time<_DestClock, _Duration>; - - constexpr bool _Has_utc_conversion = _Dest_from_utc && _Utc_from_src; - constexpr bool _Has_sys_conversion = _Dest_from_sys && _Sys_from_src; - static_assert(_Has_direct_conversion || !(_Has_utc_conversion && _Has_sys_conversion), - "A two-step clock time conversion is required to be unique, either through utc_clock or system_clock, but " - "not both (N4878 [time.clock.cast.fn]/2.)"); - - constexpr bool _Has_sys_utc_conversion = _Dest_from_sys && _Utc_from_src; - constexpr bool _Has_utc_sys_conversion = _Dest_from_utc && _Sys_from_src; - static_assert(_Has_direct_conversion || _Has_utc_conversion || _Has_sys_conversion - || !(_Has_utc_sys_conversion && _Has_sys_utc_conversion), - "A three-step clock time conversion is required to be unique, either utc-to-system or system-to-utc, but " - "not both (N4878 [time.clock.cast.fn]/2)."); + constexpr auto _Strat = _Clock_cast_choice<_DestClock, _SourceClock, _Duration>; - // clang-format off - if constexpr (_Has_direct_conversion) { + if constexpr (_Strat == _Clock_cast_strategy::_Direct) { return clock_time_conversion<_DestClock, _SourceClock>{}(_Time); - } else if constexpr (_Has_utc_conversion) { - return clock_time_conversion<_DestClock, utc_clock>{}( - clock_time_conversion{}(_Time)); - } else if constexpr (_Has_sys_conversion) { + } else if constexpr (_Strat == _Clock_cast_strategy::_Via_sys) { return clock_time_conversion<_DestClock, system_clock>{}( - clock_time_conversion{}(_Time)); - } else if constexpr (_Has_sys_utc_conversion) { - return clock_time_conversion<_DestClock, system_clock>{}( - clock_time_conversion{}( - clock_time_conversion{}(_Time))); - } else if constexpr (_Has_utc_sys_conversion) { + clock_time_conversion{}(_Time)); + } else if constexpr (_Strat == _Clock_cast_strategy::_Via_utc) { return clock_time_conversion<_DestClock, utc_clock>{}( - clock_time_conversion{}( - clock_time_conversion{}(_Time))); + clock_time_conversion{}(_Time)); + } else if constexpr (_Strat == _Clock_cast_strategy::_Via_utc_from_sys) { + return clock_time_conversion<_DestClock, utc_clock>{}( // + clock_time_conversion{}( + clock_time_conversion{}(_Time))); + } else if constexpr (_Strat == _Clock_cast_strategy::_Via_sys_from_utc) { + return clock_time_conversion<_DestClock, system_clock>{}( // + clock_time_conversion{}( + clock_time_conversion{}(_Time))); + } else if constexpr (_Strat == _Clock_cast_strategy::_Two_step_ambiguous) { + static_assert(_Always_false<_Duration>, + "A two-step clock time conversion is required to be unique, " + "either through utc_clock or system_clock, but not both (N4878 [time.clock.cast.fn]/2)."); + } else if constexpr (_Strat == _Clock_cast_strategy::_Three_step_ambiguous) { + static_assert(_Always_false<_Duration>, + "A three-step clock time conversion is required to be unique, " + "either utc-to-system or system-to-utc, but not both (N4878 [time.clock.cast.fn]/2)."); } else { - static_assert(_Always_false<_Duration>, "No clock time conversion exists from source clock type to " - "destination clock type (N4878 [time.clock.cast.fn]/1)."); + static_assert(_Always_false<_Duration>, "should be unreachable"); } - // clang-format on } // [time.parse] From 88141769cf481d75b0c9f1f0998693510a752a6a Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Sat, 27 Mar 2021 23:49:46 -0700 Subject: [PATCH 6/6] Say 'mandates' in static_assert messages. --- stl/inc/chrono | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/stl/inc/chrono b/stl/inc/chrono index e554cee520..e6593d479a 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -212,7 +212,8 @@ namespace chrono { using rep = typename _Duration::rep; using period = typename _Duration::period; - static_assert(_Is_duration_v<_Duration>, "duration must be an instance of std::duration"); + static_assert(_Is_duration_v<_Duration>, + "N4885 [time.point.general]/1 mandates Duration to be a specialization of chrono::duration."); constexpr time_point() = default; @@ -2131,7 +2132,7 @@ namespace chrono { class hh_mm_ss { public: static_assert(_Is_duration_v<_Duration>, - "N4885 [time.hms.overview]/2 requires Duration to be a specialization of chrono::duration."); + "N4885 [time.hms.overview]/2 mandates Duration to be a specialization of chrono::duration."); static constexpr unsigned int fractional_width = [] { auto _Num = _Duration::period::num; @@ -2910,7 +2911,7 @@ namespace chrono { class zoned_time { private: static_assert(_Is_duration_v<_Duration>, - "N4878 [time.zone.zonedtime.overview]/2 requires Duration to be a specialization of chrono::duration."); + "N4885 [time.zone.zonedtime.overview]/2 mandates Duration to be a specialization of chrono::duration."); using _Traits = zoned_traits<_TimeZonePtr>;