[libcxx-commits] [libcxx] 286fcce - [libc++][chrono] Implement LWG 4274: Allow chrono::hh_mm_ss to be constructed from unsigned durations (#209686)
via libcxx-commits
libcxx-commits at lists.llvm.org
Sun Jul 26 09:09:21 PDT 2026
Author: Emmett
Date: 2026-07-27T00:09:16+08:00
New Revision: 286fcce7e75a4566a813c25f4ec5824533226265
URL: https://github.com/llvm/llvm-project/commit/286fcce7e75a4566a813c25f4ec5824533226265
DIFF: https://github.com/llvm/llvm-project/commit/286fcce7e75a4566a813c25f4ec5824533226265.diff
LOG: [libc++][chrono] Implement LWG 4274: Allow chrono::hh_mm_ss to be constructed from unsigned durations (#209686)
**_Implementation details_**:
- Use `is_unsigned_v` instead of `numeric_limits<Rep>::is_signed`, which
is used by `chrono::abs`. `numeric_limits<Rep>::is_signed` may return
false when a custom `Rep` does not specialize `numeric_limits`. Relying
on it to detect the unsigned case could therefore be unreliable for such
a type. `is_unsigned_v` instead gives a compile-time path for built-in
unsigned types, while the sign check keeps signed types correct.
- The constructor calls `__abs_d` four times because each member is
initialized separately. This should not affect runtime performance
because the compiler should inline `__abs_d` and eliminate the repeated
calculations. We could use another helper function or helper class to
explicitly compute the absolute duration only once. However, this would
add extra glue code, and the added complexity is not worthwhile I think.
---------
Co-authored-by: A. Jiang <de34 at live.cn>
Added:
Modified:
libcxx/docs/Status/Cxx26Issues.csv
libcxx/include/__chrono/hh_mm_ss.h
libcxx/test/std/time/time.hms/time.hms.members/hours.pass.cpp
libcxx/test/std/time/time.hms/time.hms.members/is_negative.pass.cpp
libcxx/test/std/time/time.hms/time.hms.members/minutes.pass.cpp
libcxx/test/std/time/time.hms/time.hms.members/seconds.pass.cpp
libcxx/test/std/time/time.hms/time.hms.members/subseconds.pass.cpp
libcxx/test/std/time/time.hms/time.hms.members/to_duration.pass.cpp
Removed:
################################################################################
diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index 058dd6900cdc7..23726a57af446 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -168,7 +168,7 @@
"`LWG4266 <https://wg21.link/LWG4266>`__","``layout_stride::mapping`` should treat empty mappings as exhaustive","2025-11 (Kona)","|Complete|","23","`#171325 <https://github.com/llvm/llvm-project/issues/171325>`__",""
"`LWG4269 <https://wg21.link/LWG4269>`__","``unique_copy`` passes arguments to its predicate backwards","2025-11 (Kona)","","","`#171326 <https://github.com/llvm/llvm-project/issues/171326>`__",""
"`LWG4272 <https://wg21.link/LWG4272>`__","For ``rank == 0``, ``layout_stride`` is atypically convertible","2025-11 (Kona)","","","`#171327 <https://github.com/llvm/llvm-project/issues/171327>`__",""
-"`LWG4274 <https://wg21.link/LWG4274>`__","The ``chrono::hh_mm_ss`` constructor is ill-formed for unsigned durations","2025-11 (Kona)","","","`#171328 <https://github.com/llvm/llvm-project/issues/171328>`__",""
+"`LWG4274 <https://wg21.link/LWG4274>`__","The ``chrono::hh_mm_ss`` constructor is ill-formed for unsigned durations","2025-11 (Kona)","|Complete|","24","`#171328 <https://github.com/llvm/llvm-project/issues/171328>`__",""
"`LWG4275 <https://wg21.link/LWG4275>`__","``std::dynamic_extent`` should also be defined in ``<mdspan>``","2025-11 (Kona)","|Complete|","21","`#171329 <https://github.com/llvm/llvm-project/issues/171329>`__",""
"`LWG4276 <https://wg21.link/LWG4276>`__","``front()`` and ``back()`` are not hardened for zero-length ``std::array``\s","2025-11 (Kona)","|Complete|","17","`#171330 <https://github.com/llvm/llvm-project/issues/171330>`__",""
"`LWG4280 <https://wg21.link/LWG4280>`__","``simd::partial_load`` uses undefined identifier ``T``","2025-11 (Kona)","","","`#171331 <https://github.com/llvm/llvm-project/issues/171331>`__",""
diff --git a/libcxx/include/__chrono/hh_mm_ss.h b/libcxx/include/__chrono/hh_mm_ss.h
index 746eba903d4f8..2e77304c68a53 100644
--- a/libcxx/include/__chrono/hh_mm_ss.h
+++ b/libcxx/include/__chrono/hh_mm_ss.h
@@ -11,9 +11,9 @@
#define _LIBCPP___CHRONO_HH_MM_SS_H
#include <__chrono/duration.h>
-#include <__chrono/time_point.h>
#include <__config>
#include <__type_traits/common_type.h>
+#include <__type_traits/is_unsigned.h>
#include <ratio>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -45,6 +45,13 @@ class hh_mm_ss {
return 0;
}
+ _LIBCPP_HIDE_FROM_ABI static constexpr _Duration __abs_d(_Duration __d, bool __is_neg) {
+ if constexpr (is_unsigned_v<typename _Duration::rep>)
+ return __d;
+ else
+ return __is_neg ? -__d : __d;
+ }
+
public:
_LIBCPP_HIDE_FROM_ABI static unsigned constexpr fractional_width =
__width(__CommonType::period::den) < 19 ? __width(__CommonType::period::den) : 6u;
@@ -53,11 +60,11 @@ class hh_mm_ss {
_LIBCPP_HIDE_FROM_ABI constexpr hh_mm_ss() noexcept : hh_mm_ss{_Duration::zero()} {}
_LIBCPP_HIDE_FROM_ABI constexpr explicit hh_mm_ss(_Duration __d) noexcept
- : __is_neg_(__d < _Duration(0)),
- __h_(chrono::duration_cast<chrono::hours>(chrono::abs(__d))),
- __m_(chrono::duration_cast<chrono::minutes>(chrono::abs(__d) - hours())),
- __s_(chrono::duration_cast<chrono::seconds>(chrono::abs(__d) - hours() - minutes())),
- __f_(chrono::duration_cast<precision>(chrono::abs(__d) - hours() - minutes() - seconds())) {}
+ : __is_neg_(__d < _Duration::zero()),
+ __h_(chrono::duration_cast<chrono::hours>(__abs_d(__d, __is_neg_))),
+ __m_(chrono::duration_cast<chrono::minutes>(__abs_d(__d, __is_neg_) - hours())),
+ __s_(chrono::duration_cast<chrono::seconds>(__abs_d(__d, __is_neg_) - hours() - minutes())),
+ __f_(chrono::duration_cast<precision>(__abs_d(__d, __is_neg_) - hours() - minutes() - seconds())) {}
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr bool is_negative() const noexcept { return __is_neg_; }
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr chrono::hours hours() const noexcept { return __h_; }
diff --git a/libcxx/test/std/time/time.hms/time.hms.members/hours.pass.cpp b/libcxx/test/std/time/time.hms/time.hms.members/hours.pass.cpp
index ce7591c78b5eb..2e7e3ab7a0e3a 100644
--- a/libcxx/test/std/time/time.hms/time.hms.members/hours.pass.cpp
+++ b/libcxx/test/std/time/time.hms/time.hms.members/hours.pass.cpp
@@ -47,6 +47,8 @@ int main(int, char**)
static_assert( check_hours(std::chrono::minutes( 1)) == 0, "");
static_assert( check_hours(std::chrono::minutes(-1)) == 0, "");
+ static_assert(check_hours(std::chrono::duration<unsigned, std::milli>(123456789)) == 34, "");
+
assert( check_hours(std::chrono::seconds( 5000)) == 1);
assert( check_hours(std::chrono::seconds(-5000)) == 1);
assert( check_hours(std::chrono::minutes( 5000)) == 83);
diff --git a/libcxx/test/std/time/time.hms/time.hms.members/is_negative.pass.cpp b/libcxx/test/std/time/time.hms/time.hms.members/is_negative.pass.cpp
index 51fa2d9a0e051..03047bdfa968a 100644
--- a/libcxx/test/std/time/time.hms/time.hms.members/is_negative.pass.cpp
+++ b/libcxx/test/std/time/time.hms/time.hms.members/is_negative.pass.cpp
@@ -35,6 +35,8 @@ int main(int, char**)
static_assert(!check_neg(std::chrono::minutes( 1)), "");
static_assert( check_neg(std::chrono::minutes(-1)), "");
+ static_assert(!check_neg(std::chrono::duration<unsigned, std::milli>(123456789)), "");
+
assert(!check_neg(std::chrono::seconds( 5000)));
assert( check_neg(std::chrono::seconds(-5000)));
assert(!check_neg(std::chrono::minutes( 5000)));
diff --git a/libcxx/test/std/time/time.hms/time.hms.members/minutes.pass.cpp b/libcxx/test/std/time/time.hms/time.hms.members/minutes.pass.cpp
index 570b2727626b7..2fa1150b57146 100644
--- a/libcxx/test/std/time/time.hms/time.hms.members/minutes.pass.cpp
+++ b/libcxx/test/std/time/time.hms/time.hms.members/minutes.pass.cpp
@@ -38,6 +38,8 @@ int main(int, char**)
static_assert( check_minutes(std::chrono::minutes( 1)) == 1, "");
static_assert( check_minutes(std::chrono::minutes(-1)) == 1, "");
+ static_assert(check_minutes(std::chrono::duration<unsigned, std::milli>(123456789)) == 17, "");
+
assert( check_minutes(std::chrono::seconds( 5000)) == 23);
assert( check_minutes(std::chrono::seconds(-5000)) == 23);
assert( check_minutes(std::chrono::minutes( 5000)) == 20);
diff --git a/libcxx/test/std/time/time.hms/time.hms.members/seconds.pass.cpp b/libcxx/test/std/time/time.hms/time.hms.members/seconds.pass.cpp
index f0723be01a74c..13d5f3c313852 100644
--- a/libcxx/test/std/time/time.hms/time.hms.members/seconds.pass.cpp
+++ b/libcxx/test/std/time/time.hms/time.hms.members/seconds.pass.cpp
@@ -39,6 +39,8 @@ int main(int, char**)
static_assert( check_seconds(std::chrono::seconds( 1)) == 1, "");
static_assert( check_seconds(std::chrono::seconds(-1)) == 1, "");
+ static_assert(check_seconds(std::chrono::duration<unsigned, std::milli>(123456789)) == 36, "");
+
assert( check_seconds(std::chrono::seconds( 5000)) == 20);
assert( check_seconds(std::chrono::seconds(-5000)) == 20);
assert( check_seconds(std::chrono::minutes( 5000)) == 0);
diff --git a/libcxx/test/std/time/time.hms/time.hms.members/subseconds.pass.cpp b/libcxx/test/std/time/time.hms/time.hms.members/subseconds.pass.cpp
index 6504d19c48358..03cc15de9c68b 100644
--- a/libcxx/test/std/time/time.hms/time.hms.members/subseconds.pass.cpp
+++ b/libcxx/test/std/time/time.hms/time.hms.members/subseconds.pass.cpp
@@ -39,6 +39,8 @@ int main(int, char**)
static_assert( check_subseconds(std::chrono::seconds( 1)) == 0, "");
static_assert( check_subseconds(std::chrono::seconds(-1)) == 0, "");
+ static_assert(check_subseconds(std::chrono::duration<unsigned, std::milli>(123456789)) == 789, "");
+
assert( check_subseconds(std::chrono::seconds( 5000)) == 0);
assert( check_subseconds(std::chrono::seconds(-5000)) == 0);
assert( check_subseconds(std::chrono::minutes( 5000)) == 0);
diff --git a/libcxx/test/std/time/time.hms/time.hms.members/to_duration.pass.cpp b/libcxx/test/std/time/time.hms/time.hms.members/to_duration.pass.cpp
index 129dcceb6a0a1..9b77d3a280e94 100644
--- a/libcxx/test/std/time/time.hms/time.hms.members/to_duration.pass.cpp
+++ b/libcxx/test/std/time/time.hms/time.hms.members/to_duration.pass.cpp
@@ -39,6 +39,8 @@ int main(int, char**)
static_assert( check_duration(std::chrono::minutes( 1)) == 60, "");
static_assert( check_duration(std::chrono::minutes(-1)) == -60, "");
+ static_assert(check_duration(std::chrono::duration<unsigned, std::milli>(123456789)) == 123456789, "");
+
assert( check_duration(std::chrono::seconds( 5000)) == 5000LL);
assert( check_duration(std::chrono::seconds(-5000)) == -5000LL);
assert( check_duration(std::chrono::minutes( 5000)) == 300000LL);
More information about the libcxx-commits
mailing list