The Abseil Duration Conversion upgrade tool finds calls to absl::Duration
arithmetic operators and factories that rely on deprecated overloads. These
calls invoke implicit conversions that can lead to subtle bugs. After upcoming
API changes, an explicit cast will be required for affected call sites to
continue compiling.
The clang-tidy check to apply necessary changes is abseil-upgrade-duration-conversions.
The arithmetic operators (*=
, /=
, *
, and /
) for absl::Duration
currently have templated overloads to accept an argument of class type that is
convertible to any arithmetic type. The implementation always converts such an
argument to an int64_t
. This happens even in a case such as
std::atomic<float>
, which leads to loss of precision:
absl::Duration d;
d *= std::atomic<float>(3.5f); // actually multiplies by 3!
The following factory functions have a similar issue we are addressing:
absl::Nanoseconds
absl::Microseconds
absl::Milliseconds
absl::Seconds
absl::Minutes
absl::Hours
Currently, each of these factories has an overload that accepts an int64_t
.
This overload ends up being chosen for calls with an argument of class type that
is convertible to any arithmetic type. As before, this can lead to unexpected
and unintended behavior that is difficult to spot at the call site.
In the case of both the operators and factories, current calls using floating point types or integral types are unaffected by these issues.
These operators and factories will be changed to only accept arithmetic types in order to prevent unintended behavior. The fix ahead of this change is to explicitly cast arguments of class type that are currently getting passed to the affected APIs.
You can use the released clang-tidy check to do this in an automated way. However, if clang-tidy is not an option, here is an example of the manual fixes required:
std::atomic<int> a;
absl::Duration d = absl::Milliseconds(a);
d *= a;
becomes
std::atomic<int> a;
absl::Duration d = absl::Milliseconds(static_cast<int>(a));
d *= static_cast<int>(a);