diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt index e050362abb658..cb52105ab0ebd 100644 --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -262,6 +262,7 @@ set(files __chrono/gps_clock.h __chrono/hh_mm_ss.h __chrono/high_resolution_clock.h + __chrono/is_clock.h __chrono/leap_second.h __chrono/literals.h __chrono/local_info.h diff --git a/libcxx/include/__chrono/is_clock.h b/libcxx/include/__chrono/is_clock.h new file mode 100644 index 0000000000000..7afc7aa861d05 --- /dev/null +++ b/libcxx/include/__chrono/is_clock.h @@ -0,0 +1,73 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP___CHRONO_IS_CLOCK_H +#define _LIBCPP___CHRONO_IS_CLOCK_H + +#include <__config> +#include <__type_traits/integral_constant.h> +#include <__type_traits/is_arithmetic.h> +#include <__type_traits/is_same.h> +#include + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +#if _LIBCPP_STD_VER >= 20 + +_LIBCPP_BEGIN_NAMESPACE_STD + +namespace chrono { + +template +class duration; + +template +class time_point; + +// Helper to check that _Tp::time_point has the form time_point<_, typename _Tp::duration>. +template +constexpr bool __is_valid_clock_time_point_v = false; + +template +constexpr bool __is_valid_clock_time_point_v, _ClockType> = + true; + +// Check if a clock satisfies the Cpp17Clock requirements as defined in [time.clock.req] +template +_LIBCPP_NO_SPECIALIZATIONS inline constexpr bool is_clock_v = requires { + typename _Tp::rep; + requires is_arithmetic_v; + + typename _Tp::period; + requires __is_ratio_v; + + typename _Tp::duration; + requires _IsSame>::value; + + typename _Tp::time_point; + requires __is_valid_clock_time_point_v; + + _Tp::is_steady; + requires _IsSame::value; + + _Tp::now(); + requires _IsSame::value; +}; + +template +struct _LIBCPP_NO_SPECIALIZATIONS is_clock : bool_constant> {}; + +} // namespace chrono + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP_STD_VER +#endif // _LIBCPP___CHRONO_IS_CLOCK_H diff --git a/libcxx/include/chrono b/libcxx/include/chrono index 82e99a31bcc9f..aa4fc6218f962 100644 --- a/libcxx/include/chrono +++ b/libcxx/include/chrono @@ -218,6 +218,9 @@ template template constexpr ToDuration round(const duration& d); // C++17 +template struct is_clock; // C++20 +template inline constexpr bool is_clock_v = is_clock::value; // C++20 + // duration I/O template // C++20 basic_ostream& @@ -1057,6 +1060,7 @@ constexpr chrono::year operator ""y(unsigned lo # include <__chrono/day.h> # include <__chrono/exception.h> # include <__chrono/hh_mm_ss.h> +# include <__chrono/is_clock.h> # include <__chrono/literals.h> # include <__chrono/local_info.h> # include <__chrono/month.h> diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in index dc1933324ef79..e94384cbe338b 100644 --- a/libcxx/include/module.modulemap.in +++ b/libcxx/include/module.modulemap.in @@ -970,6 +970,10 @@ module std [system] { header "__chrono/high_resolution_clock.h" export * } + module is_clock { + header "__chrono/is_clock.h" + export std_core.type_traits.integral_constant + } module leap_second { header "__chrono/leap_second.h" } diff --git a/libcxx/modules/std/chrono.inc b/libcxx/modules/std/chrono.inc index 66eccd8d290ad..db405d482bf9e 100644 --- a/libcxx/modules/std/chrono.inc +++ b/libcxx/modules/std/chrono.inc @@ -25,8 +25,8 @@ export namespace std { using std::chrono::duration_values; - // using std::chrono::is_clock; - // using std::chrono::is_clock_v; + using std::chrono::is_clock; + using std::chrono::is_clock_v; // [time.duration.nonmember], duration arithmetic using std::chrono::operator+; diff --git a/libcxx/test/std/time/time.traits.is.clock/trait.is.clock.compile.pass.cpp b/libcxx/test/std/time/time.traits.is.clock/trait.is.clock.compile.pass.cpp new file mode 100644 index 0000000000000..76c3249626fcb --- /dev/null +++ b/libcxx/test/std/time/time.traits.is.clock/trait.is.clock.compile.pass.cpp @@ -0,0 +1,244 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++20 + +#include +#include + +#include "test_macros.h" + +struct EmptyStruct {}; + +// Test structs missing required members +struct MissingRep { + using period = std::ratio<1>; + using duration = std::chrono::seconds; + using time_point = std::chrono::time_point; + static constexpr bool is_steady = false; + static time_point now(); +}; + +struct MissingPeriod { + using rep = long; + using duration = std::chrono::seconds; + using time_point = std::chrono::time_point; + static constexpr bool is_steady = false; + static time_point now(); +}; + +struct MissingDuration { + using rep = long; + using time_point = long; + static constexpr bool is_steady = false; + static time_point now(); +}; + +struct MissingTimePoint { + using rep = long; + using period = std::ratio<1>; + using duration = std::chrono::seconds; + static constexpr bool is_steady = false; + static std::chrono::time_point now(); +}; + +struct MissingIsSteady { + using rep = long; + using period = std::ratio<1>; + using duration = std::chrono::seconds; + using time_point = std::chrono::time_point; + static time_point now(); +}; + +struct MissingNow { + using rep = long; + using period = std::ratio<1>; + using duration = std::chrono::seconds; + using time_point = std::chrono::time_point; + static constexpr bool is_steady = false; +}; + +// Valid clock types +struct ValidSteadyClock { + using rep = long long; + using period = std::nano; + using duration = std::chrono::nanoseconds; + using time_point = std::chrono::time_point; + static constexpr bool is_steady = true; + static time_point now(); +}; + +struct ValidSystemClock { + using rep = long long; + using period = std::micro; + using duration = std::chrono::microseconds; + using time_point = std::chrono::time_point; + static constexpr bool is_steady = false; + static time_point now(); +}; + +// Test clocks with invalid is_steady type +struct WrongIsSteadyType { + using rep = long; + using period = std::ratio<1>; + using duration = std::chrono::seconds; + using time_point = std::chrono::time_point; + static bool is_steady; // Not const bool + static time_point now(); +}; + +struct WrongIsSteadyNonBool { + using rep = long; + using period = std::ratio<1>; + using duration = std::chrono::seconds; + using time_point = std::chrono::time_point; + static constexpr int is_steady = 1; // Not bool + static time_point now(); +}; + +// Test clocks with invalid now() return type +struct WrongNowReturnType { + using rep = long; + using period = std::ratio<1>; + using duration = std::chrono::seconds; + using time_point = std::chrono::time_point; + static constexpr bool is_steady = false; + static int now(); // Wrong return type +}; + +// Test clocks with invalid period type +struct WrongPeriodType { + using rep = long; + using period = int; // Not a ratio + using duration = std::chrono::seconds; + using time_point = std::chrono::time_point; + static constexpr bool is_steady = false; + static time_point now(); +}; + +// Test clocks with invalid rep type (neither arithmetic nor numeric_limits specialized) +struct InvalidRepType { + using rep = EmptyStruct; // Not arithmetic, no numeric_limits specialization + using period = std::ratio<1>; + using duration = std::chrono::duration; + using time_point = std::chrono::time_point; + static constexpr bool is_steady = false; + static time_point now(); +}; + +// Test clocks with wrong duration type +struct WrongDurationType { + using rep = long; + using period = std::ratio<1>; + using duration = std::chrono::milliseconds; // Should be duration> + using time_point = std::chrono::time_point; + static constexpr bool is_steady = false; + static time_point now(); +}; + +// Test clocks with wrong time_point type +struct WrongTimePointType { + using rep = long; + using period = std::ratio<1>; + using duration = std::chrono::duration>; + using time_point = int; // Not a time_point + static constexpr bool is_steady = false; + static time_point now(); +}; + +struct WrongTimePointClock { + using rep = long; + using period = std::ratio<1>; + using duration = std::chrono::duration>; + using time_point = std::chrono::time_point; // Wrong clock type + static constexpr bool is_steady = false; + static time_point now(); +}; + +// Valid clock with time_point that has matching duration instead of matching clock +struct ValidClockWithDurationMatch { + using rep = int; + using period = std::milli; + using duration = std::chrono::duration; + using time_point = std::chrono::time_point; // Valid: matches duration + static constexpr bool is_steady = false; + static time_point now(); +}; + +// Test both is_clock and is_clock_v +static_assert(std::chrono::is_clock::value); +static_assert(std::chrono::is_clock_v); + +// Test standard clock types +static_assert(std::chrono::is_clock_v); +#if _LIBCPP_HAS_MONOTONIC_CLOCK +static_assert(std::chrono::is_clock_v); +#endif +static_assert(std::chrono::is_clock_v); + +// Test non-clock types +static_assert(!std::chrono::is_clock_v); +static_assert(!std::chrono::is_clock_v); +static_assert(!std::chrono::is_clock_v); +static_assert(!std::chrono::is_clock_v); +#if _LIBCPP_HAS_MONOTONIC_CLOCK +static_assert(!std::chrono::is_clock_v); +#endif +static_assert(!std::chrono::is_clock_v); +static_assert(!std::chrono::is_clock_v); + +// Test structs missing required members +static_assert(!std::chrono::is_clock_v); +static_assert(!std::chrono::is_clock_v); +static_assert(!std::chrono::is_clock_v); +static_assert(!std::chrono::is_clock_v); +static_assert(!std::chrono::is_clock_v); +static_assert(!std::chrono::is_clock_v); + +// Test valid custom clocks +static_assert(std::chrono::is_clock_v); +static_assert(std::chrono::is_clock_v); +static_assert(std::chrono::is_clock_v); + +// cv-qualified and reference types +static_assert(std::chrono::is_clock_v); +static_assert(std::chrono::is_clock_v); +static_assert(std::chrono::is_clock_v); +static_assert(!std::chrono::is_clock_v); +static_assert(!std::chrono::is_clock_v); +static_assert(!std::chrono::is_clock_v); + +// array and pointer types +static_assert(!std::chrono::is_clock_v); +static_assert(!std::chrono::is_clock_v); +static_assert(!std::chrono::is_clock_v); +static_assert(!std::chrono::is_clock_v); + +// The Standard defined a minimum set of checks and allowed implementation to perform stricter checks. The following +// static asserts are implementation specific and a conforming standard library implementation doesn't have to produce +// the same outcome. + +// Test clocks with invalid is_steady type +LIBCPP_STATIC_ASSERT(!std::chrono::is_clock_v); // is_steady not const bool +LIBCPP_STATIC_ASSERT(!std::chrono::is_clock_v); // is_steady not bool type + +// Test clocks with invalid now() return type +LIBCPP_STATIC_ASSERT(!std::chrono::is_clock_v); // now() doesn't return time_point + +// Test clocks with invalid period type +LIBCPP_STATIC_ASSERT(!std::chrono::is_clock_v); // period is not a ratio + +// Test clocks with invalid rep type +LIBCPP_STATIC_ASSERT(!std::chrono::is_clock_v); // rep is not arithmetic and no numeric_limits + +// Test clocks with wrong duration type +LIBCPP_STATIC_ASSERT(!std::chrono::is_clock_v); // duration doesn't match duration + +// Test clocks with wrong time_point type +LIBCPP_STATIC_ASSERT(!std::chrono::is_clock_v); // time_point is not a time_point +LIBCPP_STATIC_ASSERT(!std::chrono::is_clock_v); // time_point has wrong clock and wrong duration diff --git a/libcxx/test/std/time/time.traits.is.clock/trait.is.clock.compile.verify.cpp b/libcxx/test/std/time/time.traits.is.clock/trait.is.clock.compile.verify.cpp new file mode 100644 index 0000000000000..963ba34667416 --- /dev/null +++ b/libcxx/test/std/time/time.traits.is.clock/trait.is.clock.compile.verify.cpp @@ -0,0 +1,28 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++20 + +#include +#include + +#if !__has_warning("-Winvalid-specializations") +// expected-no-diagnostics +#else + +namespace std::chrono { +// try adding specializations to is_clock +template <> +struct is_clock : std::false_type {}; // expected-error@*:* {{'is_clock' cannot be specialized}} + +template <> +constexpr bool is_clock_v = false; // expected-error@*:* {{'is_clock_v' cannot be specialized}} + +} // namespace std::chrono + +#endif