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

P1614R2 Spaceship: Clause 27: Time (partial, pre-C++20 types) #1602

Merged
merged 4 commits into from
Feb 19, 2021
Merged
Show file tree
Hide file tree
Changes from 2 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
29 changes: 29 additions & 0 deletions stl/inc/chrono
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@

#if _HAS_CXX20
#include <compare>
#ifdef __cpp_lib_concepts
#include <concepts>
#endif // defined(__cpp_lib_concepts)
#endif // _HAS_CXX20

#pragma pack(push, _CRT_PACKING)
Expand Down Expand Up @@ -370,12 +373,14 @@ namespace chrono {
return _CT(_Left).count() == _CT(_Right).count();
}

#if !_HAS_CXX20
template <class _Rep1, class _Period1, class _Rep2, class _Period2>
_NODISCARD constexpr bool
operator!=(const duration<_Rep1, _Period1>& _Left, const duration<_Rep2, _Period2>& _Right) noexcept(
is_arithmetic_v<_Rep1>&& is_arithmetic_v<_Rep2>) /* strengthened */ {
return !(_Left == _Right);
}
#endif // !_HAS_CXX20

template <class _Rep1, class _Period1, class _Rep2, class _Period2>
_NODISCARD constexpr bool
Expand Down Expand Up @@ -406,6 +411,19 @@ namespace chrono {
return !(_Left < _Right);
}

#ifdef __cpp_lib_concepts
// clang-format off
template <class _Rep1, class _Period1, class _Rep2, class _Period2>
requires three_way_comparable<typename common_type_t<duration<_Rep1, _Period1>, duration<_Rep2, _Period2>>::rep>
_NODISCARD constexpr auto
operator<=>(const duration<_Rep1, _Period1>& _Left, const duration<_Rep2, _Period2>& _Right) noexcept(
is_arithmetic_v<_Rep1>&& is_arithmetic_v<_Rep2>) /* strengthened */ {
// clang-format on
using _CT = common_type_t<duration<_Rep1, _Period1>, duration<_Rep2, _Period2>>;
return _CT(_Left).count() <=> _CT(_Right).count();
}
#endif // defined(__cpp_lib_concepts)

// FUNCTION TEMPLATE duration_cast
template <class _To, class _Rep, class _Period, enable_if_t<_Is_duration_v<_To>, int> _Enabled>
_NODISCARD constexpr _To duration_cast(const duration<_Rep, _Period>& _Dur) noexcept(
Expand Down Expand Up @@ -550,12 +568,14 @@ namespace chrono {
return _Left.time_since_epoch() == _Right.time_since_epoch();
}

#if !_HAS_CXX20
template <class _Clock, class _Duration1, class _Duration2>
_NODISCARD constexpr bool
operator!=(const time_point<_Clock, _Duration1>& _Left, const time_point<_Clock, _Duration2>& _Right) noexcept(
is_arithmetic_v<typename _Duration1::rep>&& is_arithmetic_v<typename _Duration2::rep>) /* strengthened */ {
return !(_Left == _Right);
}
#endif // !_HAS_CXX20

template <class _Clock, class _Duration1, class _Duration2>
_NODISCARD constexpr bool
Expand Down Expand Up @@ -585,6 +605,15 @@ namespace chrono {
return !(_Left < _Right);
}

#ifdef __cpp_lib_concepts
template <class _Clock, class _Duration1, three_way_comparable_with<_Duration1> _Duration2>
_NODISCARD constexpr auto
operator<=>(const time_point<_Clock, _Duration1>& _Left, const time_point<_Clock, _Duration2>& _Right) noexcept(
is_arithmetic_v<typename _Duration1::rep>&& is_arithmetic_v<typename _Duration2::rep>) /* strengthened */ {
return _Left.time_since_epoch() <=> _Right.time_since_epoch();
}
#endif // defined(__cpp_lib_concepts)

// FUNCTION TEMPLATE time_point_cast
template <class _To, class _Clock, class _Duration, enable_if_t<_Is_duration_v<_To>, int> = 0>
_NODISCARD constexpr time_point<_Clock, _To> time_point_cast(const time_point<_Clock, _Duration>& _Time) noexcept(
Expand Down
7 changes: 7 additions & 0 deletions tests/std/tests/P1614R2_spaceship/env.lst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Copyright (c) Microsoft Corporation.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

StephanTLavavej marked this conversation as resolved.
Show resolved Hide resolved
RUNALL_INCLUDE ..\concepts_matrix.lst
RUNALL_CROSSLIST
PM_CL="/D_STL_OPTIMIZE_SYSTEM_ERROR_OPERATORS=0"
PM_CL="/D_STL_OPTIMIZE_SYSTEM_ERROR_OPERATORS=1"
262 changes: 262 additions & 0 deletions tests/std/tests/P1614R2_spaceship/test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,262 @@
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

// Covers:
// * spaceship for containers

#include <array>
#include <cassert>
#include <chrono>
#include <compare>
#include <concepts>
#include <deque>
#include <filesystem>
#include <forward_list>
#include <functional>
#include <iostream>
#include <limits>
#include <list>
#include <map>
#include <queue>
#include <ranges>
#include <ratio>
#include <regex>
#include <set>
#include <stack>
#include <string>
#include <system_error>
#include <thread>
#include <type_traits>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>

template <class T>
using SpaceshipType = decltype(std::declval<T>() <=> std::declval<T>());

template <class T, class U>
concept HasSpaceshipWith = requires {
std::declval<T>() <=> std::declval<U>();
};

using PartiallyOrdered = double;

struct WeaklyOrdered {
[[nodiscard]] constexpr bool operator==(const WeaklyOrdered&) const {
return true;
}

[[nodiscard]] constexpr std::weak_ordering operator<=>(const WeaklyOrdered&) const {
return std::weak_ordering::equivalent;
}
};

using StronglyOrdered = int;

// Activates synth-three-way in N4861 16.4.2.1 [expos.only.func]/2.
struct SynthOrdered {
int val;

constexpr SynthOrdered(const int x) : val{x} {}

[[nodiscard]] constexpr bool operator==(const SynthOrdered& other) const {
return val == other.val;
}

[[nodiscard]] constexpr bool operator<(const SynthOrdered& other) const {
return val < other.val;
}
};

struct OrderedChar {
OrderedChar() = default;
OrderedChar(const char other) : c(other) {}

OrderedChar& operator=(const char& other) {
c = other;
return *this;
}

auto operator<=>(const OrderedChar&) const = default;

operator char() const {
return c;
}

char c;
};

struct WeaklyOrderedChar : OrderedChar {};
struct WeaklyOrderedByOmissionChar : OrderedChar {};
struct PartiallyOrderedChar : OrderedChar {};

namespace std {
template <>
struct char_traits<OrderedChar> : char_traits<char> {
using char_type = OrderedChar;

static int compare(const char_type* first1, const char_type* first2, size_t count) {
for (; 0 < count; --count, ++first1, ++first2) {
if (*first1 != *first2) {
return *first1 < *first2 ? -1 : +1;
}
}

return 0;
}

static bool eq(const char_type l, const char_type r) {
return l.c == r.c;
}
};

template <>
struct char_traits<WeaklyOrderedChar> : char_traits<OrderedChar> {
using char_type = WeaklyOrderedChar;
using comparison_category = weak_ordering;
};

template <>
struct char_traits<WeaklyOrderedByOmissionChar> : char_traits<OrderedChar> {
using char_type = WeaklyOrderedByOmissionChar;

private:
using comparison_category = strong_ordering;
};

template <>
struct char_traits<PartiallyOrderedChar> : char_traits<OrderedChar> {
using char_type = PartiallyOrderedChar;
using comparison_category = partial_ordering;
};
} // namespace std

struct dummy_diagnostic : std::error_category {
const char* name() const noexcept override {
return "dummy";
}
std::string message(int) const override {
return "";
}
};

template <class ReturnType, class SmallType, class EqualType, class LargeType>
void spaceship_test(const SmallType& smaller, const EqualType& smaller_equal, const LargeType& larger) {
assert(smaller == smaller_equal);
assert(smaller_equal == smaller);
assert(smaller != larger);
assert(larger != smaller);
assert(smaller < larger);
assert(!(larger < smaller));
assert(larger > smaller);
assert(!(smaller > larger));
assert(smaller <= larger);
assert(!(larger <= smaller));
assert(larger >= smaller);
assert(!(smaller >= larger));
assert((smaller <=> larger) < 0);
assert((larger <=> smaller) > 0);
assert((smaller <=> smaller_equal) == 0);

static_assert(std::is_same_v<decltype(smaller <=> larger), ReturnType>);
}

template <class T>
inline constexpr bool is_pair = false;
template <class A, class B>
inline constexpr bool is_pair<std::pair<A, B>> = true; // TRANSITION, std::pair spaceship not yet implemented

template <class Container>
void ordered_containers_test(const Container& smaller, const Container& smaller_equal, const Container& larger) {
using Elem = typename Container::value_type;
if constexpr (is_pair<Elem> // TRANSITION, std::pair spaceship not yet implemented
|| std::is_same_v<Elem, SynthOrdered>) {
spaceship_test<std::weak_ordering>(smaller, smaller_equal, larger);
} else {
spaceship_test<std::strong_ordering>(smaller, smaller_equal, larger);
}
}

template <class Container>
void unordered_containers_test(
const Container& something, const Container& something_equal, const Container& different) {
assert(something == something_equal);
assert(something != different);
}

template <class ErrorType>
void diagnostics_test() {
dummy_diagnostic c_mem[2];
{
ErrorType e_smaller(0, c_mem[0]);
ErrorType e_equal(0, c_mem[0]);
ErrorType e_larger(1, c_mem[1]);

spaceship_test<std::strong_ordering>(e_smaller, e_equal, e_larger);
}
{
ErrorType e_smaller(0, c_mem[0]);
ErrorType e_larger(0, c_mem[1]);

assert(e_smaller < e_larger);
assert(!(e_larger < e_smaller));
assert((e_smaller <=> e_larger) < 0);
assert((e_larger <=> e_smaller) > 0);
}
{
ErrorType e_smaller(0, c_mem[0]);
ErrorType e_larger(1, c_mem[0]);

assert(e_smaller < e_larger);
assert(!(e_larger < e_smaller));
assert((e_smaller <=> e_larger) < 0);
assert((e_larger <=> e_smaller) > 0);
}
}

void ordering_test_cases() {
{ // chrono::duration
using std::chrono::hours;
using std::chrono::minutes;
using std::chrono::seconds;

spaceship_test<std::strong_ordering>(seconds{1}, seconds{1}, seconds{2});
spaceship_test<std::strong_ordering>(seconds{3600}, hours{1}, minutes{61});

using double_seconds = std::chrono::duration<double>;
using float_milliseconds = std::chrono::duration<float, std::milli>;
using ntsc_fields = std::chrono::duration<long long, std::ratio<1001, 60000>>;

spaceship_test<std::partial_ordering>(double_seconds{1}, float_milliseconds{1000}, ntsc_fields{60});

constexpr double_seconds nan_s{std::numeric_limits<double>::quiet_NaN()};
#ifdef __clang__ // TRANSITION, DevCom-445462
static_assert(nan_s <=> nan_s == std::partial_ordering::unordered);
#endif // defined(__clang__)
assert(nan_s <=> nan_s == std::partial_ordering::unordered);
}
{ // chrono::time_point
using std::chrono::milliseconds;
using double_seconds = std::chrono::duration<double>;
using sys_tp = std::chrono::system_clock::time_point;
using sys_ms = std::chrono::time_point<std::chrono::system_clock, milliseconds>;
using sys_double_s = std::chrono::time_point<std::chrono::system_clock, double_seconds>;

spaceship_test<std::strong_ordering>(sys_tp{}, sys_ms{}, sys_ms{milliseconds{1}});
spaceship_test<std::partial_ordering>(sys_tp{}, sys_double_s{}, sys_double_s{double_seconds{1}});

constexpr sys_double_s nan_tp{double_seconds{std::numeric_limits<double>::quiet_NaN()}};
#ifdef __clang__ // TRANSITION, DevCom-445462
static_assert(nan_tp <=> nan_tp == std::partial_ordering::unordered);
#endif // defined(__clang__)
assert(nan_tp <=> nan_tp == std::partial_ordering::unordered);

using steady_tp = std::chrono::steady_clock::time_point;
static_assert(!HasSpaceshipWith<sys_tp, steady_tp>);
}
}

int main() {
ordering_test_cases();
}