[libcxx-commits] [libcxx] [libcxx] Implement C++20 std::chrono::is_clock, std::chrono::is_clock_v (PR #160607)
Yuxuan Chen via libcxx-commits
libcxx-commits at lists.llvm.org
Thu Nov 6 09:59:56 PST 2025
https://github.com/yuxuanchen1997 updated https://github.com/llvm/llvm-project/pull/160607
>From 9f1068c517630cee2580c0947141ca53b6afad64 Mon Sep 17 00:00:00 2001
From: Yuxuan Chen <ych at meta.com>
Date: Wed, 24 Sep 2025 11:54:36 -0700
Subject: [PATCH] [libcxx] Implement C++20 std::chrono::is_clock,
std::chrono::is_clock_v
---
libcxx/include/CMakeLists.txt | 1 +
libcxx/include/__chrono/is_clock.h | 72 ++++++
libcxx/include/chrono | 4 +
libcxx/include/module.modulemap.in | 4 +
libcxx/modules/std/chrono.inc | 4 +-
.../trait.is.clock.compile.verify.cpp | 24 ++
.../trait.is.clock.compile.pass.cpp | 225 ++++++++++++++++++
7 files changed, 332 insertions(+), 2 deletions(-)
create mode 100644 libcxx/include/__chrono/is_clock.h
create mode 100644 libcxx/test/libcxx/time/time.traits.is.clock/trait.is.clock.compile.verify.cpp
create mode 100644 libcxx/test/std/time/time.traits.is.clock/trait.is.clock.compile.pass.cpp
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 57032ce26d4fd..46e17b584432e 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..e63b8485d06e1
--- /dev/null
+++ b/libcxx/include/__chrono/is_clock.h
@@ -0,0 +1,72 @@
+// -*- 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 <__chrono/duration.h>
+#include <__chrono/time_point.h>
+#include <__concepts/same_as.h>
+#include <__type_traits/integral_constant.h>
+#include <__type_traits/is_arithmetic.h>
+#include <__type_traits/is_class.h>
+#include <__type_traits/is_union.h>
+#include <ratio>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+#if _LIBCPP_STD_VER >= 20
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+namespace chrono {
+
+// Helper to check that _Tp::time_point has the form time_point<_, typename _Tp::duration>.
+template <class _TimePoint, class _ClockType>
+inline constexpr bool __is_valid_clock_time_point_v = false;
+
+template <class _TimePointClock, class _ClockType>
+inline constexpr bool
+ __is_valid_clock_time_point_v<time_point<_TimePointClock, typename _ClockType::duration>, _ClockType> = true;
+
+// Check if a clock satisfies the Cpp17Clock requirements as defined in [time.clock.req]
+template <class _Tp>
+_LIBCPP_NO_SPECIALIZATIONS inline constexpr bool is_clock_v = requires {
+ typename _Tp::rep;
+ requires is_arithmetic_v<typename _Tp::rep> || is_class_v<typename _Tp::rep> || is_union_v<typename _Tp::rep>;
+
+ typename _Tp::period;
+ requires __is_ratio_v<typename _Tp::period>;
+
+ typename _Tp::duration;
+ requires same_as<typename _Tp::duration, duration<typename _Tp::rep, typename _Tp::period>>;
+
+ typename _Tp::time_point;
+ requires __is_valid_clock_time_point_v<typename _Tp::time_point, _Tp>;
+
+ _Tp::is_steady;
+ requires same_as<decltype((_Tp::is_steady)), const bool&>;
+
+ _Tp::now();
+ requires same_as<decltype(_Tp::now()), typename _Tp::time_point>;
+};
+
+template <class _Tp>
+struct _LIBCPP_NO_SPECIALIZATIONS is_clock : bool_constant<is_clock_v<_Tp>> {};
+
+} // 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 <class ToDuration, class Rep, class Period>
template <class ToDuration, class Rep, class Period>
constexpr ToDuration round(const duration<Rep, Period>& d); // C++17
+template <class T> struct is_clock; // C++20
+template <class T> inline constexpr bool is_clock_v = is_clock<T>::value; // C++20
+
// duration I/O
template<class charT, class traits, class Rep, class Period> // C++20
basic_ostream<charT, traits>&
@@ -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 24a2fe761943a..f77c885da5b6a 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -973,6 +973,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/libcxx/time/time.traits.is.clock/trait.is.clock.compile.verify.cpp b/libcxx/test/libcxx/time/time.traits.is.clock/trait.is.clock.compile.verify.cpp
new file mode 100644
index 0000000000000..e9ad59aba13cb
--- /dev/null
+++ b/libcxx/test/libcxx/time/time.traits.is.clock/trait.is.clock.compile.verify.cpp
@@ -0,0 +1,24 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 <chrono>
+#include <ratio>
+
+#if !__has_warning("-Winvalid-specializations")
+// expected-no-diagnostics
+#else
+
+template <>
+struct std::chrono::is_clock<int> : std::false_type {}; // expected-error@*:* {{'is_clock' cannot be specialized}}
+
+template <>
+constexpr bool std::chrono::is_clock_v<float> = false; // expected-error@*:* {{'is_clock_v' cannot be specialized}}
+
+#endif
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..4168fa7c861ba
--- /dev/null
+++ b/libcxx/test/std/time/time.traits.is.clock/trait.is.clock.compile.pass.cpp
@@ -0,0 +1,225 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 <chrono>
+#include <ratio>
+
+#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<MissingRep>;
+ 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<MissingPeriod>;
+ 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<MissingTimePoint> now();
+};
+
+struct MissingIsSteady {
+ using rep = long;
+ using period = std::ratio<1>;
+ using duration = std::chrono::seconds;
+ using time_point = std::chrono::time_point<MissingIsSteady>;
+ 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<MissingNow>;
+ 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<ValidSteadyClock>;
+ 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<ValidSystemClock>;
+ 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<WrongIsSteadyType>;
+ 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<WrongIsSteadyNonBool>;
+ 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<WrongNowReturnType>;
+ 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<WrongPeriodType>;
+ 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<long, ratio<1>>
+ using time_point = std::chrono::time_point<WrongDurationType>;
+ 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<long, std::ratio<1>>;
+ 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<long, std::ratio<1>>;
+ using time_point = std::chrono::time_point<ValidSystemClock>; // 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<int, std::milli>;
+ using time_point = std::chrono::time_point<ValidSystemClock, duration>; // 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<std::chrono::system_clock>::value);
+static_assert(std::chrono::is_clock_v<std::chrono::system_clock>);
+
+// Test standard clock types
+static_assert(std::chrono::is_clock_v<std::chrono::system_clock>);
+static_assert(std::chrono::is_clock_v<std::chrono::high_resolution_clock>);
+
+// Test non-clock types
+static_assert(!std::chrono::is_clock_v<EmptyStruct>);
+static_assert(!std::chrono::is_clock_v<int>);
+static_assert(!std::chrono::is_clock_v<void>);
+static_assert(!std::chrono::is_clock_v<std::chrono::system_clock::time_point>);
+static_assert(!std::chrono::is_clock_v<std::chrono::seconds>);
+static_assert(!std::chrono::is_clock_v<std::chrono::milliseconds>);
+
+// Test structs missing required members
+static_assert(!std::chrono::is_clock_v<MissingRep>);
+static_assert(!std::chrono::is_clock_v<MissingPeriod>);
+static_assert(!std::chrono::is_clock_v<MissingDuration>);
+static_assert(!std::chrono::is_clock_v<MissingTimePoint>);
+static_assert(!std::chrono::is_clock_v<MissingIsSteady>);
+static_assert(!std::chrono::is_clock_v<MissingNow>);
+
+// Test valid custom clocks
+static_assert(std::chrono::is_clock_v<ValidSteadyClock>);
+static_assert(std::chrono::is_clock_v<ValidSystemClock>);
+static_assert(std::chrono::is_clock_v<ValidClockWithDurationMatch>);
+
+// cv-qualified and reference types
+static_assert(std::chrono::is_clock_v<const std::chrono::system_clock>);
+static_assert(std::chrono::is_clock_v<volatile std::chrono::system_clock>);
+static_assert(std::chrono::is_clock_v<const volatile std::chrono::system_clock>);
+static_assert(!std::chrono::is_clock_v<std::chrono::system_clock&>);
+static_assert(!std::chrono::is_clock_v<std::chrono::system_clock&&>);
+static_assert(!std::chrono::is_clock_v<const std::chrono::system_clock&>);
+
+// array and pointer types
+static_assert(!std::chrono::is_clock_v<std::chrono::system_clock[]>);
+static_assert(!std::chrono::is_clock_v<std::chrono::system_clock[10]>);
+static_assert(!std::chrono::is_clock_v<std::chrono::system_clock*>);
+static_assert(!std::chrono::is_clock_v<std::chrono::system_clock* const>);
+
+// 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<WrongIsSteadyType>); // is_steady not const bool
+LIBCPP_STATIC_ASSERT(!std::chrono::is_clock_v<WrongIsSteadyNonBool>); // is_steady not bool type
+
+// Test clocks with invalid now() return type
+LIBCPP_STATIC_ASSERT(!std::chrono::is_clock_v<WrongNowReturnType>); // now() doesn't return time_point
+
+// Test clocks with invalid period type
+LIBCPP_STATIC_ASSERT(!std::chrono::is_clock_v<WrongPeriodType>); // period is not a ratio
+
+// Test clocks with wrong duration type
+LIBCPP_STATIC_ASSERT(!std::chrono::is_clock_v<WrongDurationType>); // duration doesn't match duration<rep, period>
+
+// Test clocks with wrong time_point type
+LIBCPP_STATIC_ASSERT(!std::chrono::is_clock_v<WrongTimePointType>); // time_point is not a time_point
+LIBCPP_STATIC_ASSERT(!std::chrono::is_clock_v<WrongTimePointClock>); // time_point has wrong clock and wrong duration
More information about the libcxx-commits
mailing list