Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

<chrono>: Avoid unnecessary use of concepts #1787

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
221 changes: 134 additions & 87 deletions stl/inc/chrono
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -2128,8 +2129,11 @@ namespace chrono {
}

template <class _Duration>
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 mandates 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;
Expand Down Expand Up @@ -2907,19 +2911,19 @@ 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>;

public:
using duration = common_type_t<_Duration, seconds>;

template <class _Traits2 = _Traits, class = decltype(_Traits2::default_zone())>
template <class _Traits2 = _Traits, class = void_t<decltype(_Traits2::default_zone())>>
zoned_time() : _Zone{_Traits::default_zone()} {}
zoned_time(const zoned_time&) = default;
zoned_time& operator=(const zoned_time&) = default;

template <class _Traits2 = _Traits, class = decltype(_Traits2::default_zone())>
template <class _Traits2 = _Traits, class = void_t<decltype(_Traits2::default_zone())>>
zoned_time(const sys_time<_Duration>& _Sys) : _Zone{_Traits::default_zone()}, _Tp{_Sys} {}

explicit zoned_time(_TimeZonePtr _Tz) noexcept /* strengthened */ : _Zone{_STD move(_Tz)} {}
Expand Down Expand Up @@ -3405,131 +3409,174 @@ namespace chrono {

// [time.clock.cast.sys]

// TRANSITION, GH-395 workaround, is_same_v -> same_as
template <class _Ty, class _Clock>
concept _Is_time_point = requires {
typename _Ty::duration;
requires is_same_v<time_point<_Clock, typename _Ty::duration>, _Ty>;
};
template <class _TimePoint, class _Clock>
inline constexpr bool _Is_time_point_for_clock = false;

template <class _Clock, class _Duration>
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<system_clock>;
};

template <class _Clock, class _Duration>
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<time_point<_Clock, _Duration>, _Clock> = true;

template <class _SourceClock>
struct clock_time_conversion<system_clock, _SourceClock> {
template <class _Duration>
template <class _Duration, class _SourceClock2 = _SourceClock,
class =
void_t<decltype(_SourceClock2::to_sys(_STD declval<const time_point<_SourceClock2, _Duration>&>()))>>
_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<decltype(_SourceClock::to_sys(_Time)), system_clock>,
"N4885 [time.clock.cast.sys]/2: Mandates: SourceClock::to_sys(t) returns a sys_time<Duration>");
return _SourceClock::to_sys(_Time);
}
};

template <class _DestClock>
struct clock_time_conversion<_DestClock, system_clock> {
template <class _Duration>
template <class _Duration, class _DestClock2 = _DestClock,
class = void_t<decltype(_DestClock2::from_sys(_STD declval<const sys_time<_Duration>&>()))>>
_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<decltype(_DestClock::from_sys(_Time)), _DestClock>,
"N4885 [time.clock.cast.sys]/5: Mandates: DestClock::from_sys(t) returns a "
"time_point<DestClock, Duration>");
return _DestClock::from_sys(_Time);
}
};

// [time.clock.cast.utc]

template <class _Clock, class _Duration>
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<utc_clock>;
};

template <class _Clock, class _Duration>
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 <class _SourceClock>
struct clock_time_conversion<utc_clock, _SourceClock> {
template <class _Duration>
template <class _Duration, class _SourceClock2 = _SourceClock,
class =
void_t<decltype(_SourceClock2::to_utc(_STD declval<const time_point<_SourceClock2, _Duration>&>()))>>
_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<decltype(_SourceClock::to_utc(_Time)), utc_clock>,
"N4885 [time.clock.cast.utc]/2: Mandates: SourceClock::to_utc(t) returns a utc_time<Duration>");
return _SourceClock::to_utc(_Time);
}
};

template <class _DestClock>
struct clock_time_conversion<_DestClock, utc_clock> {
template <class _Duration>
template <class _Duration, class _DestClock2 = _DestClock,
class = void_t<decltype(_DestClock2::from_utc(_STD declval<const utc_time<_Duration>&>()))>>
_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<decltype(_DestClock::from_utc(_Time)), _DestClock>,
"N4885 [time.clock.cast.utc]/5: Mandates: DestClock::from_utc(t) returns a "
"time_point<DestClock, Duration>");
return _DestClock::from_utc(_Time);
}
};

// [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 <class _Conv1, class _Conv2, class _Tp, class = void>
inline constexpr bool _Has_two_step_conversion = false;

template <class _Conv1, class _Conv2, class _Tp>
inline constexpr bool
_Has_two_step_conversion<_Conv1, _Conv2, _Tp, void_t<decltype(_Conv1{}(_Conv2{}(_STD declval<_Tp>())))>> = true;

template <class _Conv1, class _Conv2, class _Conv3, class _Tp, class = void>
inline constexpr bool _Has_three_step_conversion = false;

template <class _Conv1, class _Conv2, class _Conv3, class _Tp>
inline constexpr bool _Has_three_step_conversion<_Conv1, _Conv2, _Conv3, _Tp,
void_t<decltype(_Conv1{}(_Conv2{}(_Conv3{}(_STD declval<_Tp>()))))>> = true;

template <class _DestClock, class _SourceClock, class _Duration>
_NODISCARD _CONSTEVAL _Clock_cast_strategy _Choose_clock_cast() noexcept {
using _Tp = const time_point<_SourceClock, _Duration>&;

if constexpr (is_invocable_v<clock_time_conversion<_DestClock, _SourceClock>, _Tp>) {
return _Clock_cast_strategy::_Direct;
} else {
constexpr bool _Has_sys = _Has_two_step_conversion< //
clock_time_conversion<_DestClock, system_clock>, //
clock_time_conversion<system_clock, _SourceClock>, _Tp>;

constexpr bool _Has_utc = _Has_two_step_conversion< //
clock_time_conversion<_DestClock, utc_clock>, //
clock_time_conversion<utc_clock, _SourceClock>, _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<utc_clock, system_clock>, //
clock_time_conversion<system_clock, _SourceClock>, _Tp>;

constexpr bool _Has_sys_from_utc = _Has_three_step_conversion< //
clock_time_conversion<_DestClock, system_clock>, //
clock_time_conversion<system_clock, utc_clock>, //
clock_time_conversion<utc_clock, _SourceClock>, _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 <class _DestClock, class _SourceClock, class _Duration>
inline constexpr auto _Clock_cast_choice = _Choose_clock_cast<_DestClock, _SourceClock, _Duration>();

// FUNCTION TEMPLATE clock_cast
template <class _DestClock, class _SourceClock, class _Duration,
enable_if_t<_Clock_cast_choice<_DestClock, _SourceClock, _Duration> != _Clock_cast_strategy::_None, int> = 0>
_NODISCARD auto clock_cast(const time_point<_SourceClock, _Duration>& _Time) {
constexpr bool _Has_direct_conversion =
is_invocable_v<clock_time_conversion<_DestClock, _SourceClock>, 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<utc_clock, _SourceClock>{}(_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<system_clock, _SourceClock>{}(_Time));
} else if constexpr (_Has_sys_utc_conversion) {
return clock_time_conversion<_DestClock, system_clock>{}(
clock_time_conversion<system_clock, utc_clock>{}(
clock_time_conversion<utc_clock, _SourceClock>{}(_Time)));
} else if constexpr (_Has_utc_sys_conversion) {
clock_time_conversion<system_clock, _SourceClock>{}(_Time));
} else if constexpr (_Strat == _Clock_cast_strategy::_Via_utc) {
return clock_time_conversion<_DestClock, utc_clock>{}(
clock_time_conversion<utc_clock, system_clock>{}(
clock_time_conversion<system_clock, _SourceClock>{}(_Time)));
clock_time_conversion<utc_clock, _SourceClock>{}(_Time));
} else if constexpr (_Strat == _Clock_cast_strategy::_Via_utc_from_sys) {
return clock_time_conversion<_DestClock, utc_clock>{}( //
clock_time_conversion<utc_clock, system_clock>{}(
clock_time_conversion<system_clock, _SourceClock>{}(_Time)));
} else if constexpr (_Strat == _Clock_cast_strategy::_Via_sys_from_utc) {
return clock_time_conversion<_DestClock, system_clock>{}( //
clock_time_conversion<system_clock, utc_clock>{}(
clock_time_conversion<utc_clock, _SourceClock>{}(_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]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down