Fix filesystem build on platforms with weird time_t types.

32-bit powerpc provides a 64 bit time_t type and older ppc64 systems
provide time_t as a floating point type. This caused problems when building
operations.cpp since operations.cpp contained compile time tests for conversions
between time_t and filesystem time type.

When these tests failed they caused the libc++ build to fail as well. This is unfortunate.

This patch moves the tests out of the source file and into the test suite. It also
expands the tests to allow testing of the weird time_t configurations on all platforms.

git-svn-id: https://llvm.org/svn/llvm-project/libcxx/trunk@307461 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Eric Fiselier
2017-07-08 04:18:41 +00:00
parent 316bb1188f
commit b2e9337b06
4 changed files with 382 additions and 144 deletions

View File

@@ -15,6 +15,8 @@
#include "cstdlib"
#include "climits"
#include "filesystem_time_helper.h"
#include <unistd.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
@@ -505,17 +507,6 @@ bool __fs_is_empty(const path& p, std::error_code *ec)
namespace detail { namespace {
using namespace std::chrono;
template <class CType, class ChronoType>
bool checked_set(CType* out, ChronoType time) {
using Lim = numeric_limits<CType>;
if (time > Lim::max() || time < Lim::min())
return false;
*out = static_cast<CType>(time);
return true;
}
using TimeSpec = struct timespec;
using StatT = struct stat;
@@ -528,137 +519,10 @@ __attribute__((unused)) // Suppress warning
TimeSpec extract_atime(StatT const& st) { return st.st_atim; }
#endif
constexpr auto max_seconds = duration_cast<seconds>(
file_time_type::duration::max()).count();
constexpr auto max_nsec = duration_cast<nanoseconds>(
file_time_type::duration::max() - seconds(max_seconds)).count();
constexpr auto min_seconds = duration_cast<seconds>(
file_time_type::duration::min()).count();
constexpr auto min_nsec_timespec = duration_cast<nanoseconds>(
(file_time_type::duration::min() - seconds(min_seconds)) + seconds(1)).count();
// Static assert that these values properly round trip.
static_assert((seconds(min_seconds) + duration_cast<microseconds>(nanoseconds(min_nsec_timespec)))
- duration_cast<microseconds>(seconds(1))
== file_time_type::duration::min(), "");
constexpr auto max_time_t = numeric_limits<time_t>::max();
constexpr auto min_time_t = numeric_limits<time_t>::min();
#if !defined(__LP64__) && defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wtautological-constant-out-of-range-compare"
#endif
_LIBCPP_CONSTEXPR_AFTER_CXX11
bool is_representable(TimeSpec const& tm) {
if (tm.tv_sec >= 0) {
return (tm.tv_sec < max_seconds) ||
(tm.tv_sec == max_seconds && tm.tv_nsec <= max_nsec);
} else if (tm.tv_sec == (min_seconds - 1)) {
return tm.tv_nsec >= min_nsec_timespec;
} else {
return (tm.tv_sec >= min_seconds);
}
}
#ifndef _LIBCPP_HAS_NO_CXX14_CONSTEXPR
#if defined(__LP64__)
static_assert(is_representable({max_seconds, max_nsec}), "");
static_assert(!is_representable({max_seconds + 1, 0}), "");
static_assert(!is_representable({max_seconds, max_nsec + 1}), "");
static_assert(!is_representable({max_time_t, 0}), "");
static_assert(is_representable({min_seconds, 0}), "");
static_assert(is_representable({min_seconds - 1, min_nsec_timespec}), "");
static_assert(is_representable({min_seconds - 1, min_nsec_timespec + 1}), "");
static_assert(!is_representable({min_seconds - 1, min_nsec_timespec - 1}), "");
static_assert(!is_representable({min_time_t, 999999999}), "");
#else
static_assert(is_representable({max_time_t, 999999999}), "");
static_assert(is_representable({max_time_t, 1000000000}), "");
static_assert(is_representable({min_time_t, 0}), "");
#endif
#endif
_LIBCPP_CONSTEXPR_AFTER_CXX11
bool is_representable(file_time_type const& tm) {
auto secs = duration_cast<seconds>(tm.time_since_epoch());
auto nsecs = duration_cast<nanoseconds>(tm.time_since_epoch() - secs);
if (nsecs.count() < 0) {
secs = secs + seconds(1);
nsecs = nsecs + seconds(1);
}
using TLim = numeric_limits<time_t>;
if (secs.count() >= 0)
return secs.count() <= TLim::max();
return secs.count() >= TLim::min();
}
#ifndef _LIBCPP_HAS_NO_CXX14_CONSTEXPR
#if defined(__LP64__)
static_assert(is_representable(file_time_type::max()), "");
static_assert(is_representable(file_time_type::min()), "");
#else
static_assert(!is_representable(file_time_type::max()), "");
static_assert(!is_representable(file_time_type::min()), "");
static_assert(is_representable(file_time_type(seconds(max_time_t))), "");
static_assert(is_representable(file_time_type(seconds(min_time_t))), "");
#endif
#endif
_LIBCPP_CONSTEXPR_AFTER_CXX11
file_time_type convert_timespec(TimeSpec const& tm) {
auto adj_msec = duration_cast<microseconds>(nanoseconds(tm.tv_nsec));
if (tm.tv_sec >= 0) {
auto Dur = seconds(tm.tv_sec) + microseconds(adj_msec);
return file_time_type(Dur);
} else if (duration_cast<microseconds>(nanoseconds(tm.tv_nsec)).count() == 0) {
return file_time_type(seconds(tm.tv_sec));
} else { // tm.tv_sec < 0
auto adj_subsec = duration_cast<microseconds>(seconds(1) - nanoseconds(tm.tv_nsec));
auto Dur = seconds(tm.tv_sec + 1) - adj_subsec;
return file_time_type(Dur);
}
}
#ifndef _LIBCPP_HAS_NO_CXX14_CONSTEXPR
#if defined(__LP64__)
static_assert(convert_timespec({max_seconds, max_nsec}) == file_time_type::max(), "");
static_assert(convert_timespec({max_seconds, max_nsec - 1}) < file_time_type::max(), "");
static_assert(convert_timespec({max_seconds - 1, 999999999}) < file_time_type::max(), "");
static_assert(convert_timespec({min_seconds - 1, min_nsec_timespec}) == file_time_type::min(), "");
static_assert(convert_timespec({min_seconds - 1, min_nsec_timespec + 1}) > file_time_type::min(), "");
static_assert(convert_timespec({min_seconds , 0}) > file_time_type::min(), "");
#else
// FIXME add tests for 32 bit builds
#endif
#endif
#if !defined(__LP64__) && defined(__clang__)
#pragma clang diagnostic pop
#endif
template <class SubSecDurT, class SubSecT>
bool set_times_checked(time_t* sec_out, SubSecT* subsec_out, file_time_type tp) {
using namespace chrono;
auto dur = tp.time_since_epoch();
auto sec_dur = duration_cast<seconds>(dur);
auto subsec_dur = duration_cast<SubSecDurT>(dur - sec_dur);
// The tv_nsec and tv_usec fields must not be negative so adjust accordingly
if (subsec_dur.count() < 0) {
if (sec_dur.count() > min_seconds) {
sec_dur -= seconds(1);
subsec_dur += seconds(1);
} else {
subsec_dur = SubSecDurT::zero();
}
}
return checked_set(sec_out, sec_dur.count())
&& checked_set(subsec_out, subsec_dur.count());
}
}} // end namespace detail
using FSTime = fs_time_util<file_time_type, time_t, struct timespec>;
file_time_type __last_write_time(const path& p, std::error_code *ec)
{
using namespace ::std::chrono;
@@ -671,12 +535,12 @@ file_time_type __last_write_time(const path& p, std::error_code *ec)
}
if (ec) ec->clear();
auto ts = detail::extract_mtime(st);
if (!detail::is_representable(ts)) {
if (!FSTime::is_representable(ts)) {
set_or_throw(error_code(EOVERFLOW, generic_category()), ec,
"last_write_time", p);
return file_time_type::min();
}
return detail::convert_timespec(ts);
return FSTime::convert_timespec(ts);
}
void __last_write_time(const path& p, file_time_type new_time,
@@ -701,7 +565,7 @@ void __last_write_time(const path& p, file_time_type new_time,
struct ::timeval tbuf[2];
tbuf[0].tv_sec = atime.tv_sec;
tbuf[0].tv_usec = duration_cast<microseconds>(nanoseconds(atime.tv_nsec)).count();
const bool overflowed = !detail::set_times_checked<microseconds>(
const bool overflowed = !FSTime::set_times_checked<microseconds>(
&tbuf[1].tv_sec, &tbuf[1].tv_usec, new_time);
if (overflowed) {
@@ -717,7 +581,7 @@ void __last_write_time(const path& p, file_time_type new_time,
tbuf[0].tv_sec = 0;
tbuf[0].tv_nsec = UTIME_OMIT;
const bool overflowed = !detail::set_times_checked<nanoseconds>(
const bool overflowed = !FSTime::set_times_checked<nanoseconds>(
&tbuf[1].tv_sec, &tbuf[1].tv_nsec, new_time);
if (overflowed) {
set_or_throw(make_error_code(errc::invalid_argument),