Skip to content

Commit 6c97ad4

Browse files
authored
[libc++][chrono] Fix streaming for unsigned durations. (llvm#97889)
This fixes formatting for durations using the unsigned types unsigned short, unsigned, unsigned long, and unsigned long long. It does not allow the unsigned char type. Since the formatting function uses ostream::operator<< this type probably does not do what it should do. Note that based on the standard all arithmetic types are allowed, including bool, char, wchar_t. These types are not implemented either. Allowing them seems like a defect in the Standard. No effort is done to support user-defined types; the wording in the Standard is unclear regarding the constrains for these types. [LWG 4118](https://cplusplus.github.io/LWG/issue4118) discusses this issue further. Fixes llvm#96820
1 parent 2642f2d commit 6c97ad4

File tree

3 files changed

+48
-3
lines changed

3 files changed

+48
-3
lines changed

libcxx/include/__chrono/formatter.h

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
#include <__memory/addressof.h>
4747
#include <cmath>
4848
#include <ctime>
49+
#include <limits>
4950
#include <sstream>
5051
#include <string_view>
5152

@@ -589,9 +590,16 @@ __format_chrono(const _Tp& __value,
589590
__sstr << __value;
590591
else {
591592
if constexpr (chrono::__is_duration<_Tp>::value) {
592-
if (__value < __value.zero())
593-
__sstr << _CharT('-');
594-
__formatter::__format_chrono_using_chrono_specs(__sstr, chrono::abs(__value), __chrono_specs);
593+
// A duration can be a user defined arithmetic type. Users may specialize
594+
// numeric_limits, but they may not specialize is_signed.
595+
if constexpr (numeric_limits<typename _Tp::rep>::is_signed) {
596+
if (__value < __value.zero()) {
597+
__sstr << _CharT('-');
598+
__formatter::__format_chrono_using_chrono_specs(__sstr, -__value, __chrono_specs);
599+
} else
600+
__formatter::__format_chrono_using_chrono_specs(__sstr, __value, __chrono_specs);
601+
} else
602+
__formatter::__format_chrono_using_chrono_specs(__sstr, __value, __chrono_specs);
595603
// TODO FMT When keeping the precision it will truncate the string.
596604
// Note that the behaviour what the precision does isn't specified.
597605
__specs.__precision_ = -1;

libcxx/test/std/time/time.duration/time.duration.nonmember/ostream.pass.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,10 +216,35 @@ static void test_units() {
216216
assert(stream_ja_JP_locale<CharT>(std::chrono::duration<int, std::ratio<11, 9>>(0)) == SV("0[11/9]s"));
217217
}
218218

219+
template <class CharT>
220+
static void test_unsigned_types() {
221+
// Reported in https://github.com/llvm/llvm-project/issues/96820
222+
using namespace std::literals::chrono_literals;
223+
224+
// C locale
225+
assert(stream_c_locale<CharT>(std::chrono::duration<unsigned short, std::atto>(0)) == SV("0as"));
226+
assert(stream_c_locale<CharT>(std::chrono::duration<unsigned, std::femto>(0)) == SV("0fs"));
227+
assert(stream_c_locale<CharT>(std::chrono::duration<unsigned long, std::pico>(0)) == SV("0ps"));
228+
assert(stream_c_locale<CharT>(std::chrono::duration<unsigned long long, std::nano>(0)) == SV("0ns"));
229+
230+
// fr_FR locale
231+
assert(stream_fr_FR_locale<CharT>(std::chrono::duration<unsigned short, std::atto>(0)) == SV("0as"));
232+
assert(stream_fr_FR_locale<CharT>(std::chrono::duration<unsigned, std::femto>(0)) == SV("0fs"));
233+
assert(stream_fr_FR_locale<CharT>(std::chrono::duration<unsigned long, std::pico>(0)) == SV("0ps"));
234+
assert(stream_fr_FR_locale<CharT>(std::chrono::duration<unsigned long long, std::nano>(0)) == SV("0ns"));
235+
236+
// ja_JP locale
237+
assert(stream_ja_JP_locale<CharT>(std::chrono::duration<unsigned short, std::atto>(0)) == SV("0as"));
238+
assert(stream_ja_JP_locale<CharT>(std::chrono::duration<unsigned, std::femto>(0)) == SV("0fs"));
239+
assert(stream_ja_JP_locale<CharT>(std::chrono::duration<unsigned long, std::pico>(0)) == SV("0ps"));
240+
assert(stream_ja_JP_locale<CharT>(std::chrono::duration<unsigned long long, std::nano>(0)) == SV("0ns"));
241+
}
242+
219243
template <class CharT>
220244
static void test() {
221245
test_values<CharT>();
222246
test_units<CharT>();
247+
test_unsigned_types<CharT>();
223248
}
224249

225250
int main(int, char**) {

libcxx/test/std/time/time.syn/formatter.duration.pass.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1157,12 +1157,24 @@ static void test_pr62082() {
11571157
check(SV("00.111111"), SV("{:%S}"), std::chrono::duration<long double, std::ratio<1, 9>>{1});
11581158
}
11591159

1160+
template <class CharT>
1161+
static void test_unsigned_duration() {
1162+
// Reported in https://github.com/llvm/llvm-project/issues/96820
1163+
using namespace std::literals::chrono_literals;
1164+
1165+
check(SV("1as"), SV("{}"), std::chrono::duration<unsigned short, std::atto>(1));
1166+
check(SV("1fs"), SV("{}"), std::chrono::duration<unsigned, std::femto>(1));
1167+
check(SV("1ps"), SV("{}"), std::chrono::duration<unsigned long, std::pico>(1));
1168+
check(SV("1ns"), SV("{}"), std::chrono::duration<unsigned long long, std::nano>(1));
1169+
}
1170+
11601171
template <class CharT>
11611172
static void test() {
11621173
using namespace std::literals::chrono_literals;
11631174

11641175
test_no_chrono_specs<CharT>();
11651176
test_valid_values<CharT>();
1177+
test_unsigned_duration<CharT>();
11661178
test_pr62082<CharT>();
11671179

11681180
check_invalid_types<CharT>(

0 commit comments

Comments
 (0)