In C++17 the clock used for filesystem::file_time_type is unspecified,
allowing it to be chrono::system_clock. The C++2a draft requires it to
be a distinct type, with additional member functions to convert to/from
other clocks (either the system clock or UTC). In order to avoid an ABI
change later, this patch defines a new distinct type now, which will be
used for std::chrono::file_clock later.

        * include/bits/fs_fwd.h (__file_clock): Define new clock.
        (file_time_type): Redefine in terms of __file_clock.
        * src/filesystem/ops-common.h (file_time): Add FIXME comment about
        overflow.
        * src/filesystem/std-ops.cc (is_set(perm_options, perm_options)): Give
        internal linkage.
        (internal_file_lock): New helper type for accessing __file_clock.
        (do_copy_file): Use internal_file_lock to convert system time to
        file_time_type.
        (last_write_time(const path&, error_code&)): Likewise.
        (last_write_time(const path&, file_time_type, error_code&)): Likewise.

Tested powerpc64-linux, committed to trunk.

commit c91a86730ed8df76325dd0649d8837e6feb24aab
Author: Jonathan Wakely <jwak...@redhat.com>
Date:   Sat Jan 5 12:52:40 2019 +0000

    Define new filesystem::__file_clock type
    
    In C++17 the clock used for filesystem::file_time_type is unspecified,
    allowing it to be chrono::system_clock. The C++2a draft requires it to
    be a distinct type, with additional member functions to convert to/from
    other clocks (either the system clock or UTC). In order to avoid an ABI
    change later, this patch defines a new distinct type now, which will be
    used for std::chrono::file_clock later.
    
            * include/bits/fs_fwd.h (__file_clock): Define new clock.
            (file_time_type): Redefine in terms of __file_clock.
            * src/filesystem/ops-common.h (file_time): Add FIXME comment about
            overflow.
            * src/filesystem/std-ops.cc (is_set(perm_options, perm_options)): 
Give
            internal linkage.
            (internal_file_lock): New helper type for accessing __file_clock.
            (do_copy_file): Use internal_file_lock to convert system time to
            file_time_type.
            (last_write_time(const path&, error_code&)): Likewise.
            (last_write_time(const path&, file_time_type, error_code&)): 
Likewise.

diff --git a/libstdc++-v3/include/bits/fs_fwd.h 
b/libstdc++-v3/include/bits/fs_fwd.h
index 3bcf2dd650c..a0e3d73e2a3 100644
--- a/libstdc++-v3/include/bits/fs_fwd.h
+++ b/libstdc++-v3/include/bits/fs_fwd.h
@@ -294,7 +294,49 @@ _GLIBCXX_END_NAMESPACE_CXX11
   operator^=(directory_options& __x, directory_options __y) noexcept
   { return __x = __x ^ __y; }
 
-  using file_time_type = std::chrono::system_clock::time_point;
+  struct __file_clock
+  {
+    using duration                  = chrono::nanoseconds;
+    using rep                       = duration::rep;
+    using period                    = duration::period;
+    using time_point                = chrono::time_point<__file_clock>;
+    static constexpr bool is_steady = false;
+
+    static time_point
+    now() noexcept
+    { return _S_from_sys(chrono::system_clock::now()); }
+
+  private:
+    using __sys_clock = chrono::system_clock;
+
+    // This clock's (unspecified) epoch is 2174-01-01 00:00:00 UTC.
+    // A signed 64-bit duration with nanosecond resolution gives roughly
+    // +/- 292 years, which covers the 1901-2446 date range for ext4.
+    static constexpr chrono::seconds _S_epoch_diff{6437664000};
+
+  protected:
+    // For internal use only
+    template<typename _Dur>
+      static
+      chrono::time_point<__file_clock, _Dur>
+      _S_from_sys(const chrono::time_point<__sys_clock, _Dur>& __t) noexcept
+      {
+       using __file_time = chrono::time_point<__file_clock, _Dur>;
+       return __file_time{__t.time_since_epoch()} - _S_epoch_diff;
+      }
+
+    // For internal use only
+    template<typename _Dur>
+      static
+      chrono::time_point<__sys_clock, _Dur>
+      _S_to_sys(const chrono::time_point<__file_clock, _Dur>& __t) noexcept
+      {
+       using __sys_time = chrono::time_point<__sys_clock, _Dur>;
+       return __sys_time{__t.time_since_epoch()} + _S_epoch_diff;
+      }
+  };
+
+  using file_time_type = __file_clock::time_point;
 
   // operational functions
 
diff --git a/libstdc++-v3/src/filesystem/ops-common.h 
b/libstdc++-v3/src/filesystem/ops-common.h
index dcd61cc26cd..1c0d650f444 100644
--- a/libstdc++-v3/src/filesystem/ops-common.h
+++ b/libstdc++-v3/src/filesystem/ops-common.h
@@ -158,6 +158,24 @@ namespace __gnu_posix
     nanoseconds ns{};
 #endif
 
+    // FIXME
+    // There are possible timespec values which will overflow
+    // chrono::system_clock::time_point but would not overflow
+    // __file_clock::time_point, due to its different epoch.
+    //
+    // By checking for overflow of the intermediate system_clock::duration
+    // type, we report an error for values which are actually representable
+    // in the file_time_type result type.
+    //
+    // Howard Hinnant's solution for this problem is to use
+    // duration<__int128>{s} + ns, which doesn't overflow.
+    // An alternative would be to do the epoch correction on s before
+    // the addition, and then go straight to file_time_type instead of
+    // going via chrono::system_clock::time_point.
+    //
+    // (This only applies to the C++17 Filesystem library, because for the
+    // Filesystem TS we don't have a distinct __file_clock, we just use the
+    // system clock for file timestamps).
     if (s >= (nanoseconds::max().count() / 1e9))
       {
        ec = std::make_error_code(std::errc::value_too_large); // EOVERFLOW
diff --git a/libstdc++-v3/src/filesystem/std-ops.cc 
b/libstdc++-v3/src/filesystem/std-ops.cc
index 26a7ad2a198..8c3ec1d9a9a 100644
--- a/libstdc++-v3/src/filesystem/std-ops.cc
+++ b/libstdc++-v3/src/filesystem/std-ops.cc
@@ -268,13 +268,32 @@ fs::copy(const path& from, const path& to, copy_options 
options)
 namespace std::filesystem
 {
   // Need this as there's no 'perm_options::none' enumerator.
-  inline bool is_set(fs::perm_options obj, fs::perm_options bits)
+  static inline bool is_set(fs::perm_options obj, fs::perm_options bits)
   {
     return (obj & bits) != fs::perm_options{};
   }
 }
 
 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
+
+namespace
+{
+  struct internal_file_clock : fs::__file_clock
+  {
+    using __file_clock::_S_to_sys;
+    using __file_clock::_S_from_sys;
+
+    static fs::file_time_type
+    from_stat(const fs::stat_type& st, std::error_code& ec) noexcept
+    {
+      const auto sys_time = fs::file_time(st, ec);
+      if (sys_time == sys_time.min())
+       return fs::file_time_type::min();
+      return _S_from_sys(sys_time);
+    }
+  };
+}
+
 #ifdef NEED_DO_COPY_FILE
 bool
 fs::do_copy_file(const path::value_type* from, const path::value_type* to,
@@ -348,10 +367,10 @@ fs::do_copy_file(const path::value_type* from, const 
path::value_type* to,
        }
       else if (options.update)
        {
-         const auto from_mtime = file_time(*from_st, ec);
+         const auto from_mtime = internal_file_clock::from_stat(*from_st, ec);
          if (ec)
            return false;
-         if ((from_mtime <= file_time(*to_st, ec)) || ec)
+         if ((from_mtime <= internal_file_clock::from_stat(*to_st, ec)) || ec)
            return false;
        }
       else if (!options.overwrite)
@@ -1122,7 +1141,10 @@ fs::last_write_time(const path& p)
 fs::file_time_type
 fs::last_write_time(const path& p, error_code& ec) noexcept
 {
-  return do_stat(p, ec, [&ec](const auto& st) { return file_time(st, ec); },
+  return do_stat(p, ec,
+                [&ec](const auto& st) {
+                    return internal_file_clock::from_stat(st, ec);
+                },
                 file_time_type::min());
 }
 
@@ -1139,7 +1161,7 @@ void
 fs::last_write_time(const path& p __attribute__((__unused__)),
                    file_time_type new_time, error_code& ec) noexcept
 {
-  auto d = new_time.time_since_epoch();
+  auto d = internal_file_clock::_S_to_sys(new_time).time_since_epoch();
   auto s = chrono::duration_cast<chrono::seconds>(d);
 #if _GLIBCXX_USE_UTIMENSAT
   auto ns = chrono::duration_cast<chrono::nanoseconds>(d - s);

Reply via email to