Stl: <chrono>: No diagnostic for truncating conversion of chrono duration

Created on 12 Jul 2020  Â·  3Comments  Â·  Source: microsoft/STL

Describe the bug
Chrono does not emit any compile-time warning when truncating duration

Command-line test case


d:\Temp2>type repro.cpp
#pragma warning(disable:4820) // '%d' bytes padding added after data member '%s'
#pragma warning(disable:4514) // '%s': unreferenced inline function has been removed

#include <chrono>

void f(std::chrono::duration<unsigned long, std::milli>) {}

int main() {
    f(std::chrono::duration<unsigned long long, std::milli>{ 10000000000000000000ULL });
    return 0;
}
d:\Temp2>cl /EHsc /W4 /Wall /WX .\repro.cpp
Microsoft (R) C/C++ Optimizing Compiler Version 19.27.29009.1 for x86
Copyright (C) Microsoft Corporation.  All rights reserved.

repro.cpp
Microsoft (R) Incremental Linker Version 14.27.29009.1
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:repro.exe
repro.obj

Expected behavior
A warning

STL version

Microsoft Visual Studio Professional 2019 Preview
Version 16.7.0 Preview 3.1

Additional context
Also tracked by DevCom-671799 and VSO-961527 / AB#961527 .

bug wontfix

All 3 comments

I was trying to solve this issue but there are two reasons that made me unsure about the validity of this issue.
First of all, I couldn't find anything missed in the implementation according to the standard.
This is the standard definitions about Duration constructors [time.duration.cons]:

template<class Rep2>
constexpr explicit duration(const Rep2& r);
1. Constraints: is_convertible_v<const Rep2&, rep> is true and
(1.1) — treat_as_floating_point_v<rep> is true or
(1.2) — treat_as_floating_point_v<Rep2> is false.
[Example:
duration<int, milli> d(3); // OK
duration<int, milli> d(3.5); // error
— end example]
2. Postconditions: count() == static_cast<rep>(r).

template<class Rep2, class Period2>
constexpr duration(const duration<Rep2, Period2>& d);
3. Constraints: No overflow is induced in the conversion and treat_as_floating_point_v<rep> is
true or both ratio_divide<Period2, period>::den is 1 and treat_as_floating_point_v<Rep2>
is false. [Note: This requirement prevents implicit truncation error when converting between integralbased duration types. Such a construction could easily lead to confusion about the value of the
duration. — end note] [Example:
duration<int, milli> ms(3);
duration<int, micro> us = ms; // OK
duration<int, milli> ms2 = us; // error
— end example]
4. Effects: Initializes rep_ with duration_cast<duration>(d).count()

And here is the current implementation:
https://github.com/microsoft/STL/blob/5f3e91211ab25a2f493edf5f7d28520ac6b81d28/stl/inc/chrono#L76-L91

Furthermore, neither GCC nor Clangdoesn't generate the expected truncation warning for the described test case. I know it's not a strong enough reasoning but I think it's very unlikely that the other implementations have exactly the same issue!

To me it is not a bug, but a desired behavior to have this warning. Even though other compilers don't implement it, if it is possible then why not.

Thanks for looking into this, @Arzaghi. After checking the code and the Standard, I believe this is by design - our implementation is doing exactly what the Standard says - and while we're free to emit warnings whenever we want, and we often emit truncation warnings when the truncation is triggered by the user and it would warn in their own code (this is a major difference between our implementation and others, which generally suppress warnings from "system headers"), in this case the Standard's depicted static_casts are suppressing the warning. I believe that attempting to remove the static_casts would be more trouble than it's worth - specifically emitting warnings in code that has been compiling successfully for a long time, and which isn't actually truncating values at runtime.

The relevant Standardese is
WG21-N4868 [time.duration.cons]/4 saying that duration's converting constructor "Initializes rep_ with duration_cast<duration>(d).count()." and [time.duration.cast]/2.1 saying "If CF::num == 1 and CF::den == 1, returns ToDuration(static_cast<typename ToDuration::rep>(d.count()))". This means that when the ratios are the same (here, both milli), we static_cast the representation.

I'll go reply on DevCom and resolve the linked bug.

Was this page helpful?
0 / 5 - 0 ratings