Skip to content

Commit

Permalink
[libc++][chrono] Fix streaming for unsigned durations. (#97889)
Browse files Browse the repository at this point in the history
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 #96820
  • Loading branch information
mordante committed Jul 10, 2024
1 parent 2642f2d commit 6c97ad4
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 3 deletions.
14 changes: 11 additions & 3 deletions libcxx/include/__chrono/formatter.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
#include <__memory/addressof.h>
#include <cmath>
#include <ctime>
#include <limits>
#include <sstream>
#include <string_view>

Expand Down Expand Up @@ -589,9 +590,16 @@ __format_chrono(const _Tp& __value,
__sstr << __value;
else {
if constexpr (chrono::__is_duration<_Tp>::value) {
if (__value < __value.zero())
__sstr << _CharT('-');
__formatter::__format_chrono_using_chrono_specs(__sstr, chrono::abs(__value), __chrono_specs);
// A duration can be a user defined arithmetic type. Users may specialize
// numeric_limits, but they may not specialize is_signed.
if constexpr (numeric_limits<typename _Tp::rep>::is_signed) {
if (__value < __value.zero()) {
__sstr << _CharT('-');
__formatter::__format_chrono_using_chrono_specs(__sstr, -__value, __chrono_specs);
} else
__formatter::__format_chrono_using_chrono_specs(__sstr, __value, __chrono_specs);
} else
__formatter::__format_chrono_using_chrono_specs(__sstr, __value, __chrono_specs);
// TODO FMT When keeping the precision it will truncate the string.
// Note that the behaviour what the precision does isn't specified.
__specs.__precision_ = -1;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -216,10 +216,35 @@ static void test_units() {
assert(stream_ja_JP_locale<CharT>(std::chrono::duration<int, std::ratio<11, 9>>(0)) == SV("0[11/9]s"));
}

template <class CharT>
static void test_unsigned_types() {
// Reported in https://github.com/llvm/llvm-project/issues/96820
using namespace std::literals::chrono_literals;

// C locale
assert(stream_c_locale<CharT>(std::chrono::duration<unsigned short, std::atto>(0)) == SV("0as"));
assert(stream_c_locale<CharT>(std::chrono::duration<unsigned, std::femto>(0)) == SV("0fs"));
assert(stream_c_locale<CharT>(std::chrono::duration<unsigned long, std::pico>(0)) == SV("0ps"));
assert(stream_c_locale<CharT>(std::chrono::duration<unsigned long long, std::nano>(0)) == SV("0ns"));

// fr_FR locale
assert(stream_fr_FR_locale<CharT>(std::chrono::duration<unsigned short, std::atto>(0)) == SV("0as"));
assert(stream_fr_FR_locale<CharT>(std::chrono::duration<unsigned, std::femto>(0)) == SV("0fs"));
assert(stream_fr_FR_locale<CharT>(std::chrono::duration<unsigned long, std::pico>(0)) == SV("0ps"));
assert(stream_fr_FR_locale<CharT>(std::chrono::duration<unsigned long long, std::nano>(0)) == SV("0ns"));

// ja_JP locale
assert(stream_ja_JP_locale<CharT>(std::chrono::duration<unsigned short, std::atto>(0)) == SV("0as"));
assert(stream_ja_JP_locale<CharT>(std::chrono::duration<unsigned, std::femto>(0)) == SV("0fs"));
assert(stream_ja_JP_locale<CharT>(std::chrono::duration<unsigned long, std::pico>(0)) == SV("0ps"));
assert(stream_ja_JP_locale<CharT>(std::chrono::duration<unsigned long long, std::nano>(0)) == SV("0ns"));
}

template <class CharT>
static void test() {
test_values<CharT>();
test_units<CharT>();
test_unsigned_types<CharT>();
}

int main(int, char**) {
Expand Down
12 changes: 12 additions & 0 deletions libcxx/test/std/time/time.syn/formatter.duration.pass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1157,12 +1157,24 @@ static void test_pr62082() {
check(SV("00.111111"), SV("{:%S}"), std::chrono::duration<long double, std::ratio<1, 9>>{1});
}

template <class CharT>
static void test_unsigned_duration() {
// Reported in https://github.com/llvm/llvm-project/issues/96820
using namespace std::literals::chrono_literals;

check(SV("1as"), SV("{}"), std::chrono::duration<unsigned short, std::atto>(1));
check(SV("1fs"), SV("{}"), std::chrono::duration<unsigned, std::femto>(1));
check(SV("1ps"), SV("{}"), std::chrono::duration<unsigned long, std::pico>(1));
check(SV("1ns"), SV("{}"), std::chrono::duration<unsigned long long, std::nano>(1));
}

template <class CharT>
static void test() {
using namespace std::literals::chrono_literals;

test_no_chrono_specs<CharT>();
test_valid_values<CharT>();
test_unsigned_duration<CharT>();
test_pr62082<CharT>();

check_invalid_types<CharT>(
Expand Down

0 comments on commit 6c97ad4

Please sign in to comment.