Bug #14352
closedCompilation fails with C++17 filesystem and Clang in C++20 mode (or higher)
0%
Description
When building with -DWT_CPP17_FILESYSTEM_IMPLEMENTATION="std" and compiling with Clang in C++20 mode (or higher), FileUtils.C fails to compile. The reason is the function lastWriteTime starting at line 69. The relevant lines are (I cannot add line numbers without ruining the formatting, but the first line in this snippet is line 71):
#ifdef WT_FILESYSTEM_IMPL_STD
#ifndef WT_FILESYSTEM_IMPL_STD_CLOCK_17
auto ftime = Wt::cpp17::filesystem::last_write_time(file);
auto systime = decltype(ftime)::clock::to_sys(ftime);
return std::chrono::system_clock::from_time_t(decltype(systime)::clock::to_time_t(systime));
#else // WT_FILESYSTEM_IMPL_STD_CLOCK_17
LOG_DEBUG("When using cpp17 or lower with std::filesystem, the result of this function is an approximation. Use boost::filesystem, instead of std::filesystem (see WT_CPP17_FILESYSTEM_IMPLEMENTATION) if this is a problem for your application.");
auto ftime = Wt::cpp17::filesystem::last_write_time(file);
return std::chrono::time_point_cast<std::chrono::system_clock::duration>(ftime - Wt::cpp17::filesystem::file_time_type::clock::now() + std::chrono::system_clock::now());
#endif //WT_FILESYSTEM_IMPL_STD_CLOCK_17
#else // !WT_FILESYSTEM_IMPL_STD
return std::chrono::system_clock::from_time_t(Wt::cpp17::filesystem::last_write_time(file));
#endif // WT_FILESYSTEM_IMPL_STD
Clang fails on line 75:
error: no viable conversion from 'time_point<[...], duration<__int128, ratio<[...], 1000000000>>>' to 'const
time_point<[...], duration<long long, ratio<[...], 1000000>>>'
75 | return std::chrono::system_clock::from_time_t(decltype(systime)::clock::to_time_t(systime));
|
I target Clang’s “native” libc++ standard library implementation, not GCC’s libstdc++, which I believe would not be affected by this issue.
After looking into this code, I have questions. First of all, why convert via time_t at all? A simple time_point_cast should be enough:
return std::chrono::time_point_cast<std::chrono::system_clock::duration>(systime);
This will, however, keep the sub-second precision that is lost in the current implementation. In my example, the file timestamp was 2026-02-23 22:03:01.145992113. The current code rounds down to the nearest whole second, giving 2026-02-23 22:03:01.000000000 (nanosecond precision) with GCC. Is this intentional? If it is, an explicit chrono::floor on line 74 solves the Clang problem without having to change the return statement on line 75 (though I would still argue that conversion via time_t is unnecessary):
- auto systime = decltype(ftime)::clock::to_sys(ftime);
+ auto systime = std::chrono::floor<std::chrono::seconds>(decltype(ftime)::clock::to_sys(ftime));
(the program compiled with Clang will print six zeros instead of nine, as libc++ uses microsecond precision for system_clock whereas GNU libstdc++ has nanosecond precision)
I compile with C++20/C++23, so WT_FILESYSTEM_IMPL_STD_CLOCK_17 is not defined by default. If overriding this, Clang succeeds, using the return statement on line 79 instead. According to the LOG_DEBUG message, the function will then return “an approximation”, but in reality I seem to get a more accurate timestamp (although less precise), namely 2026-02-23 22:03:01.145992. This value is indeed less precise than ftime, but for Clang that precision was lost anyway when converting to system_clock. With GCC the precision is kept in the C++20 case, but it is rounded down to the nearest second, so while 01.000000000 is more precise than 01.145992, the value itself is further from 01.145992113.
As time_point_cast has been around since C++11 (long before <filesystem>), is it not better to skip the WT_FILESYSTEM_IMPL_STD_CLOCK_17 macro altogether, just keep the #ifndef case and use time_point_cast instead of conversion via time_t? That is, simplify the block of code above to (patch suggestion attached):
#ifdef WT_FILESYSTEM_IMPL_STD
auto ftime = Wt::cpp17::filesystem::last_write_time(file);
auto systime = decltype(ftime)::clock::to_sys(ftime);
return std::chrono::time_point_cast<std::chrono::system_clock::duration>(systime);
#else // !WT_FILESYSTEM_IMPL_STD
return std::chrono::system_clock::from_time_t(Wt::cpp17::filesystem::last_write_time(file));
#endif // WT_FILESYSTEM_IMPL_STD
(If rounding is desired, std::chrono::floor<std::chrono::seconds>(...) must be added to the definition of systime.)
(In my opinion the code would also be clearer if defining ftime above the #ifdef and using it in both cases, but that is not directly relevant to this issue and not part of my patch. It just looks a bit confusing when Wt::cpp17::filesystem is used inside the feature test macro used to define it.)
Files
Updated by Romain Mardulyn about 1 month ago
- Assignee set to Romain Mardulyn
- Target version set to 4.12.4
Updated by Romain Mardulyn 25 days ago
- Status changed from New to InProgress
- Target version set to 4.12.6
Updated by Romain Mardulyn 25 days ago
- Status changed from InProgress to Review
- Assignee deleted (
Romain Mardulyn)
Updated by Raf Pauwels 19 days ago
- Status changed from Review to Resolved
- Assignee changed from Raf Pauwels to Romain Mardulyn
Updated by Romain Mardulyn 18 days ago
- Status changed from Resolved to Implemented @Emweb
Updated by Romain Mardulyn 14 days ago
- Status changed from Implemented @Emweb to Closed
Updated by Erik Solem 9 days ago
The code simplification implemented is an improvement, but it does not solve the problem, which is that compilation fails with Clang, because libc++ does not yet implement clock_cast from C++20.
Updated by Romain Mardulyn about 6 hours ago
- Target version changed from 4.12.6 to 4.13.0
Updated by Romain Mardulyn about 6 hours ago
Hi Eric,
We will add a workaround for Clang in the next version.