[libcxx] r329028 - Implement filesystem NB comments, relative paths, and related issues.

Eric Fiselier via cfe-commits cfe-commits at lists.llvm.org
Mon Apr 2 16:03:41 PDT 2018


Author: ericwf
Date: Mon Apr  2 16:03:41 2018
New Revision: 329028

URL: http://llvm.org/viewvc/llvm-project?rev=329028&view=rev
Log:
Implement filesystem NB comments, relative paths, and related issues.

This is a fairly large patch that implements all of the filesystem NB comments
and the relative paths changes (ex. adding weakly_canonical). These issues
and papers are all interrelated so their implementation couldn't be split up
nicely.

This patch upgrades <experimental/filesystem> to match the C++17 spec and not
the published experimental TS spec. Some of the changes in this patch are both
API and ABI breaking, however libc++ makes no guarantee about stability for
experimental implementations.

The major changes in this patch are:

* Implement NB comments for filesystem (P0492R2), including:
  * Implement `perm_options` enum as part of NB comments, and update the
    `permissions` function to match.
  * Implement changes to `remove_filename` and `replace_filename`
  * Implement changes to `path::stem()` and `path::extension()` which support
    splitting examples like `.profile`.
  * Change path iteration to return an empty path instead of '.' for trailing
    separators.
  * Change `operator/=` to handle absolute paths on the RHS.
  * Change `absolute` to no longer accept a current path argument.

* Implement relative paths according to NB comments (P0219r1)

* Combine `path.cpp` and `operations.cpp` since some path functions require
  access to the operations internals, and some fs operations require access
  to the path parser.

Added:
    libcxx/trunk/test/std/experimental/filesystem/class.path/path.member/path.gen/
    libcxx/trunk/test/std/experimental/filesystem/class.path/path.member/path.gen/lexically_normal.pass.cpp
    libcxx/trunk/test/std/experimental/filesystem/class.path/path.member/path.gen/lexically_relative_and_proximate.pass.cpp
    libcxx/trunk/test/std/experimental/filesystem/fs.enum/enum.perm_options.pass.cpp
    libcxx/trunk/test/std/experimental/filesystem/fs.op.funcs/fs.op.proximate/
    libcxx/trunk/test/std/experimental/filesystem/fs.op.funcs/fs.op.proximate/proximate.pass.cpp
    libcxx/trunk/test/std/experimental/filesystem/fs.op.funcs/fs.op.relative/
    libcxx/trunk/test/std/experimental/filesystem/fs.op.funcs/fs.op.relative/relative.pass.cpp
    libcxx/trunk/test/std/experimental/filesystem/fs.op.funcs/fs.op.weakly_canonical/
    libcxx/trunk/test/std/experimental/filesystem/fs.op.funcs/fs.op.weakly_canonical/weakly_canonical.pass.cpp
    libcxx/trunk/test/support/verbose_assert.h
Removed:
    libcxx/trunk/src/experimental/filesystem/path.cpp
    libcxx/trunk/test/libcxx/experimental/filesystem/class.path/path.member/path.append.pass.cpp
    libcxx/trunk/test/std/experimental/filesystem/fs.op.funcs/fs.op.system_complete/system_complete.pass.cpp
Modified:
    libcxx/trunk/benchmarks/CMakeLists.txt
    libcxx/trunk/benchmarks/GenerateInput.hpp
    libcxx/trunk/benchmarks/filesystem.bench.cpp
    libcxx/trunk/include/experimental/filesystem
    libcxx/trunk/src/experimental/filesystem/operations.cpp
    libcxx/trunk/test/std/experimental/filesystem/class.path/path.itr/iterator.pass.cpp
    libcxx/trunk/test/std/experimental/filesystem/class.path/path.member/path.append.pass.cpp
    libcxx/trunk/test/std/experimental/filesystem/class.path/path.member/path.compare.pass.cpp
    libcxx/trunk/test/std/experimental/filesystem/class.path/path.member/path.decompose/empty.fail.cpp
    libcxx/trunk/test/std/experimental/filesystem/class.path/path.member/path.decompose/path.decompose.pass.cpp
    libcxx/trunk/test/std/experimental/filesystem/class.path/path.member/path.generic.obs/generic_string_alloc.pass.cpp
    libcxx/trunk/test/std/experimental/filesystem/class.path/path.member/path.generic.obs/named_overloads.pass.cpp
    libcxx/trunk/test/std/experimental/filesystem/class.path/path.member/path.modifiers/remove_filename.pass.cpp
    libcxx/trunk/test/std/experimental/filesystem/class.path/path.member/path.modifiers/replace_filename.pass.cpp
    libcxx/trunk/test/std/experimental/filesystem/fs.op.funcs/fs.op.absolute/absolute.pass.cpp
    libcxx/trunk/test/std/experimental/filesystem/fs.op.funcs/fs.op.canonical/canonical.pass.cpp
    libcxx/trunk/test/std/experimental/filesystem/fs.op.funcs/fs.op.last_write_time/last_write_time.pass.cpp
    libcxx/trunk/test/support/filesystem_test_helper.hpp
    libcxx/trunk/www/cxx1z_status.html
    libcxx/trunk/www/cxx2a_status.html

Modified: libcxx/trunk/benchmarks/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/benchmarks/CMakeLists.txt?rev=329028&r1=329027&r2=329028&view=diff
==============================================================================
--- libcxx/trunk/benchmarks/CMakeLists.txt (original)
+++ libcxx/trunk/benchmarks/CMakeLists.txt Mon Apr  2 16:03:41 2018
@@ -68,7 +68,7 @@ set(BENCHMARK_OUTPUT_DIR ${CMAKE_CURRENT
 set(BENCHMARK_LIBCXX_INSTALL ${CMAKE_CURRENT_BINARY_DIR}/benchmark-libcxx)
 set(BENCHMARK_NATIVE_INSTALL ${CMAKE_CURRENT_BINARY_DIR}/benchmark-native)
 set(BENCHMARK_TEST_COMPILE_FLAGS
-    -std=c++14 -O2
+    -std=c++17 -O2
     -I${BENCHMARK_LIBCXX_INSTALL}/include
     -I${LIBCXX_SOURCE_DIR}/test/support
 )

Modified: libcxx/trunk/benchmarks/GenerateInput.hpp
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/benchmarks/GenerateInput.hpp?rev=329028&r1=329027&r2=329028&view=diff
==============================================================================
--- libcxx/trunk/benchmarks/GenerateInput.hpp (original)
+++ libcxx/trunk/benchmarks/GenerateInput.hpp Mon Apr  2 16:03:41 2018
@@ -29,14 +29,16 @@ inline std::default_random_engine& getRa
     return RandEngine;
 }
 
+
 inline char getRandomChar() {
     std::uniform_int_distribution<> LettersDist(0, LettersSize-1);
     return Letters[LettersDist(getRandomEngine())];
 }
 
 template <class IntT>
-inline IntT getRandomInteger() {
-    std::uniform_int_distribution<IntT> dist;
+inline IntT getRandomInteger(IntT Min = 0,
+                             IntT Max = std::numeric_limits<IntT>::max()) {
+    std::uniform_int_distribution<IntT> dist(Min, Max);
     return dist(getRandomEngine());
 }
 

Modified: libcxx/trunk/benchmarks/filesystem.bench.cpp
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/benchmarks/filesystem.bench.cpp?rev=329028&r1=329027&r2=329028&view=diff
==============================================================================
--- libcxx/trunk/benchmarks/filesystem.bench.cpp (original)
+++ libcxx/trunk/benchmarks/filesystem.bench.cpp Mon Apr  2 16:03:41 2018
@@ -1,17 +1,14 @@
-#include <experimental/filesystem>
-
 #include "benchmark/benchmark.h"
 #include "GenerateInput.hpp"
 #include "test_iterators.h"
-
-namespace fs = std::experimental::filesystem;
+#include "filesystem_include.hpp"
 
 static const size_t TestNumInputs = 1024;
 
 
 template <class GenInputs>
 void BM_PathConstructString(benchmark::State &st, GenInputs gen) {
-  using namespace fs;
+  using fs::path;
   const auto in = gen(st.range(0));
   path PP;
   for (auto& Part : in)
@@ -21,14 +18,15 @@ void BM_PathConstructString(benchmark::S
     const path P(PP.native());
     benchmark::DoNotOptimize(P.native().data());
   }
+  st.SetComplexityN(st.range(0));
 }
 BENCHMARK_CAPTURE(BM_PathConstructString, large_string,
-  getRandomStringInputs)->Arg(TestNumInputs);
+  getRandomStringInputs)->Range(8, TestNumInputs)->Complexity();
 
 
 template <class GenInputs>
 void BM_PathConstructCStr(benchmark::State &st, GenInputs gen) {
-  using namespace fs;
+  using fs::path;
   const auto in = gen(st.range(0));
   path PP;
   for (auto& Part : in)
@@ -45,7 +43,7 @@ BENCHMARK_CAPTURE(BM_PathConstructCStr,
 
 template <template <class...> class ItType, class GenInputs>
 void BM_PathConstructIter(benchmark::State &st, GenInputs gen) {
-  using namespace fs;
+  using fs::path;
   using Iter = ItType<std::string::const_iterator>;
   const auto in = gen(st.range(0));
   path PP;
@@ -60,6 +58,7 @@ void BM_PathConstructIter(benchmark::Sta
     const path P(Start, End);
     benchmark::DoNotOptimize(P.native().data());
   }
+  st.SetComplexityN(st.range(0));
 }
 template <class GenInputs>
 void BM_PathConstructInputIter(benchmark::State &st, GenInputs gen) {
@@ -70,14 +69,14 @@ void BM_PathConstructForwardIter(benchma
   BM_PathConstructIter<forward_iterator>(st, gen);
 }
 BENCHMARK_CAPTURE(BM_PathConstructInputIter, large_string,
-  getRandomStringInputs)->Arg(TestNumInputs);
+  getRandomStringInputs)->Range(8, TestNumInputs)->Complexity();
 BENCHMARK_CAPTURE(BM_PathConstructForwardIter, large_string,
-  getRandomStringInputs)->Arg(TestNumInputs);
+  getRandomStringInputs)->Range(8, TestNumInputs)->Complexity();
 
 
 template <class GenInputs>
 void BM_PathIterateMultipleTimes(benchmark::State &st, GenInputs gen) {
-  using namespace fs;
+  using fs::path;
   const auto in = gen(st.range(0));
   path PP;
   for (auto& Part : in)
@@ -89,14 +88,15 @@ void BM_PathIterateMultipleTimes(benchma
     }
     benchmark::ClobberMemory();
   }
+  st.SetComplexityN(st.range(0));
 }
 BENCHMARK_CAPTURE(BM_PathIterateMultipleTimes, iterate_elements,
-  getRandomStringInputs)->Arg(TestNumInputs);
+  getRandomStringInputs)->Range(8, TestNumInputs)->Complexity();
 
 
 template <class GenInputs>
 void BM_PathIterateOnce(benchmark::State &st, GenInputs gen) {
-  using namespace fs;
+  using fs::path;
   const auto in = gen(st.range(0));
   path PP;
   for (auto& Part : in)
@@ -109,13 +109,14 @@ void BM_PathIterateOnce(benchmark::State
     }
     benchmark::ClobberMemory();
   }
+  st.SetComplexityN(st.range(0));
 }
 BENCHMARK_CAPTURE(BM_PathIterateOnce, iterate_elements,
-  getRandomStringInputs)->Arg(TestNumInputs);
+  getRandomStringInputs)->Range(8, TestNumInputs)->Complexity();
 
 template <class GenInputs>
 void BM_PathIterateOnceBackwards(benchmark::State &st, GenInputs gen) {
-  using namespace fs;
+  using fs::path;
   const auto in = gen(st.range(0));
   path PP;
   for (auto& Part : in)
@@ -135,4 +136,28 @@ void BM_PathIterateOnceBackwards(benchma
 BENCHMARK_CAPTURE(BM_PathIterateOnceBackwards, iterate_elements,
   getRandomStringInputs)->Arg(TestNumInputs);
 
+static fs::path getRandomPaths(int NumParts, int PathLen) {
+  fs::path Result;
+  while (NumParts--) {
+    std::string Part = getRandomString(PathLen);
+    Result /= Part;
+  }
+  return Result;
+}
+
+template <class GenInput>
+void BM_LexicallyNormal(benchmark::State &st, GenInput gen, size_t PathLen) {
+  using fs::path;
+  auto In = gen(st.range(0), PathLen);
+  benchmark::DoNotOptimize(&In);
+  while (st.KeepRunning()) {
+    benchmark::DoNotOptimize(In.lexically_normal());
+  }
+  st.SetComplexityN(st.range(0));
+}
+BENCHMARK_CAPTURE(BM_LexicallyNormal, small_path,
+  getRandomPaths, /*PathLen*/5)->RangeMultiplier(2)->Range(2, 256)->Complexity();
+BENCHMARK_CAPTURE(BM_LexicallyNormal, large_path,
+  getRandomPaths, /*PathLen*/32)->RangeMultiplier(2)->Range(2, 256)->Complexity();
+
 BENCHMARK_MAIN();

Modified: libcxx/trunk/include/experimental/filesystem
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/include/experimental/filesystem?rev=329028&r1=329027&r2=329028&view=diff
==============================================================================
--- libcxx/trunk/include/experimental/filesystem (original)
+++ libcxx/trunk/include/experimental/filesystem Mon Apr  2 16:03:41 2018
@@ -76,11 +76,11 @@
 
     // operational functions
 
-    path absolute(const path& p, const path& base=current_path());
+    path absolute(const path& p);
+    path absolute(const path& p, error_code &ec);
 
-    path canonical(const path& p, const path& base = current_path());
+    path canonical(const path& p);
     path canonical(const path& p, error_code& ec);
-    path canonical(const path& p, const path& base, error_code& ec);
 
     void copy(const path& from, const path& to);
     void copy(const path& from, const path& to, error_code& ec);
@@ -185,9 +185,17 @@
     void permissions(const path& p, perms prms, perm_options opts,
                      error_code& ec);
 
+    path proximate(const path& p, error_code& ec);
+    path proximate(const path& p, const path& base = current_path());
+    path proximate(const path& p, const path& base, error_code &ec);
+
     path read_symlink(const path& p);
     path read_symlink(const path& p, error_code& ec);
 
+    path relative(const path& p, error_code& ec);
+    path relative(const path& p, const path& base=current_path());
+    path relative(const path& p, const path& base, error_code& ec);
+
     bool remove(const path& p);
     bool remove(const path& p, error_code& ec) _NOEXCEPT;
 
@@ -211,12 +219,13 @@
     file_status  symlink_status(const path& p);
     file_status  symlink_status(const path& p, error_code& ec) _NOEXCEPT;
 
-    path system_complete(const path& p);
-    path system_complete(const path& p, error_code& ec);
-
     path temp_directory_path();
     path temp_directory_path(error_code& ec);
 
+    path weakly_canonical(path const& p);
+    path weakly_canonical(path const& p, error_code& ec);
+
+
 } } } }  // namespaces std::experimental::filesystem::v1
 
 */
@@ -796,26 +805,26 @@ public:
 
 private:
     template <class _ECharT>
-    void __append_sep_if_needed(_ECharT __first_or_null) {
-        const _ECharT __null_val = {};
-        bool __append_sep = !empty()                       &&
-                            !__is_separator(__pn_.back())  &&
-                            __first_or_null != __null_val  && // non-empty
-                            !__is_separator(__first_or_null);
-        if (__append_sep)
-            __pn_ += preferred_separator;
+    static bool __source_is_absolute(_ECharT __first_or_null) {
+        return __is_separator(__first_or_null);
     }
 
 public:
     // appends
     path& operator/=(const path& __p) {
-        _LIBCPP_ASSERT(!__p.has_root_name(),
-                      "cannot append to a path with a root name");
-        __append_sep_if_needed(__p.empty() ? char{} : __p.__pn_[0]);
+        if (__p.is_absolute()) {
+          __pn_ = __p.__pn_;
+          return *this;
+        }
+        if (has_filename())
+          __pn_ += preferred_separator;
         __pn_ += __p.native();
         return *this;
     }
 
+    // FIXME: Use _LIBCPP_DIAGNOSE_WARNING to produce a diagnostic when __src
+    // is known at compile time to be "/' since the user almost certainly intended
+    // to append a separator instead of overwriting the path with "/"
     template <class _Source>
     _LIBCPP_INLINE_VISIBILITY
     _EnableIfPathable<_Source>
@@ -828,7 +837,10 @@ public:
     append(const _Source& __src) {
         using _Traits = __is_pathable<_Source>;
         using _CVT = _PathCVT<_SourceChar<_Source>>;
-        __append_sep_if_needed(_Traits::__first_or_null(__src));
+        if (__source_is_absolute(_Traits::__first_or_null(__src)))
+          __pn_.clear();
+        else if (has_filename())
+          __pn_ += preferred_separator;
         _CVT::__append_source(__pn_, __src);
         return *this;
     }
@@ -838,10 +850,11 @@ public:
         typedef typename iterator_traits<_InputIt>::value_type _ItVal;
         static_assert(__can_convert_char<_ItVal>::value, "Must convertible");
         using _CVT = _PathCVT<_ItVal>;
-        if (__first != __last) {
-            __append_sep_if_needed(*__first);
-            _CVT::__append_range(__pn_, __first, __last);
-        }
+        if (__first != __last && __source_is_absolute(*__first))
+          __pn_.clear();
+        else if (has_filename())
+          __pn_ += preferred_separator;
+        _CVT::__append_range(__pn_, __first, __last);
         return *this;
     }
 
@@ -916,10 +929,9 @@ public:
 
     _LIBCPP_INLINE_VISIBILITY
     path& remove_filename() {
-      if (__pn_.size() == __root_path_raw().size())
-        clear();
-      else
-        __pn_ = __parent_path();
+      auto __fname = __filename();
+      if (!__fname.empty())
+        __pn_.erase(__fname.data() - __pn_.data());
       return *this;
     }
 
@@ -935,6 +947,10 @@ public:
         __pn_.swap(__rhs.__pn_);
     }
 
+    // private helper to allow reserving memory in the path
+    _LIBCPP_INLINE_VISIBILITY
+    void __reserve(size_t __s) { __pn_.reserve(__s); }
+
     // native format observers
     _LIBCPP_INLINE_VISIBILITY
     const string_type& native() const _NOEXCEPT {
@@ -1023,6 +1039,17 @@ public:
     _LIBCPP_INLINE_VISIBILITY bool is_absolute()        const { return has_root_directory(); }
     _LIBCPP_INLINE_VISIBILITY bool is_relative()        const { return !is_absolute(); }
 
+    // relative paths
+    path lexically_normal() const;
+    path lexically_relative(const path& __base) const;
+
+    _LIBCPP_INLINE_VISIBILITY path lexically_proximate(const path& __base) const {
+      path __result = this->lexically_relative(__base);
+      if (__result.native().empty())
+        return *this;
+      return __result;
+    }
+
     // iterators
     class _LIBCPP_TYPE_VIS iterator;
     typedef iterator const_iterator;
@@ -1277,7 +1304,9 @@ void __throw_filesystem_error(_Args&&...
 // operational functions
 
 _LIBCPP_FUNC_VIS
-path __canonical(const path&, const path&, error_code *__ec=nullptr);
+path __absolute(const path&, error_code *__ec=nullptr);
+_LIBCPP_FUNC_VIS
+path __canonical(const path&, error_code *__ec=nullptr);
 _LIBCPP_FUNC_VIS
 void __copy(const path& __from, const path& __to, copy_options __opt,
         error_code *__ec=nullptr);
@@ -1342,6 +1371,8 @@ _LIBCPP_FUNC_VIS
 path __system_complete(const path&, error_code *__ec=nullptr);
 _LIBCPP_FUNC_VIS
 path __temp_directory_path(error_code *__ec=nullptr);
+_LIBCPP_FUNC_VIS
+path __weakly_canonical(path const& __p, error_code *__ec=nullptr);
 
 inline _LIBCPP_INLINE_VISIBILITY
 path current_path() {
@@ -1363,24 +1394,24 @@ void current_path(const path& __p, error
     __current_path(__p, &__ec);
 }
 
-_LIBCPP_FUNC_VIS
-path absolute(const path&, const path& __p2 = current_path());
+inline _LIBCPP_INLINE_VISIBILITY
+path absolute(const path& __p) {
+  return __absolute(__p);
+}
 
 inline _LIBCPP_INLINE_VISIBILITY
-path canonical(const path& __p, const path& __base = current_path()) {
-    return __canonical(__p, __base);
+path absolute(const path& __p, error_code &__ec) {
+  return __absolute(__p, &__ec);
 }
 
 inline _LIBCPP_INLINE_VISIBILITY
-path canonical(const path& __p, error_code& __ec) {
-    path __base = __current_path(&__ec);
-    if (__ec) return {};
-    return __canonical(__p, __base, &__ec);
+path canonical(const path& __p) {
+    return __canonical(__p);
 }
 
 inline _LIBCPP_INLINE_VISIBILITY
-path canonical(const path& __p, const path& __base, error_code& __ec) {
-    return __canonical(__p, __base, &__ec);
+path canonical(const path& __p, error_code& __ec) {
+    return __canonical(__p, &__ec);
 }
 
 inline _LIBCPP_INLINE_VISIBILITY
@@ -1492,7 +1523,8 @@ void create_symlink(const path& __to, co
 }
 
 inline _LIBCPP_INLINE_VISIBILITY
-void create_symlink(const path& __to, const path& __new, error_code& __ec) _NOEXCEPT {
+void create_symlink(const path& __to, const path& __new,
+                    error_code& __ec) _NOEXCEPT {
     return __create_symlink(__to, __new, &__ec);
 }
 
@@ -1716,6 +1748,27 @@ void permissions(const path& __p, perms
 }
 
 inline _LIBCPP_INLINE_VISIBILITY
+path proximate(const path& __p, const path& __base, error_code& __ec) {
+   path __tmp = __weakly_canonical(__p, &__ec);
+  if (__ec)
+    return {};
+  path __tmp_base = __weakly_canonical(__base, &__ec);
+  if (__ec)
+    return {};
+  return __tmp.lexically_proximate(__tmp_base);
+}
+
+inline _LIBCPP_INLINE_VISIBILITY
+path proximate(const path& __p, error_code& __ec) {
+  return proximate(__p, current_path(), __ec);
+}
+
+inline _LIBCPP_INLINE_VISIBILITY
+path proximate(const path& __p, const path& __base = current_path()) {
+  return __weakly_canonical(__p).lexically_proximate(__weakly_canonical(__base));
+}
+
+inline _LIBCPP_INLINE_VISIBILITY
 path read_symlink(const path& __p) {
     return __read_symlink(__p);
 }
@@ -1725,6 +1778,29 @@ path read_symlink(const path& __p, error
     return __read_symlink(__p, &__ec);
 }
 
+
+inline _LIBCPP_INLINE_VISIBILITY
+path relative(const path& __p, const path& __base, error_code& __ec) {
+  path __tmp = __weakly_canonical(__p, &__ec);
+  if (__ec)
+    return path();
+  path __tmpbase = __weakly_canonical(__base, &__ec);
+  if (__ec)
+    return path();
+  return __tmp.lexically_relative(__tmpbase);
+}
+
+inline _LIBCPP_INLINE_VISIBILITY
+path relative(const path& __p, error_code& __ec) {
+  return relative(__p, current_path(), __ec);
+}
+
+inline _LIBCPP_INLINE_VISIBILITY
+path relative(const path& __p, const path& __base=current_path()) {
+  return __weakly_canonical(__p).lexically_relative(__weakly_canonical(__base));
+}
+
+
 inline _LIBCPP_INLINE_VISIBILITY
 bool remove(const path& __p) {
     return __remove(__p);
@@ -1796,23 +1872,23 @@ file_status symlink_status(const path& _
 }
 
 inline _LIBCPP_INLINE_VISIBILITY
-path system_complete(const path& __p) {
-    return __system_complete(__p);
+path temp_directory_path() {
+    return __temp_directory_path();
 }
 
 inline _LIBCPP_INLINE_VISIBILITY
-path system_complete(const path& __p, error_code& __ec) {
-    return __system_complete(__p, &__ec);
+path temp_directory_path(error_code& __ec) {
+    return __temp_directory_path(&__ec);
 }
 
 inline _LIBCPP_INLINE_VISIBILITY
-path temp_directory_path() {
-    return __temp_directory_path();
+path weakly_canonical(path const& __p) {
+  return __weakly_canonical(__p);
 }
 
 inline _LIBCPP_INLINE_VISIBILITY
-path temp_directory_path(error_code& __ec) {
-    return __temp_directory_path(&__ec);
+path weakly_canonical(path const& __p, error_code& __ec) {
+  return __weakly_canonical(__p, &__ec);
 }
 
 

Modified: libcxx/trunk/src/experimental/filesystem/operations.cpp
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/src/experimental/filesystem/operations.cpp?rev=329028&r1=329027&r2=329028&view=diff
==============================================================================
--- libcxx/trunk/src/experimental/filesystem/operations.cpp (original)
+++ libcxx/trunk/src/experimental/filesystem/operations.cpp Mon Apr  2 16:03:41 2018
@@ -10,8 +10,10 @@
 #include "experimental/filesystem"
 #include "iterator"
 #include "fstream"
-#include "type_traits"
 #include "random"  /* for unique_path */
+#include "string_view"
+#include "type_traits"
+#include "vector"
 #include "cstdlib"
 #include "climits"
 
@@ -57,6 +59,250 @@ _LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL_FIL
 filesystem_error::~filesystem_error() {}
 
 
+namespace { namespace parser
+{
+
+using string_view_t = path::__string_view;
+using string_view_pair = pair<string_view_t, string_view_t>;
+using PosPtr = path::value_type const*;
+
+struct PathParser {
+  enum ParserState : unsigned char {
+    // Zero is a special sentinel value used by default constructed iterators.
+    PS_BeforeBegin = 1,
+    PS_InRootName,
+    PS_InRootDir,
+    PS_InFilenames,
+    PS_InTrailingSep,
+    PS_AtEnd
+  };
+
+  const string_view_t Path;
+  string_view_t RawEntry;
+  ParserState State;
+
+private:
+  PathParser(string_view_t P, ParserState State) noexcept
+      : Path(P), State(State) {}
+
+public:
+  PathParser(string_view_t P, string_view_t E, unsigned char S)
+      : Path(P), RawEntry(E), State(static_cast<ParserState>(S)) {
+    // S cannot be '0' or PS_BeforeBegin.
+  }
+
+  static PathParser CreateBegin(string_view_t P) noexcept {
+    PathParser PP(P, PS_BeforeBegin);
+    PP.increment();
+    return PP;
+  }
+
+  static PathParser CreateEnd(string_view_t P) noexcept {
+    PathParser PP(P, PS_AtEnd);
+    return PP;
+  }
+
+  PosPtr peek() const noexcept {
+    auto TkEnd = getNextTokenStartPos();
+    auto End = getAfterBack();
+    return TkEnd == End ? nullptr : TkEnd;
+  }
+
+  void increment() noexcept {
+    const PosPtr End = getAfterBack();
+    const PosPtr Start = getNextTokenStartPos();
+    if (Start == End)
+      return makeState(PS_AtEnd);
+
+    switch (State) {
+    case PS_BeforeBegin: {
+      PosPtr TkEnd = consumeSeparator(Start, End);
+      if (TkEnd)
+        return makeState(PS_InRootDir, Start, TkEnd);
+      else
+        return makeState(PS_InFilenames, Start, consumeName(Start, End));
+    }
+    case PS_InRootDir:
+      return makeState(PS_InFilenames, Start, consumeName(Start, End));
+
+    case PS_InFilenames: {
+      PosPtr SepEnd = consumeSeparator(Start, End);
+      if (SepEnd != End) {
+        PosPtr TkEnd = consumeName(SepEnd, End);
+        if (TkEnd)
+          return makeState(PS_InFilenames, SepEnd, TkEnd);
+      }
+      return makeState(PS_InTrailingSep, Start, SepEnd);
+    }
+
+    case PS_InTrailingSep:
+      return makeState(PS_AtEnd);
+
+    case PS_InRootName:
+    case PS_AtEnd:
+      _LIBCPP_UNREACHABLE();
+    }
+  }
+
+  void decrement() noexcept {
+    const PosPtr REnd = getBeforeFront();
+    const PosPtr RStart = getCurrentTokenStartPos() - 1;
+    if (RStart == REnd) // we're decrementing the begin
+      return makeState(PS_BeforeBegin);
+
+    switch (State) {
+    case PS_AtEnd: {
+      // Try to consume a trailing separator or root directory first.
+      if (PosPtr SepEnd = consumeSeparator(RStart, REnd)) {
+        if (SepEnd == REnd)
+          return makeState(PS_InRootDir, Path.data(), RStart + 1);
+        return makeState(PS_InTrailingSep, SepEnd + 1, RStart + 1);
+      } else {
+        PosPtr TkStart = consumeName(RStart, REnd);
+        return makeState(PS_InFilenames, TkStart + 1, RStart + 1);
+      }
+    }
+    case PS_InTrailingSep:
+      return makeState(PS_InFilenames, consumeName(RStart, REnd) + 1, RStart + 1);
+    case PS_InFilenames: {
+      PosPtr SepEnd = consumeSeparator(RStart, REnd);
+      if (SepEnd == REnd)
+        return makeState(PS_InRootDir, Path.data(), RStart + 1);
+      PosPtr TkEnd = consumeName(SepEnd, REnd);
+      return makeState(PS_InFilenames, TkEnd + 1, SepEnd + 1);
+    }
+    case PS_InRootDir:
+      // return makeState(PS_InRootName, Path.data(), RStart + 1);
+    case PS_InRootName:
+    case PS_BeforeBegin:
+      _LIBCPP_UNREACHABLE();
+    }
+  }
+
+  /// \brief Return a view with the "preferred representation" of the current
+  ///   element. For example trailing separators are represented as a '.'
+  string_view_t operator*() const noexcept {
+    switch (State) {
+    case PS_BeforeBegin:
+    case PS_AtEnd:
+      return "";
+    case PS_InRootDir:
+      return "/";
+    case PS_InTrailingSep:
+      return "";
+    case PS_InRootName:
+    case PS_InFilenames:
+      return RawEntry;
+    }
+    _LIBCPP_UNREACHABLE();
+  }
+
+  explicit operator bool() const noexcept {
+    return State != PS_BeforeBegin && State != PS_AtEnd;
+  }
+
+  PathParser& operator++() noexcept {
+    increment();
+    return *this;
+  }
+
+  PathParser& operator--() noexcept {
+    decrement();
+    return *this;
+  }
+
+  bool inRootPath() const noexcept {
+    return State == PS_InRootDir || State == PS_InRootName;
+  }
+
+private:
+  void makeState(ParserState NewState, PosPtr Start, PosPtr End) noexcept {
+    State = NewState;
+    RawEntry = string_view_t(Start, End - Start);
+  }
+  void makeState(ParserState NewState) noexcept {
+    State = NewState;
+    RawEntry = {};
+  }
+
+  PosPtr getAfterBack() const noexcept {
+    return Path.data() + Path.size();
+  }
+
+  PosPtr getBeforeFront() const noexcept {
+    return Path.data() - 1;
+  }
+
+  /// \brief Return a pointer to the first character after the currently
+  ///   lexed element.
+  PosPtr getNextTokenStartPos() const noexcept {
+    switch (State) {
+    case PS_BeforeBegin:
+      return Path.data();
+    case PS_InRootName:
+    case PS_InRootDir:
+    case PS_InFilenames:
+      return &RawEntry.back() + 1;
+    case PS_InTrailingSep:
+    case PS_AtEnd:
+      return getAfterBack();
+    }
+    _LIBCPP_UNREACHABLE();
+  }
+
+  /// \brief Return a pointer to the first character in the currently lexed
+  ///   element.
+  PosPtr getCurrentTokenStartPos() const noexcept {
+    switch (State) {
+    case PS_BeforeBegin:
+    case PS_InRootName:
+      return &Path.front();
+    case PS_InRootDir:
+    case PS_InFilenames:
+    case PS_InTrailingSep:
+      return &RawEntry.front();
+    case PS_AtEnd:
+      return &Path.back() + 1;
+    }
+    _LIBCPP_UNREACHABLE();
+  }
+
+  PosPtr consumeSeparator(PosPtr P, PosPtr End) const noexcept {
+    if (P == End || *P != '/')
+      return nullptr;
+    const int Inc = P < End ? 1 : -1;
+    P += Inc;
+    while (P != End && *P == '/')
+      P += Inc;
+    return P;
+  }
+
+  PosPtr consumeName(PosPtr P, PosPtr End) const noexcept {
+    if (P == End || *P == '/')
+      return nullptr;
+    const int Inc = P < End ? 1 : -1;
+    P += Inc;
+    while (P != End && *P != '/')
+      P += Inc;
+    return P;
+  }
+};
+
+string_view_pair separate_filename(string_view_t const & s) {
+    if (s == "." || s == ".." || s.empty()) return string_view_pair{s, ""};
+    auto pos = s.find_last_of('.');
+    if (pos == string_view_t::npos || pos == 0)
+        return string_view_pair{s, string_view_t{}};
+    return string_view_pair{s.substr(0, pos), s.substr(pos)};
+}
+
+string_view_t createView(PosPtr S, PosPtr E) noexcept {
+  return {S, static_cast<size_t>(E - S) + 1};
+}
+
+}} // namespace parser
+
+
 //                       POSIX HELPERS
 
 namespace detail { namespace  {
@@ -186,14 +432,33 @@ bool copy_file_impl(const path& from, co
 }} // end namespace detail
 
 using detail::set_or_throw;
+using parser::string_view_t;
+using parser::PathParser;
+using parser::createView;
+
+static path __do_absolute(const path& p, path *cwd, std::error_code *ec) {
+  if (ec) ec->clear();
+    if (p.is_absolute())
+      return p;
+    *cwd = __current_path(ec);
+    if (ec && *ec)
+      return {};
+    return (*cwd) / p;
+}
+
+path __absolute(const path& p, std::error_code *ec) {
+    path cwd;
+    return __do_absolute(p, &cwd, ec);
+}
 
-path __canonical(path const & orig_p, const path& base, std::error_code *ec)
+path __canonical(path const & orig_p, std::error_code *ec)
 {
-    path p = absolute(orig_p, base);
+    path cwd;
+    path p = __do_absolute(orig_p, &cwd, ec);
     char buff[PATH_MAX + 1];
     char *ret;
     if ((ret = ::realpath(p.c_str(), buff)) == nullptr) {
-        set_or_throw(ec, "canonical", orig_p, base);
+        set_or_throw(ec, "canonical", orig_p, cwd);
         return {};
     }
     if (ec) ec->clear();
@@ -791,11 +1056,6 @@ file_status __symlink_status(const path&
     return detail::posix_lstat(p, ec);
 }
 
-path __system_complete(const path& p, std::error_code *ec) {
-    if (ec) ec->clear();
-    return absolute(p, current_path());
-}
-
 path __temp_directory_path(std::error_code* ec) {
   const char* env_paths[] = {"TMPDIR", "TMP", "TEMP", "TEMPDIR"};
   const char* ret = nullptr;
@@ -820,34 +1080,393 @@ path __temp_directory_path(std::error_co
   return p;
 }
 
-// An absolute path is composed according to the table in [fs.op.absolute].
-path absolute(const path& p, const path& base) {
-    auto root_name = p.root_name();
-    auto root_dir = p.root_directory();
 
-    if (!root_name.empty() && !root_dir.empty())
-      return p;
+path __weakly_canonical(const path& p, std::error_code *ec) {
+  if (p.empty())
+    return __canonical("", ec);
+
+  path result;
+  path tmp;
+  tmp.__reserve(p.native().size());
+  auto PP = PathParser::CreateEnd(p.native());
+  --PP;
+  std::vector<string_view_t> DNEParts;
 
-    auto abs_base = base.is_absolute() ? base : absolute(base);
+  while (PP.State != PathParser::PS_BeforeBegin) {
+    tmp.assign(createView(p.native().data(), &PP.RawEntry.back()));
+    std::error_code m_ec;
+    file_status st = __status(tmp, &m_ec);
+    if (!status_known(st)) {
+      set_or_throw(m_ec, ec, "weakly_canonical", p);
+      return {};
+    } else if (exists(st)) {
+      result = __canonical(tmp, ec);
+      break;
+    }
+    DNEParts.push_back(*PP);
+    --PP;
+  }
+  if (PP.State == PathParser::PS_BeforeBegin)
+    result = __canonical("", ec);
+  if (ec) ec->clear();
+  if (DNEParts.empty())
+    return result;
+  for (auto It=DNEParts.rbegin(); It != DNEParts.rend(); ++It)
+    result /= *It;
+  return result.lexically_normal();
+}
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+//                            path definitions
+///////////////////////////////////////////////////////////////////////////////
+
+constexpr path::value_type path::preferred_separator;
+
+path & path::replace_extension(path const & replacement)
+{
+    path p = extension();
+    if (not p.empty()) {
+      __pn_.erase(__pn_.size() - p.native().size());
+    }
+    if (!replacement.empty()) {
+        if (replacement.native()[0] != '.') {
+            __pn_ += ".";
+        }
+        __pn_.append(replacement.__pn_);
+    }
+    return *this;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// path.decompose
+
+string_view_t path::__root_name() const
+{
+    auto PP = PathParser::CreateBegin(__pn_);
+    if (PP.State == PathParser::PS_InRootName)
+      return *PP;
+    return {};
+}
 
-    /* !has_root_name && !has_root_dir */
-    if (root_name.empty() && root_dir.empty())
+string_view_t path::__root_directory() const
+{
+    auto PP = PathParser::CreateBegin(__pn_);
+    if (PP.State == PathParser::PS_InRootName)
+      ++PP;
+    if (PP.State == PathParser::PS_InRootDir)
+      return *PP;
+    return {};
+}
+
+string_view_t path::__root_path_raw() const
+{
+    auto PP = PathParser::CreateBegin(__pn_);
+    if (PP.State == PathParser::PS_InRootName) {
+      auto NextCh = PP.peek();
+      if (NextCh && *NextCh == '/') {
+        ++PP;
+        return createView(__pn_.data(), &PP.RawEntry.back());
+      }
+      return PP.RawEntry;
+    }
+    if (PP.State == PathParser::PS_InRootDir)
+      return *PP;
+    return {};
+}
+
+static bool ConsumeRootDir(PathParser* PP) {
+  while (PP->State <= PathParser::PS_InRootDir)
+    ++(*PP);
+  return PP->State == PathParser::PS_AtEnd;
+}
+
+string_view_t path::__relative_path() const
+{
+    auto PP = PathParser::CreateBegin(__pn_);
+    if (ConsumeRootDir(&PP))
+      return {};
+    return createView(PP.RawEntry.data(), &__pn_.back());
+}
+
+string_view_t path::__parent_path() const
+{
+    if (empty())
+      return {};
+    // Determine if we have a root path but not a relative path. In that case
+    // return *this.
     {
-      return abs_base / p;
+      auto PP = PathParser::CreateBegin(__pn_);
+      if (ConsumeRootDir(&PP))
+        return __pn_;
     }
-    else if (!root_name.empty()) /* has_root_name && !has_root_dir */
+    // Otherwise remove a single element from the end of the path, and return
+    // a string representing that path
     {
-      return  root_name / abs_base.root_directory()
-              /
-              abs_base.relative_path() / p.relative_path();
+      auto PP = PathParser::CreateEnd(__pn_);
+      --PP;
+      if (PP.RawEntry.data() == __pn_.data())
+        return {};
+      --PP;
+      return createView(__pn_.data(), &PP.RawEntry.back());
     }
-    else /* !has_root_name && has_root_dir */
+}
+
+string_view_t path::__filename() const
+{
+    if (empty()) return {};
     {
-      if (abs_base.has_root_name())
-        return abs_base.root_name() / p;
-      // else p is absolute,  return outside of block
+      PathParser PP = PathParser::CreateBegin(__pn_);
+      if (ConsumeRootDir(&PP))
+        return {};
     }
-    return p;
+    return *(--PathParser::CreateEnd(__pn_));
 }
 
+string_view_t path::__stem() const
+{
+    return parser::separate_filename(__filename()).first;
+}
+
+string_view_t path::__extension() const
+{
+    return parser::separate_filename(__filename()).second;
+}
+
+////////////////////////////////////////////////////////////////////////////
+// path.gen
+
+
+enum PathPartKind : unsigned char {
+  PK_None,
+  PK_RootSep,
+  PK_Filename,
+  PK_Dot,
+  PK_DotDot,
+  PK_TrailingSep
+};
+
+static PathPartKind ClassifyPathPart(string_view_t Part) {
+  if (Part.empty())
+    return PK_TrailingSep;
+  if (Part == ".")
+    return PK_Dot;
+  if (Part == "..")
+    return PK_DotDot;
+  if (Part == "/")
+    return PK_RootSep;
+  return PK_Filename;
+}
+
+path path::lexically_normal() const {
+  if (__pn_.empty())
+    return *this;
+
+  using PartKindPair = std::pair<string_view_t, PathPartKind>;
+  std::vector<PartKindPair> Parts;
+  // Guess as to how many elements the path has to avoid reallocating.
+  Parts.reserve(32);
+
+  // Track the total size of the parts as we collect them. This allows the
+  // resulting path to reserve the correct amount of memory.
+  size_t NewPathSize = 0;
+  auto AddPart = [&](PathPartKind K, string_view_t P) {
+    NewPathSize += P.size();
+    Parts.emplace_back(P, K);
+  };
+  auto LastPartKind = [&]() {
+    if (Parts.empty())
+      return PK_None;
+    return Parts.back().second;
+  };
+
+  bool MaybeNeedTrailingSep = false;
+  // Build a stack containing the remaining elements of the path, popping off
+  // elements which occur before a '..' entry.
+  for (auto PP = PathParser::CreateBegin(__pn_); PP; ++PP) {
+    auto Part = *PP;
+    PathPartKind Kind = ClassifyPathPart(Part);
+    switch (Kind) {
+    case PK_Filename:
+    case PK_RootSep: {
+      // Add all non-dot and non-dot-dot elements to the stack of elements.
+      AddPart(Kind, Part);
+      MaybeNeedTrailingSep = false;
+      break;
+    }
+    case PK_DotDot: {
+      // Only push a ".." element if there are no elements preceding the "..",
+      // or if the preceding element is itself "..".
+      auto LastKind = LastPartKind();
+      if (LastKind == PK_Filename) {
+        NewPathSize -= Parts.back().first.size();
+        Parts.pop_back();
+      } else if (LastKind != PK_RootSep)
+        AddPart(PK_DotDot, "..");
+      MaybeNeedTrailingSep = LastKind == PK_Filename;
+      break;
+    }
+    case PK_Dot:
+    case PK_TrailingSep: {
+      MaybeNeedTrailingSep = true;
+      break;
+    }
+    case PK_None:
+      _LIBCPP_UNREACHABLE();
+    }
+  }
+  // [fs.path.generic]p6.8: If the path is empty, add a dot.
+  if (Parts.empty())
+     return ".";
+
+  // [fs.path.generic]p6.7: If the last filename is dot-dot, remove any
+  // trailing directory-separator.
+  bool NeedTrailingSep = MaybeNeedTrailingSep && LastPartKind() == PK_Filename;
+
+  path Result;
+  Result.__pn_.reserve(Parts.size() + NewPathSize + NeedTrailingSep);
+  for (auto &PK : Parts)
+    Result /= PK.first;
+
+  if (NeedTrailingSep)
+    Result /= "";
+
+  return Result;
+}
+
+static int DetermineLexicalElementCount(PathParser PP) {
+  int Count = 0;
+  for (; PP; ++PP) {
+    auto Elem = *PP;
+    if (Elem == "..")
+      --Count;
+    else if (Elem != ".")
+      ++Count;
+  }
+  return Count;
+}
+
+path path::lexically_relative(const path& base) const {
+  { // perform root-name/root-directory mismatch checks
+    auto PP = PathParser::CreateBegin(__pn_);
+    auto PPBase = PathParser::CreateBegin(base.__pn_);
+    auto CheckIterMismatchAtBase = [&]() {
+        return PP.State != PPBase.State && (
+            PP.inRootPath() || PPBase.inRootPath());
+    };
+    if (PP.State == PathParser::PS_InRootName &&
+        PPBase.State == PathParser::PS_InRootName) {
+      if (*PP != *PPBase)
+        return {};
+    } else if (CheckIterMismatchAtBase())
+      return {};
+
+    if (PP.inRootPath()) ++PP;
+    if (PPBase.inRootPath()) ++PPBase;
+    if (CheckIterMismatchAtBase())
+      return {};
+  }
+
+  // Find the first mismatching element
+  auto PP = PathParser::CreateBegin(__pn_);
+  auto PPBase = PathParser::CreateBegin(base.__pn_);
+  while (PP && PPBase && PP.State == PPBase.State &&
+         *PP == *PPBase) {
+    ++PP;
+    ++PPBase;
+  }
+
+  // If there is no mismatch, return ".".
+  if (!PP && !PPBase)
+    return ".";
+
+  // Otherwise, determine the number of elements, 'n', which are not dot or
+  // dot-dot minus the number of dot-dot elements.
+  int ElemCount = DetermineLexicalElementCount(PPBase);
+  if (ElemCount < 0)
+    return {};
+
+  // return a path constructed with 'n' dot-dot elements, followed by the the
+  // elements of '*this' after the mismatch.
+  path Result;
+  // FIXME: Reserve enough room in Result that it won't have to re-allocate.
+  while (ElemCount--)
+    Result /= "..";
+  for (; PP; ++PP)
+    Result /= *PP;
+  return Result;
+}
+
+////////////////////////////////////////////////////////////////////////////
+// path.comparisons
+int path::__compare(string_view_t __s) const {
+    auto PP = PathParser::CreateBegin(__pn_);
+    auto PP2 = PathParser::CreateBegin(__s);
+    while (PP && PP2) {
+        int res = (*PP).compare(*PP2);
+        if (res != 0) return res;
+        ++PP; ++PP2;
+    }
+    if (PP.State == PP2.State && !PP)
+        return 0;
+    if (!PP)
+        return -1;
+    return 1;
+}
+
+////////////////////////////////////////////////////////////////////////////
+// path.nonmembers
+size_t hash_value(const path& __p) noexcept {
+  auto PP = PathParser::CreateBegin(__p.native());
+  size_t hash_value = 0;
+  std::hash<string_view_t> hasher;
+  while (PP) {
+    hash_value = __hash_combine(hash_value, hasher(*PP));
+    ++PP;
+  }
+  return hash_value;
+}
+
+////////////////////////////////////////////////////////////////////////////
+// path.itr
+path::iterator path::begin() const
+{
+    auto PP = PathParser::CreateBegin(__pn_);
+    iterator it;
+    it.__path_ptr_ = this;
+    it.__state_ = PP.State;
+    it.__entry_ = PP.RawEntry;
+    it.__stashed_elem_.__assign_view(*PP);
+    return it;
+}
+
+path::iterator path::end() const
+{
+    iterator it{};
+    it.__state_ = PathParser::PS_AtEnd;
+    it.__path_ptr_ = this;
+    return it;
+}
+
+path::iterator& path::iterator::__increment() {
+  static_assert(__at_end == PathParser::PS_AtEnd, "");
+  PathParser PP(__path_ptr_->native(), __entry_, __state_);
+  ++PP;
+  __state_ = PP.State;
+  __entry_ = PP.RawEntry;
+  __stashed_elem_.__assign_view(*PP);
+  return *this;
+}
+
+path::iterator& path::iterator::__decrement() {
+  PathParser PP(__path_ptr_->native(), __entry_, __state_);
+  --PP;
+  __state_ = PP.State;
+  __entry_ = PP.RawEntry;
+  __stashed_elem_.__assign_view(*PP);
+  return *this;
+}
+
+
 _LIBCPP_END_NAMESPACE_EXPERIMENTAL_FILESYSTEM

Removed: libcxx/trunk/src/experimental/filesystem/path.cpp
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/src/experimental/filesystem/path.cpp?rev=329027&view=auto
==============================================================================
--- libcxx/trunk/src/experimental/filesystem/path.cpp (original)
+++ libcxx/trunk/src/experimental/filesystem/path.cpp (removed)
@@ -1,448 +0,0 @@
-//===--------------------- filesystem/path.cpp ----------------------------===//
-//
-//                     The LLVM Compiler Infrastructure
-//
-// This file is dual licensed under the MIT and the University of Illinois Open
-// Source Licenses. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-#include "experimental/filesystem"
-#include "string_view"
-#include "utility"
-
-namespace { namespace parser
-{
-using namespace std;
-using namespace std::experimental::filesystem;
-
-using string_view_t = path::__string_view;
-using string_view_pair = pair<string_view_t, string_view_t>;
-using PosPtr = path::value_type const*;
-
-struct PathParser {
-  enum ParserState : unsigned char {
-    // Zero is a special sentinel value used by default constructed iterators.
-    PS_BeforeBegin = 1,
-    PS_InRootName,
-    PS_InRootDir,
-    PS_InFilenames,
-    PS_InTrailingSep,
-    PS_AtEnd
-  };
-
-  const string_view_t Path;
-  string_view_t RawEntry;
-  ParserState State;
-
-private:
-  PathParser(string_view_t P, ParserState State) noexcept
-      : Path(P), State(State) {}
-
-public:
-  PathParser(string_view_t P, string_view_t E, unsigned char S)
-      : Path(P), RawEntry(E), State(static_cast<ParserState>(S)) {
-    // S cannot be '0' or PS_BeforeBegin.
-  }
-
-  static PathParser CreateBegin(string_view_t P) noexcept {
-    PathParser PP(P, PS_BeforeBegin);
-    PP.increment();
-    return PP;
-  }
-
-  static PathParser CreateEnd(string_view_t P) noexcept {
-    PathParser PP(P, PS_AtEnd);
-    return PP;
-  }
-
-  PosPtr peek() const noexcept {
-    auto TkEnd = getNextTokenStartPos();
-    auto End = getAfterBack();
-    return TkEnd == End ? nullptr : TkEnd;
-  }
-
-  void increment() noexcept {
-    const PosPtr End = getAfterBack();
-    const PosPtr Start = getNextTokenStartPos();
-    if (Start == End)
-      return makeState(PS_AtEnd);
-
-    switch (State) {
-    case PS_BeforeBegin: {
-      PosPtr TkEnd = consumeSeparator(Start, End);
-      // If we consumed exactly two separators we have a root name.
-      if (TkEnd && TkEnd == Start + 2) {
-        // FIXME Do we need to consume a name or is '//' a root name on its own?
-        // what about '//.', '//..', '//...'?
-        auto NameEnd = consumeName(TkEnd, End);
-        if (NameEnd)
-          TkEnd = NameEnd;
-        return makeState(PS_InRootName, Start, TkEnd);
-      }
-      else if (TkEnd)
-        return makeState(PS_InRootDir, Start, TkEnd);
-      else
-        return makeState(PS_InFilenames, Start, consumeName(Start, End));
-    }
-
-    case PS_InRootName:
-      return makeState(PS_InRootDir, Start, consumeSeparator(Start, End));
-    case PS_InRootDir:
-      return makeState(PS_InFilenames, Start, consumeName(Start, End));
-
-    case PS_InFilenames: {
-      PosPtr SepEnd = consumeSeparator(Start, End);
-      if (SepEnd != End) {
-        PosPtr TkEnd = consumeName(SepEnd, End);
-        if (TkEnd)
-          return makeState(PS_InFilenames, SepEnd, TkEnd);
-      }
-      return makeState(PS_InTrailingSep, Start, SepEnd);
-    }
-
-    case PS_InTrailingSep:
-      return makeState(PS_AtEnd);
-
-    case PS_AtEnd:
-      _LIBCPP_UNREACHABLE();
-    }
-  }
-
-  void decrement() noexcept {
-    const PosPtr REnd = getBeforeFront();
-    const PosPtr RStart = getCurrentTokenStartPos() - 1;
-
-    switch (State) {
-    case PS_AtEnd: {
-      // Try to consume a trailing separator or root directory first.
-      if (PosPtr SepEnd = consumeSeparator(RStart, REnd)) {
-        if (SepEnd == REnd)
-          return makeState((RStart == REnd + 2) ? PS_InRootName : PS_InRootDir,
-                           Path.data(), RStart + 1);
-        // Check if we're seeing the root directory separator
-        auto PP = CreateBegin(Path);
-        bool InRootDir = PP.State == PS_InRootName &&
-            &PP.RawEntry.back() == SepEnd;
-        return makeState(InRootDir ? PS_InRootDir : PS_InTrailingSep,
-                         SepEnd + 1, RStart + 1);
-      } else {
-        PosPtr TkStart = consumeName(RStart, REnd);
-        if (TkStart == REnd + 2 && consumeSeparator(TkStart, REnd) == REnd)
-          return makeState(PS_InRootName, Path.data(), RStart + 1);
-        else
-          return makeState(PS_InFilenames, TkStart + 1, RStart + 1);
-      }
-    }
-    case PS_InTrailingSep:
-      return makeState(PS_InFilenames, consumeName(RStart, REnd) + 1, RStart + 1);
-    case PS_InFilenames: {
-      PosPtr SepEnd = consumeSeparator(RStart, REnd);
-      if (SepEnd == REnd)
-        return makeState((RStart == REnd + 2) ? PS_InRootName : PS_InRootDir,
-                         Path.data(), RStart + 1);
-      PosPtr TkEnd = consumeName(SepEnd, REnd);
-      if (TkEnd == REnd + 2 && consumeSeparator(TkEnd, REnd) == REnd)
-        return makeState(PS_InRootDir, SepEnd + 1, RStart + 1);
-      return makeState(PS_InFilenames, TkEnd + 1, SepEnd + 1);
-    }
-    case PS_InRootDir:
-      return makeState(PS_InRootName, Path.data(), RStart + 1);
-    case PS_InRootName:
-    case PS_BeforeBegin:
-      _LIBCPP_UNREACHABLE();
-    }
-  }
-
-  /// \brief Return a view with the "preferred representation" of the current
-  ///   element. For example trailing separators are represented as a '.'
-  string_view_t operator*() const noexcept {
-    switch (State) {
-    case PS_BeforeBegin:
-    case PS_AtEnd:
-      return "";
-    case PS_InRootDir:
-      return "/";
-    case PS_InTrailingSep:
-      return ".";
-    case PS_InRootName:
-    case PS_InFilenames:
-      return RawEntry;
-    }
-    _LIBCPP_UNREACHABLE();
-  }
-
-  explicit operator bool() const noexcept {
-    return State != PS_BeforeBegin && State != PS_AtEnd;
-  }
-
-  PathParser& operator++() noexcept {
-    increment();
-    return *this;
-  }
-
-  PathParser& operator--() noexcept {
-    decrement();
-    return *this;
-  }
-
-private:
-  void makeState(ParserState NewState, PosPtr Start, PosPtr End) noexcept {
-    State = NewState;
-    RawEntry = string_view_t(Start, End - Start);
-  }
-  void makeState(ParserState NewState) noexcept {
-    State = NewState;
-    RawEntry = {};
-  }
-
-  PosPtr getAfterBack() const noexcept {
-    return Path.data() + Path.size();
-  }
-
-  PosPtr getBeforeFront() const noexcept {
-    return Path.data() - 1;
-  }
-
-  /// \brief Return a pointer to the first character after the currently
-  ///   lexed element.
-  PosPtr getNextTokenStartPos() const noexcept {
-    switch (State) {
-    case PS_BeforeBegin:
-      return Path.data();
-    case PS_InRootName:
-    case PS_InRootDir:
-    case PS_InFilenames:
-      return &RawEntry.back() + 1;
-    case PS_InTrailingSep:
-    case PS_AtEnd:
-      return getAfterBack();
-    }
-    _LIBCPP_UNREACHABLE();
-  }
-
-  /// \brief Return a pointer to the first character in the currently lexed
-  ///   element.
-  PosPtr getCurrentTokenStartPos() const noexcept {
-    switch (State) {
-    case PS_BeforeBegin:
-    case PS_InRootName:
-      return &Path.front();
-    case PS_InRootDir:
-    case PS_InFilenames:
-    case PS_InTrailingSep:
-      return &RawEntry.front();
-    case PS_AtEnd:
-      return &Path.back() + 1;
-    }
-    _LIBCPP_UNREACHABLE();
-  }
-
-  PosPtr consumeSeparator(PosPtr P, PosPtr End) const noexcept {
-    if (P == End || *P != '/')
-      return nullptr;
-    const int Inc = P < End ? 1 : -1;
-    P += Inc;
-    while (P != End && *P == '/')
-      P += Inc;
-    return P;
-  }
-
-  PosPtr consumeName(PosPtr P, PosPtr End) const noexcept {
-    if (P == End || *P == '/')
-      return nullptr;
-    const int Inc = P < End ? 1 : -1;
-    P += Inc;
-    while (P != End && *P != '/')
-      P += Inc;
-    return P;
-  }
-};
-
-string_view_pair separate_filename(string_view_t const & s) {
-    if (s == "." || s == ".." || s.empty()) return string_view_pair{s, ""};
-    auto pos = s.find_last_of('.');
-    if (pos == string_view_t::npos)
-        return string_view_pair{s, string_view_t{}};
-    return string_view_pair{s.substr(0, pos), s.substr(pos)};
-}
-
-string_view_t createView(PosPtr S, PosPtr E) noexcept {
-  return {S, static_cast<size_t>(E - S) + 1};
-}
-
-}} // namespace parser
-
-_LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL_FILESYSTEM
-
-using parser::string_view_t;
-using parser::string_view_pair;
-using parser::PathParser;
-using parser::createView;
-
-///////////////////////////////////////////////////////////////////////////////
-//                            path definitions
-///////////////////////////////////////////////////////////////////////////////
-
-constexpr path::value_type path::preferred_separator;
-
-path & path::replace_extension(path const & replacement)
-{
-    path p = extension();
-    if (not p.empty()) {
-      __pn_.erase(__pn_.size() - p.native().size());
-    }
-    if (!replacement.empty()) {
-        if (replacement.native()[0] != '.') {
-            __pn_ += ".";
-        }
-        __pn_.append(replacement.__pn_);
-    }
-    return *this;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// path.decompose
-
-string_view_t path::__root_name() const
-{
-    auto PP = PathParser::CreateBegin(__pn_);
-    if (PP.State == PathParser::PS_InRootName)
-      return *PP;
-    return {};
-}
-
-string_view_t path::__root_directory() const
-{
-    auto PP = PathParser::CreateBegin(__pn_);
-    if (PP.State == PathParser::PS_InRootName)
-      ++PP;
-    if (PP.State == PathParser::PS_InRootDir)
-      return *PP;
-    return {};
-}
-
-string_view_t path::__root_path_raw() const
-{
-    auto PP = PathParser::CreateBegin(__pn_);
-    if (PP.State == PathParser::PS_InRootName) {
-      auto NextCh = PP.peek();
-      if (NextCh && *NextCh == '/') {
-        ++PP;
-        return createView(__pn_.data(), &PP.RawEntry.back());
-      }
-      return PP.RawEntry;
-    }
-    if (PP.State == PathParser::PS_InRootDir)
-      return *PP;
-    return {};
-}
-
-string_view_t path::__relative_path() const
-{
-    auto PP = PathParser::CreateBegin(__pn_);
-    while (PP.State <= PathParser::PS_InRootDir)
-      ++PP;
-    if (PP.State == PathParser::PS_AtEnd)
-      return {};
-    return createView(PP.RawEntry.data(), &__pn_.back());
-}
-
-string_view_t path::__parent_path() const
-{
-    if (empty())
-      return {};
-    auto PP = PathParser::CreateEnd(__pn_);
-    --PP;
-    if (PP.RawEntry.data() == __pn_.data())
-      return {};
-    --PP;
-    return createView(__pn_.data(), &PP.RawEntry.back());
-}
-
-string_view_t path::__filename() const
-{
-    if (empty()) return {};
-    return *(--PathParser::CreateEnd(__pn_));
-}
-
-string_view_t path::__stem() const
-{
-    return parser::separate_filename(__filename()).first;
-}
-
-string_view_t path::__extension() const
-{
-    return parser::separate_filename(__filename()).second;
-}
-
-////////////////////////////////////////////////////////////////////////////
-// path.comparisons
-int path::__compare(string_view_t __s) const {
-    auto PP = PathParser::CreateBegin(__pn_);
-    auto PP2 = PathParser::CreateBegin(__s);
-    while (PP && PP2) {
-        int res = (*PP).compare(*PP2);
-        if (res != 0) return res;
-        ++PP; ++PP2;
-    }
-    if (PP.State == PP2.State && PP.State == PathParser::PS_AtEnd)
-        return 0;
-    if (PP.State == PathParser::PS_AtEnd)
-        return -1;
-    return 1;
-}
-
-////////////////////////////////////////////////////////////////////////////
-// path.nonmembers
-size_t hash_value(const path& __p) noexcept {
-  auto PP = PathParser::CreateBegin(__p.native());
-  size_t hash_value = 0;
-  std::hash<string_view_t> hasher;
-  while (PP) {
-    hash_value = __hash_combine(hash_value, hasher(*PP));
-    ++PP;
-  }
-  return hash_value;
-}
-
-////////////////////////////////////////////////////////////////////////////
-// path.itr
-path::iterator path::begin() const
-{
-    auto PP = PathParser::CreateBegin(__pn_);
-    iterator it;
-    it.__path_ptr_ = this;
-    it.__state_ = PP.State;
-    it.__entry_ = PP.RawEntry;
-    it.__stashed_elem_.__assign_view(*PP);
-    return it;
-}
-
-path::iterator path::end() const
-{
-    iterator it{};
-    it.__state_ = PathParser::PS_AtEnd;
-    it.__path_ptr_ = this;
-    return it;
-}
-
-path::iterator& path::iterator::__increment() {
-  static_assert(__at_end == PathParser::PS_AtEnd, "");
-  PathParser PP(__path_ptr_->native(), __entry_, __state_);
-  ++PP;
-  __state_ = PP.State;
-  __entry_ = PP.RawEntry;
-  __stashed_elem_.__assign_view(*PP);
-  return *this;
-}
-
-path::iterator& path::iterator::__decrement() {
-  PathParser PP(__path_ptr_->native(), __entry_, __state_);
-  --PP;
-  __state_ = PP.State;
-  __entry_ = PP.RawEntry;
-  __stashed_elem_.__assign_view(*PP);
-  return *this;
-}
-
-_LIBCPP_END_NAMESPACE_EXPERIMENTAL_FILESYSTEM

Removed: libcxx/trunk/test/libcxx/experimental/filesystem/class.path/path.member/path.append.pass.cpp
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/libcxx/experimental/filesystem/class.path/path.member/path.append.pass.cpp?rev=329027&view=auto
==============================================================================
--- libcxx/trunk/test/libcxx/experimental/filesystem/class.path/path.member/path.append.pass.cpp (original)
+++ libcxx/trunk/test/libcxx/experimental/filesystem/class.path/path.member/path.append.pass.cpp (removed)
@@ -1,70 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-//                     The LLVM Compiler Infrastructure
-//
-// This file is dual licensed under the MIT and the University of Illinois Open
-// Source Licenses. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-
-// UNSUPPORTED: c++98, c++03
-
-// <experimental/filesystem>
-
-// class path
-
-// path& operator/=(path const&)
-// path operator/(path const&, path const&)
-
-
-#define _LIBCPP_DEBUG 0
-#define _LIBCPP_ASSERT(x, m) ((x) ? (void)0 : (void)::AssertCount++)
-int AssertCount = 0;
-
-#include <experimental/filesystem>
-#include <type_traits>
-#include <string_view>
-#include <cassert>
-
-#include "test_macros.h"
-#include "test_iterators.h"
-#include "count_new.hpp"
-#include "filesystem_test_helper.hpp"
-
-namespace fs = std::experimental::filesystem;
-
-int main()
-{
-  using namespace fs;
-  {
-    path lhs("//foo");
-    path rhs("/bar");
-    assert(AssertCount == 0);
-    lhs /= rhs;
-    assert(AssertCount == 0);
-  }
-  {
-    path lhs("//foo");
-    path rhs("/bar");
-    assert(AssertCount == 0);
-    (void)(lhs / rhs);
-    assert(AssertCount == 0);
-  }
-  {
-    path lhs("//foo");
-    path rhs("//bar");
-    assert(AssertCount == 0);
-    lhs /= rhs;
-    assert(AssertCount == 1);
-    AssertCount = 0;
-  }
-  {
-    path lhs("//foo");
-    path rhs("//bar");
-    assert(AssertCount == 0);
-    (void)(lhs / rhs);
-    assert(AssertCount == 1);
-  }
-  // FIXME The same error is not diagnosed for the append(Source) and
-  // append(It, It) overloads.
-}

Modified: libcxx/trunk/test/std/experimental/filesystem/class.path/path.itr/iterator.pass.cpp
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/std/experimental/filesystem/class.path/path.itr/iterator.pass.cpp?rev=329028&r1=329027&r2=329028&view=diff
==============================================================================
--- libcxx/trunk/test/std/experimental/filesystem/class.path/path.itr/iterator.pass.cpp (original)
+++ libcxx/trunk/test/std/experimental/filesystem/class.path/path.itr/iterator.pass.cpp Mon Apr  2 16:03:41 2018
@@ -83,14 +83,14 @@ void checkBeginEndBasic() {
   }
   {
     path p("//root_name//first_dir////second_dir");
-    const path expect[] = {"//root_name", "/", "first_dir", "second_dir"};
+    const path expect[] = {"/", "root_name", "first_dir", "second_dir"};
     assert(checkCollectionsEqual(p.begin(), p.end(), std::begin(expect), std::end(expect)));
     assert(checkCollectionsEqualBackwards(p.begin(), p.end(), std::begin(expect), std::end(expect)));
 
   }
   {
     path p("////foo/bar/baz///");
-    const path expect[] = {"/", "foo", "bar", "baz", "."};
+    const path expect[] = {"/", "foo", "bar", "baz", ""};
     assert(checkCollectionsEqual(p.begin(), p.end(), std::begin(expect), std::end(expect)));
     assert(checkCollectionsEqualBackwards(p.begin(), p.end(), std::begin(expect), std::end(expect)));
 

Modified: libcxx/trunk/test/std/experimental/filesystem/class.path/path.member/path.append.pass.cpp
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/std/experimental/filesystem/class.path/path.member/path.append.pass.cpp?rev=329028&r1=329027&r2=329028&view=diff
==============================================================================
--- libcxx/trunk/test/std/experimental/filesystem/class.path/path.member/path.append.pass.cpp (original)
+++ libcxx/trunk/test/std/experimental/filesystem/class.path/path.member/path.append.pass.cpp Mon Apr  2 16:03:41 2018
@@ -31,6 +31,7 @@
 #include "test_iterators.h"
 #include "count_new.hpp"
 #include "filesystem_test_helper.hpp"
+#include "verbose_assert.h"
 
 
 struct AppendOperatorTestcase {
@@ -45,13 +46,22 @@ const AppendOperatorTestcase Cases[] =
         {S(""),     S(""),      S("")}
       , {S("p1"),   S("p2"),    S("p1/p2")}
       , {S("p1/"),  S("p2"),    S("p1/p2")}
-      , {S("p1"),   S("/p2"),   S("p1/p2")}
-      , {S("p1/"),  S("/p2"),   S("p1//p2")}
+      , {S("p1"),   S("/p2"),   S("/p2")}
+      , {S("p1/"),  S("/p2"),   S("/p2")}
       , {S("p1"),   S("\\p2"),  S("p1/\\p2")}
       , {S("p1\\"), S("p2"),  S("p1\\/p2")}
       , {S("p1\\"), S("\\p2"),  S("p1\\/\\p2")}
-      , {S("p1"),   S(""),      S("p1")}
       , {S(""),     S("p2"),    S("p2")}
+      , {S("/p1"),  S("p2"),    S("/p1/p2")}
+      , {S("/p1"),  S("/p2"),    S("/p2")}
+      , {S("/p1/p3"),  S("p2"),    S("/p1/p3/p2")}
+      , {S("/p1/p3/"),  S("p2"),    S("/p1/p3/p2")}
+      , {S("/p1/"),  S("p2"),    S("/p1/p2")}
+      , {S("/p1/p3/"),  S("/p2/p4"),    S("/p2/p4")}
+      , {S("/"),    S(""),      S("/")}
+      , {S("/p1"), S("/p2/"), S("/p2/")}
+      , {S("p1"),   S(""),      S("p1/")}
+      , {S("p1/"),  S(""),      S("p1/")}
     };
 
 
@@ -59,7 +69,8 @@ const AppendOperatorTestcase LongLHSCase
     {
         {S("p1"),   S("p2"),    S("p1/p2")}
       , {S("p1/"),  S("p2"),    S("p1/p2")}
-      , {S("p1"),   S("/p2"),   S("p1/p2")}
+      , {S("p1"),   S("/p2"),   S("/p2")}
+      , {S("/p1"),  S("p2"),    S("/p1/p2")}
     };
 #undef S
 
@@ -98,7 +109,7 @@ void doAppendSourceAllocTest(AppendOpera
       DisableAllocationGuard g;
       LHS /= RHS;
     }
-    assert(LHS == E);
+    ASSERT_PRED(PathEq, LHS , E);
   }
   // basic_string_view
   {
@@ -108,7 +119,7 @@ void doAppendSourceAllocTest(AppendOpera
       DisableAllocationGuard g;
       LHS /= RHS;
     }
-    assert(LHS == E);
+    assert(PathEq(LHS, E));
   }
   // CharT*
   {
@@ -118,7 +129,7 @@ void doAppendSourceAllocTest(AppendOpera
       DisableAllocationGuard g;
       LHS /= RHS;
     }
-    assert(LHS == E);
+    assert(PathEq(LHS, E));
   }
   {
     path LHS(L); PathReserve(LHS, ReserveSize);
@@ -127,7 +138,7 @@ void doAppendSourceAllocTest(AppendOpera
       DisableAllocationGuard g;
       LHS.append(RHS, StrEnd(RHS));
     }
-    assert(LHS == E);
+    assert(PathEq(LHS, E));
   }
   // input iterator - For non-native char types, appends needs to copy the
   // iterator range into a contiguous block of memory before it can perform the
@@ -143,7 +154,7 @@ void doAppendSourceAllocTest(AppendOpera
       if (DisableAllocations) g.requireExactly(0);
       LHS /= RHS;
     }
-    assert(LHS == E);
+    assert(PathEq(LHS, E));
   }
   {
     path LHS(L); PathReserve(LHS, ReserveSize);
@@ -154,7 +165,7 @@ void doAppendSourceAllocTest(AppendOpera
       if (DisableAllocations) g.requireExactly(0);
       LHS.append(RHS, REnd);
     }
-    assert(LHS == E);
+    assert(PathEq(LHS, E));
   }
 }
 
@@ -171,17 +182,18 @@ void doAppendSourceTest(AppendOperatorTe
   const Ptr E = TC.expect;
   // basic_string
   {
-    path LHS(L);
+    path Result(L);
     Str RHS(R);
-    path& Ref = (LHS /= RHS);
-    assert(LHS == E);
-    assert(&Ref == &LHS);
+    path& Ref = (Result /= RHS);
+    ASSERT_EQ(Result, E)
+        << DISPLAY(L) << DISPLAY(R);
+    assert(&Ref == &Result);
   }
   {
     path LHS(L);
     Str RHS(R);
     path& Ref = LHS.append(RHS);
-    assert(LHS == E);
+    assert(PathEq(LHS, E));
     assert(&Ref == &LHS);
   }
   // basic_string_view
@@ -189,14 +201,14 @@ void doAppendSourceTest(AppendOperatorTe
     path LHS(L);
     StrView RHS(R);
     path& Ref = (LHS /= RHS);
-    assert(LHS == E);
+    assert(PathEq(LHS, E));
     assert(&Ref == &LHS);
   }
   {
     path LHS(L);
     StrView RHS(R);
     path& Ref = LHS.append(RHS);
-    assert(LHS == E);
+    assert(PathEq(LHS, E));
     assert(&Ref == &LHS);
   }
   // Char*
@@ -204,21 +216,22 @@ void doAppendSourceTest(AppendOperatorTe
     path LHS(L);
     Str RHS(R);
     path& Ref = (LHS /= RHS);
-    assert(LHS == E);
+    assert(PathEq(LHS, E));
     assert(&Ref == &LHS);
   }
   {
     path LHS(L);
     Ptr RHS(R);
     path& Ref = LHS.append(RHS);
-    assert(LHS == E);
+    assert(PathEq(LHS, E));
     assert(&Ref == &LHS);
   }
   {
     path LHS(L);
     Ptr RHS(R);
     path& Ref = LHS.append(RHS, StrEnd(RHS));
-    assert(LHS == E);
+    ASSERT_PRED(PathEq, LHS, E)
+        << DISPLAY(L) << DISPLAY(R);
     assert(&Ref == &LHS);
   }
   // iterators
@@ -226,13 +239,13 @@ void doAppendSourceTest(AppendOperatorTe
     path LHS(L);
     InputIter RHS(R);
     path& Ref = (LHS /= RHS);
-    assert(LHS == E);
+    assert(PathEq(LHS, E));
     assert(&Ref == &LHS);
   }
   {
     path LHS(L); InputIter RHS(R);
     path& Ref = LHS.append(RHS);
-    assert(LHS == E);
+    assert(PathEq(LHS, E));
     assert(&Ref == &LHS);
   }
   {
@@ -240,7 +253,7 @@ void doAppendSourceTest(AppendOperatorTe
     InputIter RHS(R);
     InputIter REnd(StrEnd(R));
     path& Ref = LHS.append(RHS, REnd);
-    assert(LHS == E);
+    assert(PathEq(LHS, E));
     assert(&Ref == &LHS);
   }
 }
@@ -304,11 +317,14 @@ int main()
   using namespace fs;
   for (auto const & TC : Cases) {
     {
-      path LHS((const char*)TC.lhs);
-      path RHS((const char*)TC.rhs);
-      path& Ref = (LHS /= RHS);
-      assert(LHS == (const char*)TC.expect);
-      assert(&Ref == &LHS);
+      const char* LHS_In = TC.lhs;
+      const char* RHS_In = TC.rhs;
+      path LHS(LHS_In);
+      path RHS(RHS_In);
+      path& Res = (LHS /= RHS);
+      ASSERT_PRED(PathEq, Res, (const char*)TC.expect)
+          << DISPLAY(LHS_In) << DISPLAY(RHS_In);
+      assert(&Res == &LHS);
     }
     doAppendSourceTest<char>    (TC);
     doAppendSourceTest<wchar_t> (TC);

Modified: libcxx/trunk/test/std/experimental/filesystem/class.path/path.member/path.compare.pass.cpp
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/std/experimental/filesystem/class.path/path.member/path.compare.pass.cpp?rev=329028&r1=329027&r2=329028&view=diff
==============================================================================
--- libcxx/trunk/test/std/experimental/filesystem/class.path/path.member/path.compare.pass.cpp (original)
+++ libcxx/trunk/test/std/experimental/filesystem/class.path/path.member/path.compare.pass.cpp Mon Apr  2 16:03:41 2018
@@ -26,6 +26,7 @@
 //
 // size_t hash_value(path const&) noexcept;
 
+
 #include "filesystem_include.hpp"
 #include <type_traits>
 #include <vector>
@@ -35,7 +36,7 @@
 #include "test_iterators.h"
 #include "count_new.hpp"
 #include "filesystem_test_helper.hpp"
-
+#include "verbose_assert.h"
 
 struct PathCompareTest {
   const char* LHS;
@@ -57,8 +58,9 @@ const PathCompareTest CompareTestCases[]
     {"a/b/c", "b/a/c", -1},
     {"a/b", "a/b/c", -1},
     {"a/b/c", "a/b", 1},
-    {"a/b/", "a/b/.", 0},
-    {"a/b//////", "a/b/////.", 0},
+    {"a/b/", "a/b/.", -1},
+    {"a/b/", "a/b",    1},
+    {"a/b//////", "a/b/////.", -1},
     {"a/.././b", "a///..//.////b", 0},
     {"//foo//bar///baz////", "//foo/bar/baz/", 0}, // duplicate separators
     {"///foo/bar", "/foo/bar", 0}, // "///" is not a root directory
@@ -94,8 +96,13 @@ int main()
       int ret2 = normalize_ret(p1.compare(R));
       int ret3 = normalize_ret(p1.compare(TC.RHS));
       int ret4 = normalize_ret(p1.compare(RV));
-      assert(ret1 == ret2 && ret1 == ret3 && ret1 == ret4);
-      assert(ret1 == E);
+
+      g.release();
+      ASSERT_EQ(ret1, ret2);
+      ASSERT_EQ(ret1, ret3);
+      ASSERT_EQ(ret1, ret4);
+      ASSERT_EQ(ret1, E)
+          << DISPLAY(TC.LHS) << DISPLAY(TC.RHS);
 
       // check signatures
       ASSERT_NOEXCEPT(p1.compare(p2));

Modified: libcxx/trunk/test/std/experimental/filesystem/class.path/path.member/path.decompose/empty.fail.cpp
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/std/experimental/filesystem/class.path/path.member/path.decompose/empty.fail.cpp?rev=329028&r1=329027&r2=329028&view=diff
==============================================================================
--- libcxx/trunk/test/std/experimental/filesystem/class.path/path.member/path.decompose/empty.fail.cpp (original)
+++ libcxx/trunk/test/std/experimental/filesystem/class.path/path.member/path.decompose/empty.fail.cpp Mon Apr  2 16:03:41 2018
@@ -18,6 +18,7 @@
 // UNSUPPORTED: clang-3.3, clang-3.4, clang-3.5, clang-3.6, clang-3.7, clang-3.8
 
 #include "filesystem_include.hpp"
+
 #include "test_macros.h"
 
 int main ()

Modified: libcxx/trunk/test/std/experimental/filesystem/class.path/path.member/path.decompose/path.decompose.pass.cpp
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/std/experimental/filesystem/class.path/path.member/path.decompose/path.decompose.pass.cpp?rev=329028&r1=329027&r2=329028&view=diff
==============================================================================
--- libcxx/trunk/test/std/experimental/filesystem/class.path/path.member/path.decompose/path.decompose.pass.cpp (original)
+++ libcxx/trunk/test/std/experimental/filesystem/class.path/path.member/path.decompose/path.decompose.pass.cpp Mon Apr  2 16:03:41 2018
@@ -53,6 +53,14 @@
 #include "test_iterators.h"
 #include "count_new.hpp"
 #include "filesystem_test_helper.hpp"
+#include "assert_checkpoint.h"
+#include "verbose_assert.h"
+
+struct ComparePathExact {
+  bool operator()(std::string const& LHS, std::string const& RHS) const {
+    return LHS == RHS;
+  }
+};
 
 struct PathDecomposeTestcase
 {
@@ -72,80 +80,89 @@ const PathDecomposeTestcase PathTestCase
     , {".", {"."}, "", "", "", ".", "", "."}
     , {"..", {".."}, "", "", "", "..", "", ".."}
     , {"foo", {"foo"}, "", "", "", "foo", "", "foo"}
-    , {"/", {"/"}, "/", "", "/", "", "", "/"}
+    , {"/", {"/"}, "/", "", "/", "", "/", ""}
     , {"/foo", {"/", "foo"}, "/", "", "/", "foo", "/", "foo"}
-    , {"foo/", {"foo", "."}, "", "", "", "foo/", "foo", "."}
-    , {"/foo/", {"/", "foo", "."}, "/", "", "/", "foo/", "/foo", "."}
+    , {"foo/", {"foo", ""}, "", "", "", "foo/", "foo", ""}
+    , {"/foo/", {"/", "foo", ""}, "/", "", "/", "foo/", "/foo", ""}
     , {"foo/bar", {"foo","bar"}, "",  "", "",  "foo/bar", "foo", "bar"}
     , {"/foo//bar", {"/","foo","bar"}, "/", "", "/", "foo/bar", "/foo", "bar"}
-    , {"//net", {"//net"}, "//net", "//net", "", "", "", "//net"}
-    , {"//net/foo", {"//net", "/", "foo"}, "//net/", "//net", "/", "foo", "//net/", "foo"}
-    , {"///foo///", {"/", "foo", "."}, "/", "", "/", "foo///", "///foo", "."}
+    , {"//net", {"/", "net"}, "/", "", "/", "net", "/", "net"}
+    , {"//net/foo", {"/", "net", "foo"}, "/", "", "/", "net/foo", "/net", "foo"}
+    , {"///foo///", {"/", "foo", ""}, "/", "", "/", "foo///", "///foo", ""}
     , {"///foo///bar", {"/", "foo", "bar"}, "/", "", "/", "foo///bar", "///foo", "bar"}
     , {"/.", {"/", "."}, "/", "", "/", ".", "/", "."}
-    , {"./", {".", "."}, "", "", "", "./", ".", "."}
+    , {"./", {".", ""}, "", "", "", "./", ".", ""}
     , {"/..", {"/", ".."}, "/", "", "/", "..", "/", ".."}
-    , {"../", {"..", "."}, "", "", "", "../", "..", "."}
+    , {"../", {"..", ""}, "", "", "", "../", "..", ""}
     , {"foo/.", {"foo", "."}, "", "", "", "foo/.", "foo", "."}
     , {"foo/..", {"foo", ".."}, "", "", "", "foo/..", "foo", ".."}
-    , {"foo/./", {"foo", ".", "."}, "", "", "", "foo/./", "foo/.", "."}
+    , {"foo/./", {"foo", ".", ""}, "", "", "", "foo/./", "foo/.", ""}
     , {"foo/./bar", {"foo", ".", "bar"}, "", "", "", "foo/./bar", "foo/.", "bar"}
-    , {"foo/../", {"foo", "..", "."}, "", "", "", "foo/../", "foo/..", "."}
+    , {"foo/../", {"foo", "..", ""}, "", "", "", "foo/../", "foo/..", ""}
     , {"foo/../bar", {"foo", "..", "bar"}, "", "", "", "foo/../bar", "foo/..", "bar"}
     , {"c:", {"c:"}, "", "", "", "c:", "", "c:"}
-    , {"c:/", {"c:", "."}, "", "", "", "c:/", "c:", "."}
+    , {"c:/", {"c:", ""}, "", "", "", "c:/", "c:", ""}
     , {"c:foo", {"c:foo"}, "", "", "", "c:foo", "", "c:foo"}
     , {"c:/foo", {"c:", "foo"}, "", "", "", "c:/foo", "c:", "foo"}
-    , {"c:foo/", {"c:foo", "."}, "", "", "", "c:foo/", "c:foo", "."}
-    , {"c:/foo/", {"c:", "foo", "."}, "", "", "", "c:/foo/",  "c:/foo", "."}
+    , {"c:foo/", {"c:foo", ""}, "", "", "", "c:foo/", "c:foo", ""}
+    , {"c:/foo/", {"c:", "foo", ""}, "", "", "", "c:/foo/",  "c:/foo", ""}
     , {"c:/foo/bar", {"c:", "foo", "bar"}, "", "", "", "c:/foo/bar", "c:/foo", "bar"}
     , {"prn:", {"prn:"}, "", "", "", "prn:", "", "prn:"}
     , {"c:\\", {"c:\\"}, "", "", "", "c:\\", "", "c:\\"}
     , {"c:\\foo", {"c:\\foo"}, "", "", "", "c:\\foo", "", "c:\\foo"}
     , {"c:foo\\", {"c:foo\\"}, "", "", "", "c:foo\\", "", "c:foo\\"}
     , {"c:\\foo\\", {"c:\\foo\\"}, "", "", "", "c:\\foo\\", "", "c:\\foo\\"}
-    , {"c:\\foo/",  {"c:\\foo", "."}, "", "", "", "c:\\foo/", "c:\\foo", "."}
+    , {"c:\\foo/",  {"c:\\foo", ""}, "", "", "", "c:\\foo/", "c:\\foo", ""}
     , {"c:/foo\\bar", {"c:", "foo\\bar"}, "", "", "", "c:/foo\\bar", "c:", "foo\\bar"}
-    , {"//", {"//"}, "//", "//", "", "", "", "//"}
+    , {"//", {"/"}, "/", "", "/", "", "/", ""}
   };
 
 void decompPathTest()
 {
   using namespace fs;
   for (auto const & TC : PathTestCases) {
-    path p(TC.raw);
-    assert(p == TC.raw);
-
-    assert(p.root_path() == TC.root_path);
-    assert(p.has_root_path() !=  TC.root_path.empty());
-
-    assert(p.root_name() == TC.root_name);
-    assert(p.has_root_name() !=  TC.root_name.empty());
-
-    assert(p.root_directory() == TC.root_directory);
-    assert(p.has_root_directory() !=  TC.root_directory.empty());
-
-    assert(p.relative_path() == TC.relative_path);
-    assert(p.has_relative_path() !=  TC.relative_path.empty());
-
-    assert(p.parent_path() == TC.parent_path);
-    assert(p.has_parent_path() !=  TC.parent_path.empty());
-
-    assert(p.filename() == TC.filename);
-    assert(p.has_filename() !=  TC.filename.empty());
-
-    assert(p.is_absolute() == p.has_root_directory());
-    assert(p.is_relative() !=  p.is_absolute());
-
-    assert(checkCollectionsEqual(p.begin(), p.end(),
-                                 TC.elements.begin(), TC.elements.end()));
+    CHECKPOINT(TC.raw.c_str());
+    fs::path p(TC.raw);
+    ASSERT(p == TC.raw);
+
+    ASSERT_EQ(p.root_path(), TC.root_path);
+    ASSERT_NEQ(p.has_root_path(), TC.root_path.empty());
+
+    ASSERT(p.root_name().native().empty())
+        << DISPLAY(p.root_name());
+    ASSERT_EQ(p.root_name(),TC.root_name);
+    ASSERT_NEQ(p.has_root_name(), TC.root_name.empty());
+
+    ASSERT_EQ(p.root_directory(), TC.root_directory);
+    ASSERT_NEQ(p.has_root_directory(), TC.root_directory.empty());
+
+    ASSERT_EQ(p.relative_path(), TC.relative_path);
+    ASSERT_NEQ(p.has_relative_path(), TC.relative_path.empty());
+
+    ASSERT_EQ(p.parent_path(), TC.parent_path);
+    ASSERT_NEQ(p.has_parent_path(), TC.parent_path.empty());
+
+    ASSERT_EQ(p.filename(), TC.filename);
+    ASSERT_NEQ(p.has_filename(), TC.filename.empty());
+
+    ASSERT_EQ(p.is_absolute(), p.has_root_directory());
+    ASSERT_NEQ(p.is_relative(), p.is_absolute());
+    if (p.empty())
+      ASSERT(p.is_relative());
+
+    ASSERT_COLLECTION_EQ_COMP(
+        p.begin(), p.end(),
+        TC.elements.begin(), TC.elements.end(),
+        ComparePathExact()
+    );
     // check backwards
 
     std::vector<fs::path> Parts;
     for (auto it = p.end(); it != p.begin(); )
       Parts.push_back(*--it);
-    assert(checkCollectionsEqual(Parts.begin(), Parts.end(),
-                                 TC.elements.rbegin(), TC.elements.rend()));
+    ASSERT_COLLECTION_EQ_COMP(Parts.begin(), Parts.end(),
+                                 TC.elements.rbegin(), TC.elements.rend(),
+                              ComparePathExact());
   }
 }
 
@@ -163,10 +180,12 @@ const FilenameDecompTestcase FilenameTes
     {"", "", "", ""}
   , {".", ".", ".", ""}
   , {"..", "..", "..", ""}
-  , {"/", "/", "/", ""}
+  , {"/", "", "", ""}
   , {"foo", "foo", "foo", ""}
   , {"/foo/bar.txt", "bar.txt", "bar", ".txt"}
   , {"foo..txt", "foo..txt", "foo.", ".txt"}
+  , {".profile", ".profile", ".profile", ""}
+  , {".profile.txt", ".profile.txt", ".profile", ".txt"}
 };
 
 
@@ -174,18 +193,19 @@ void decompFilenameTest()
 {
   using namespace fs;
   for (auto const & TC : FilenameTestCases) {
-    path p(TC.raw);
-    assert(p == TC.raw);
+    CHECKPOINT(TC.raw.c_str());
+    fs::path p(TC.raw);
+    ASSERT_EQ(p, TC.raw);
     ASSERT_NOEXCEPT(p.empty());
 
-    assert(p.filename() == TC.filename);
-    assert(p.has_filename() != TC.filename.empty());
+    ASSERT_EQ(p.filename(), TC.filename);
+    ASSERT_NEQ(p.has_filename(), TC.filename.empty());
 
-    assert(p.stem() == TC.stem);
-    assert(p.has_stem() != TC.stem.empty());
+    ASSERT_EQ(p.stem(), TC.stem);
+    ASSERT_NEQ(p.has_stem(), TC.stem.empty());
 
-    assert(p.extension() == TC.extension);
-    assert(p.has_extension() != TC.extension.empty());
+    ASSERT_EQ(p.extension(), TC.extension);
+    ASSERT_NEQ(p.has_extension(), TC.extension.empty());
   }
 }
 

Added: libcxx/trunk/test/std/experimental/filesystem/class.path/path.member/path.gen/lexically_normal.pass.cpp
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/std/experimental/filesystem/class.path/path.member/path.gen/lexically_normal.pass.cpp?rev=329028&view=auto
==============================================================================
--- libcxx/trunk/test/std/experimental/filesystem/class.path/path.member/path.gen/lexically_normal.pass.cpp (added)
+++ libcxx/trunk/test/std/experimental/filesystem/class.path/path.member/path.gen/lexically_normal.pass.cpp Mon Apr  2 16:03:41 2018
@@ -0,0 +1,142 @@
+//===----------------------------------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++98, c++03
+
+// <experimental/filesystem>
+
+// class path
+
+// path lexically_normal() const;
+
+#include "filesystem_include.hpp"
+#include <type_traits>
+#include <vector>
+#include <iostream>
+#include <cassert>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+#include "count_new.hpp"
+#include "filesystem_test_helper.hpp"
+
+
+int main() {
+  // clang-format off
+  struct {
+    std::string input;
+    std::string expect;
+  } TestCases[] = {
+      {"", ""},
+      {"/a/b/c", "/a/b/c"},
+      {"/a/b//c", "/a/b/c"},
+      {"foo/./bar/..", "foo/"},
+      {"foo/.///bar/../", "foo/"},
+      {"/a/b/", "/a/b/"},
+      {"a/b", "a/b"},
+      {"a/b/.", "a/b/"},
+      {"a/b/./", "a/b/"},
+      {"a/..", "."},
+      {".", "."},
+      {"./", "."},
+      {"./.", "."},
+      {"./..", ".."},
+      {"..", ".."},
+      {"../..", "../.."},
+      {"/../", "/"},
+      {"/../..", "/"},
+      {"/../../", "/"},
+      {"..", ".."},
+      {"../", ".."},
+      {"/a/b/c/../", "/a/b/"},
+      {"/a/b/./", "/a/b/"},
+      {"/a/b/c/../d", "/a/b/d"},
+      {"/a/b/c/../d/", "/a/b/d/"},
+      {"//a/", "/a/"},
+      {"//a/b/", "/a/b/"},
+      {"//a/b/.", "/a/b/"},
+      {"//a/..", "/"},
+      ///===---------------------------------------------------------------===//
+      /// Tests specifically for the clauses under [fs.path.generic]p6
+      ///===---------------------------------------------------------------===//
+      // p1: If the path is empty, stop.
+      {"", ""},
+      // p2: Replace each slash character in the root-name with a preferred
+      // separator.
+      {"NO_ROOT_NAME_ON_LINUX", "NO_ROOT_NAME_ON_LINUX"},
+      // p3: Replace each directory-separator with a preferred-separator.
+      // [ Note: The generic pathname grammar ([fs.path.generic]) defines
+      //   directory-separator as one or more slashes and preferred-separators.
+      //   — end note ]
+      {"/", "/"},
+      {"//", "/"},
+      {"///", "/"},
+      {"a/b", "a/b"},
+      {"a//b", "a/b"},
+      {"a///b", "a/b"},
+      {"a/b/", "a/b/"},
+      {"a/b//", "a/b/"},
+      {"a/b///", "a/b/"},
+      {"///a////b//////", "/a/b/"},
+      // p4: Remove each dot filename and any immediately following directory
+      // separators
+      {"foo/.", "foo/"},
+      {"foo/./bar/.", "foo/bar/"},
+      {"./foo/././bar/./", "foo/bar/"},
+      {".///foo//.////./bar/.///", "foo/bar/"},
+      // p5: As long as any appear, remove a non-dot-dot filename immediately
+      // followed by a directory-separator and a dot-dot filename, along with
+      // any immediately following directory separator.
+      {"foo/..", "."},
+      {"foo/../", "."},
+      {"foo/bar/..", "foo/"},
+      {"foo/bar/../", "foo/"},
+      {"foo/bar/../..", "."},
+      {"foo/bar/../../", "."},
+      {"foo/bar/baz/../..", "foo/"},
+      {"foo/bar/baz/../../", "foo/"},
+      {"foo/bar/./..", "foo/"},
+      {"foo/bar/./../", "foo/"},
+      // p6: If there is a root-directory, remove all dot-dot filenames and any
+      // directory-separators immediately following them. [ Note: These dot-dot
+      // filenames attempt to refer to nonexistent parent directories. — end note ]
+      {"/..", "/"},
+      {"/../", "/"},
+      {"/foo/../..", "/"},
+      {"/../foo", "/foo"},
+      {"/../foo/../..", "/"},
+      // p7: If the last filename is dot-dot, remove any trailing
+      // directory-separator.
+      {"../", ".."},
+      {"../../", "../.."},
+      {"foo/../bar/../..///", ".."},
+      {"foo/../bar/..//..///../", "../.."},
+      // p8: If the path is empty, add a dot
+      {".", "."},
+      {"./", "."},
+      {"foo/..", "."}
+  };
+  // clang-format on
+  int ID = 0;
+  bool Failed = false;
+  for (auto& TC : TestCases) {
+    ++ID;
+    fs::path p(TC.input);
+    const fs::path output = p.lexically_normal();
+    if (!PathEq(output, TC.expect)) {
+      Failed = true;
+      std::cerr << "TEST CASE #" << ID << " FAILED: \n";
+      std::cerr << "  Input: '" << TC.input << "'\n";
+      std::cerr << "  Expected: '" << TC.expect << "'\n";
+      std::cerr << "  Output: '" << output.native() << "'";
+      std::cerr << std::endl;
+    }
+  }
+  return Failed;
+}

Added: libcxx/trunk/test/std/experimental/filesystem/class.path/path.member/path.gen/lexically_relative_and_proximate.pass.cpp
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/std/experimental/filesystem/class.path/path.member/path.gen/lexically_relative_and_proximate.pass.cpp?rev=329028&view=auto
==============================================================================
--- libcxx/trunk/test/std/experimental/filesystem/class.path/path.member/path.gen/lexically_relative_and_proximate.pass.cpp (added)
+++ libcxx/trunk/test/std/experimental/filesystem/class.path/path.member/path.gen/lexically_relative_and_proximate.pass.cpp Mon Apr  2 16:03:41 2018
@@ -0,0 +1,89 @@
+//===----------------------------------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++98, c++03
+
+// <experimental/filesystem>
+
+// class path
+
+// path lexically_relative(const path& p) const;
+// path lexically_proximate(const path& p) const;
+
+#include "filesystem_include.hpp"
+#include <type_traits>
+#include <vector>
+#include <iostream>
+#include <cassert>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+#include "count_new.hpp"
+#include "filesystem_test_helper.hpp"
+
+
+int main() {
+  // clang-format off
+  struct {
+    std::string input;
+    std::string base;
+    std::string expect;
+  } TestCases[] = {
+      {"", "", "."},
+      {"/", "a", ""},
+      {"a", "/", ""},
+      {"//net", "a", ""},
+      {"a", "//net", ""},
+      {"//net/", "//net", ""},
+      {"//net", "//net/", ".."},
+      {"//base", "a", ""},
+      {"a", "a", "."},
+      {"a/b", "a/b", "."},
+      {"a/b/c/", "a/b/c/", "."},
+      {"//net", "//net", "."},
+      {"//net/", "//net/", "."},
+      {"//net/a/b", "//net/a/b", "."},
+      {"/a/d", "/a/b/c", "../../d"},
+      {"/a/b/c", "/a/d", "../b/c"},
+      {"a/b/c", "a", "b/c"},
+      {"a/b/c", "a/b/c/x/y", "../.."},
+      {"a/b/c", "a/b/c", "."},
+      {"a/b", "c/d", "../../a/b"}
+  };
+  // clang-format on
+  int ID = 0;
+  bool Failed = false;
+  for (auto& TC : TestCases) {
+    ++ID;
+    const fs::path p(TC.input);
+    const fs::path output = p.lexically_relative(TC.base);
+    auto ReportErr = [&](const char* Testing, fs::path const& Output,
+                                              fs::path const& Expected) {
+      Failed = true;
+      std::cerr << "TEST CASE #" << ID << " FAILED: \n";
+      std::cerr << "  Testing: " << Testing << "\n";
+      std::cerr << "  Input: '" << TC.input << "'\n";
+      std::cerr << "  Base: '" << TC.base << "'\n";
+      std::cerr << "  Expected: '" << Expected << "'\n";
+      std::cerr << "  Output: '" << Output.native() << "'";
+      std::cerr << std::endl;
+    };
+    if (!PathEq(output, TC.expect))
+      ReportErr("path::lexically_relative", output, TC.expect);
+    const fs::path proximate_output = p.lexically_proximate(TC.base);
+    // [path.gen] lexically_proximate
+    // Returns: If the value of lexically_relative(base) is not an empty path,
+    // return it.Otherwise return *this.
+    const fs::path proximate_expected = output.native().empty() ? p
+        : output;
+    if (!PathEq(proximate_expected, proximate_output))
+      ReportErr("path::lexically_proximate", proximate_output, proximate_expected);
+  }
+  return Failed;
+}

Modified: libcxx/trunk/test/std/experimental/filesystem/class.path/path.member/path.generic.obs/generic_string_alloc.pass.cpp
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/std/experimental/filesystem/class.path/path.member/path.generic.obs/generic_string_alloc.pass.cpp?rev=329028&r1=329027&r2=329028&view=diff
==============================================================================
--- libcxx/trunk/test/std/experimental/filesystem/class.path/path.member/path.generic.obs/generic_string_alloc.pass.cpp (original)
+++ libcxx/trunk/test/std/experimental/filesystem/class.path/path.member/path.generic.obs/generic_string_alloc.pass.cpp Mon Apr  2 16:03:41 2018
@@ -28,7 +28,6 @@
 #include "min_allocator.h"
 #include "filesystem_test_helper.hpp"
 
-
 MultiStringType longString = MKSTR("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/123456789/abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
 
 

Modified: libcxx/trunk/test/std/experimental/filesystem/class.path/path.member/path.generic.obs/named_overloads.pass.cpp
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/std/experimental/filesystem/class.path/path.member/path.generic.obs/named_overloads.pass.cpp?rev=329028&r1=329027&r2=329028&view=diff
==============================================================================
--- libcxx/trunk/test/std/experimental/filesystem/class.path/path.member/path.generic.obs/named_overloads.pass.cpp (original)
+++ libcxx/trunk/test/std/experimental/filesystem/class.path/path.member/path.generic.obs/named_overloads.pass.cpp Mon Apr  2 16:03:41 2018
@@ -30,7 +30,6 @@
 #include "min_allocator.h"
 #include "filesystem_test_helper.hpp"
 
-
 MultiStringType longString = MKSTR("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/123456789/abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
 
 int main()

Modified: libcxx/trunk/test/std/experimental/filesystem/class.path/path.member/path.modifiers/remove_filename.pass.cpp
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/std/experimental/filesystem/class.path/path.member/path.modifiers/remove_filename.pass.cpp?rev=329028&r1=329027&r2=329028&view=diff
==============================================================================
--- libcxx/trunk/test/std/experimental/filesystem/class.path/path.member/path.modifiers/remove_filename.pass.cpp (original)
+++ libcxx/trunk/test/std/experimental/filesystem/class.path/path.member/path.modifiers/remove_filename.pass.cpp Mon Apr  2 16:03:41 2018
@@ -23,7 +23,7 @@
 #include "test_iterators.h"
 #include "count_new.hpp"
 #include "filesystem_test_helper.hpp"
-
+#include "verbose_assert.h"
 
 struct RemoveFilenameTestcase {
   const char* value;
@@ -33,27 +33,29 @@ struct RemoveFilenameTestcase {
 const RemoveFilenameTestcase TestCases[] =
   {
       {"", ""}
-    , {"/", ""}
-    , {"//", ""}
-    , {"///", ""}
+    , {"/", "/"}
+    , {"//", "//"}
+    , {"///", "///"}
     , {"\\", ""}
     , {".", ""}
     , {"..", ""}
     , {"/foo", "/"}
-    , {"//foo", ""}
-    , {"//foo/", ""}
-    , {"//foo///", ""}
-    , {"///foo", "/"}
-    , {"///foo/", "///foo"}
-    , {"/foo/", "/foo"}
-    , {"/foo/.", "/foo"}
-    , {"/foo/..", "/foo"}
-    , {"/foo/////", "/foo"}
+    , {"foo/bar", "foo/"}
+    , {"foo/", "foo/"}
+    , {"//foo", "//"}
+    , {"//foo/", "//foo/"}
+    , {"//foo///", "//foo///"}
+    , {"///foo", "///"}
+    , {"///foo/", "///foo/"}
+    , {"/foo/", "/foo/"}
+    , {"/foo/.", "/foo/"}
+    , {"/foo/..", "/foo/"}
+    , {"/foo/////", "/foo/////"}
     , {"/foo\\\\", "/"}
-    , {"/foo//\\/", "/foo//\\"}
-    , {"///foo", "/"}
+    , {"/foo//\\/", "/foo//\\/"}
+    , {"///foo", "///"}
     , {"file.txt", ""}
-    , {"bar/../baz/./file.txt", "bar/../baz/."}
+    , {"bar/../baz/./file.txt", "bar/../baz/./"}
   };
 
 int main()
@@ -64,16 +66,8 @@ int main()
     path p(p_orig);
     assert(p == TC.value);
     path& Ref = (p.remove_filename());
-    assert(p == TC.expect);
+    ASSERT_EQ(p, TC.expect) << DISPLAY(p_orig);
     assert(&Ref == &p);
-    {
-      const path parentp = p_orig.parent_path();
-      if (parentp == p_orig.root_name()) {
-
-        assert(p.empty());
-      } else {
-        assert(p == parentp);
-      }
-    }
+    assert(!p.has_filename());
   }
 }

Modified: libcxx/trunk/test/std/experimental/filesystem/class.path/path.member/path.modifiers/replace_filename.pass.cpp
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/std/experimental/filesystem/class.path/path.member/path.modifiers/replace_filename.pass.cpp?rev=329028&r1=329027&r2=329028&view=diff
==============================================================================
--- libcxx/trunk/test/std/experimental/filesystem/class.path/path.member/path.modifiers/replace_filename.pass.cpp (original)
+++ libcxx/trunk/test/std/experimental/filesystem/class.path/path.member/path.modifiers/replace_filename.pass.cpp Mon Apr  2 16:03:41 2018
@@ -23,7 +23,8 @@
 #include "test_iterators.h"
 #include "count_new.hpp"
 #include "filesystem_test_helper.hpp"
-
+#include "assert_checkpoint.h"
+#include "verbose_assert.h"
 
 struct ReplaceFilenameTestcase {
   const char* value;
@@ -36,9 +37,9 @@ const ReplaceFilenameTestcase TestCases[
       {"/foo", "/bar", "bar"}
     , {"/foo", "/", ""}
     , {"foo", "bar", "bar"}
-    , {"/", "bar", "bar"}
+    , {"/", "/bar", "bar"}
     , {"\\", "bar", "bar"}
-    , {"///", "bar", "bar"}
+    , {"///", "///bar", "bar"}
     , {"\\\\", "bar", "bar"}
     , {"\\/\\", "\\/bar", "bar"}
     , {".", "bar", "bar"}
@@ -52,9 +53,11 @@ int main()
   using namespace fs;
   for (auto const & TC : TestCases) {
     path p(TC.value);
-    assert(p == TC.value);
+    ASSERT_EQ(p, TC.value);
     path& Ref = (p.replace_filename(TC.filename));
-    assert(p == TC.expect);
+    ASSERT_EQ(p, TC.expect)
+        << DISPLAY(TC.value)
+        << DISPLAY(TC.filename);
     assert(&Ref == &p);
     // Tests Effects "as-if": remove_filename() append(filename)
     {
@@ -62,7 +65,7 @@ int main()
       path replace(TC.filename);
       p2.remove_filename();
       p2 /= replace;
-      assert(p2 == p);
+      ASSERT_EQ(p, p2);
     }
   }
 }

Added: libcxx/trunk/test/std/experimental/filesystem/fs.enum/enum.perm_options.pass.cpp
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/std/experimental/filesystem/fs.enum/enum.perm_options.pass.cpp?rev=329028&view=auto
==============================================================================
--- libcxx/trunk/test/std/experimental/filesystem/fs.enum/enum.perm_options.pass.cpp (added)
+++ libcxx/trunk/test/std/experimental/filesystem/fs.enum/enum.perm_options.pass.cpp Mon Apr  2 16:03:41 2018
@@ -0,0 +1,48 @@
+//===----------------------------------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++98, c++03
+
+// <experimental/filesystem>
+
+// enum class perm_options;
+
+#include "filesystem_include.hpp"
+#include <type_traits>
+#include <cassert>
+#include <sys/stat.h>
+
+#include "test_macros.h"
+#include "check_bitmask_types.hpp"
+
+
+constexpr fs::perm_options ME(int val) {
+  return static_cast<fs::perm_options>(val);
+}
+
+int main() {
+  typedef fs::perm_options E;
+  static_assert(std::is_enum<E>::value, "");
+
+  // Check that E is a scoped enum by checking for conversions.
+  typedef std::underlying_type<E>::type UT;
+  static_assert(!std::is_convertible<E, UT>::value, "");
+
+  static_assert(std::is_same<UT, unsigned char >::value, ""); // Implementation detail
+
+  typedef check_bitmask_type<E, E::replace, E::nofollow> BitmaskTester;
+  assert(BitmaskTester::check());
+
+  static_assert(
+        E::replace  == ME(1) &&
+        E::add      == ME(2) &&
+        E::remove   == ME(4) &&
+        E::nofollow == ME(8),
+        "Expected enumeration values do not match");
+}

Modified: libcxx/trunk/test/std/experimental/filesystem/fs.op.funcs/fs.op.absolute/absolute.pass.cpp
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/std/experimental/filesystem/fs.op.funcs/fs.op.absolute/absolute.pass.cpp?rev=329028&r1=329027&r2=329028&view=diff
==============================================================================
--- libcxx/trunk/test/std/experimental/filesystem/fs.op.funcs/fs.op.absolute/absolute.pass.cpp (original)
+++ libcxx/trunk/test/std/experimental/filesystem/fs.op.funcs/fs.op.absolute/absolute.pass.cpp Mon Apr  2 16:03:41 2018
@@ -28,89 +28,30 @@ TEST_SUITE(filesystem_absolute_path_test
 TEST_CASE(absolute_signature_test)
 {
     const path p; ((void)p);
+    std::error_code ec;
     ASSERT_NOT_NOEXCEPT(absolute(p));
-    ASSERT_NOT_NOEXCEPT(absolute(p, p));
+    ASSERT_NOT_NOEXCEPT(absolute(p, ec));
 }
 
-// There are 4 cases is the proposal for absolute path.
-// Each scope tests one of the cases.
-TEST_CASE(absolute_path_test)
-{
-    // has_root_name() && has_root_directory()
-    {
-        const path p("//net/foo");
-        const path base("//net/bar/baz");
-        TEST_REQUIRE(p.has_root_name());
-        TEST_REQUIRE(p.has_root_directory());
-        TEST_CHECK(p.is_absolute());
-        path ret = absolute(p, base);
-        TEST_CHECK(ret.is_absolute());
-        TEST_CHECK(ret == p);
-    }
-    // !has_root_name() && has_root_directory()
-    {
-        const path p("/foo");
-        const path base("//net/bar");
-        TEST_REQUIRE(not p.has_root_name());
-        TEST_REQUIRE(p.has_root_directory());
-        TEST_CHECK(p.is_absolute());
-        // ensure absolute(base) is not recursively called
-        TEST_REQUIRE(base.has_root_name());
-        TEST_REQUIRE(base.has_root_directory());
-
-        path ret = absolute(p, base);
-        TEST_CHECK(ret.is_absolute());
-        TEST_CHECK(ret.has_root_name());
-        TEST_CHECK(ret.root_name() == path("//net"));
-        TEST_CHECK(ret.has_root_directory());
-        TEST_CHECK(ret.root_directory() == path("/"));
-        TEST_CHECK(ret == path("//net/foo"));
-    }
-    // has_root_name() && !has_root_directory()
-    {
-        const path p("//net");
-        const path base("//net/foo/bar");
-        TEST_REQUIRE(p.has_root_name());
-        TEST_REQUIRE(not p.has_root_directory());
-        TEST_CHECK(not p.is_absolute());
-        // absolute is called recursively on base. The following conditions
-        // must be true for it to return base unmodified
-        TEST_REQUIRE(base.has_root_name());
-        TEST_REQUIRE(base.has_root_directory());
-        path ret = absolute(p, base);
-        const path expect("//net/foo/bar");
-        TEST_CHECK(ret.is_absolute());
-        TEST_CHECK(ret == path("//net/foo/bar"));
-    }
-    // !has_root_name() && !has_root_directory()
-    {
-        const path p("bar/baz");
-        const path base("//net/foo");
-        TEST_REQUIRE(not p.has_root_name());
-        TEST_REQUIRE(not p.has_root_directory());
-        TEST_REQUIRE(base.has_root_name());
-        TEST_REQUIRE(base.has_root_directory());
-
-        path ret = absolute(p, base);
-        TEST_CHECK(ret.is_absolute());
-        TEST_CHECK(ret == path("//net/foo/bar/baz"));
-    }
-}
 
-TEST_CASE(absolute_path_with_default_base)
+TEST_CASE(basic_test)
 {
-    const path testCases[] = {
-        "//net/foo", //  has_root_name() &&  has_root_directory()
-        "/foo",      // !has_root_name() &&  has_root_directory()
-        "//net",     //  has_root_name() && !has_root_directory()
-        "bar/baz"    // !has_root_name() && !has_root_directory()
+    const fs::path cwd = fs::current_path();
+    const struct {
+      std::string input;
+      std::string expect;
+    } TestCases [] = {
+        {"", cwd / ""},
+        {"foo", cwd / "foo"},
+        {"foo/", cwd / "foo/"},
+        {"/already_absolute", "/already_absolute"}
     };
-    const path base = current_path();
-    for (auto& p : testCases) {
-        const path ret = absolute(p);
-        const path expect = absolute(p, base);
+    for (auto& TC : TestCases) {
+        std::error_code ec = GetTestEC();
+        const path ret = absolute(TC.input, ec);
+        TEST_CHECK(!ec);
         TEST_CHECK(ret.is_absolute());
-        TEST_CHECK(ret == expect);
+        TEST_CHECK(PathEq(ret, TC.expect));
     }
 }
 

Modified: libcxx/trunk/test/std/experimental/filesystem/fs.op.funcs/fs.op.canonical/canonical.pass.cpp
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/std/experimental/filesystem/fs.op.funcs/fs.op.canonical/canonical.pass.cpp?rev=329028&r1=329027&r2=329028&view=diff
==============================================================================
--- libcxx/trunk/test/std/experimental/filesystem/fs.op.funcs/fs.op.canonical/canonical.pass.cpp (original)
+++ libcxx/trunk/test/std/experimental/filesystem/fs.op.funcs/fs.op.canonical/canonical.pass.cpp Mon Apr  2 16:03:41 2018
@@ -11,9 +11,8 @@
 
 // <experimental/filesystem>
 
-// path canonical(const path& p, const path& base = current_path());
+// path canonical(const path& p);
 // path canonical(const path& p, error_code& ec);
-// path canonical(const path& p, const path& base, error_code& ec);
 
 #include "filesystem_include.hpp"
 #include <type_traits>
@@ -25,6 +24,15 @@
 
 using namespace fs;
 
+struct CWDGuard {
+  path OldCWD;
+  CWDGuard() : OldCWD(fs::current_path()) { }
+  ~CWDGuard() { fs::current_path(OldCWD); }
+
+  CWDGuard(CWDGuard const&) = delete;
+  CWDGuard& operator=(CWDGuard const&) = delete;
+};
+
 TEST_SUITE(filesystem_canonical_path_test_suite)
 
 TEST_CASE(signature_test)
@@ -32,15 +40,14 @@ TEST_CASE(signature_test)
     const path p; ((void)p);
     std::error_code ec; ((void)ec);
     ASSERT_NOT_NOEXCEPT(canonical(p));
-    ASSERT_NOT_NOEXCEPT(canonical(p, p));
     ASSERT_NOT_NOEXCEPT(canonical(p, ec));
-    ASSERT_NOT_NOEXCEPT(canonical(p, p, ec));
 }
 
 // There are 4 cases is the proposal for absolute path.
 // Each scope tests one of the cases.
 TEST_CASE(test_canonical)
 {
+    CWDGuard guard;
     // has_root_name() && has_root_directory()
     const path Root = StaticEnv::Root;
     const path RootName = Root.filename();
@@ -65,54 +72,51 @@ TEST_CASE(test_canonical)
         { SymlinkName, StaticEnv::File, StaticEnv::Root}
     };
     for (auto& TC : testCases) {
-        std::error_code ec;
-        const path ret = canonical(TC.p, TC.base, ec);
+        std::error_code ec = GetTestEC();
+        fs::current_path(TC.base);
+        const path ret = canonical(TC.p, ec);
         TEST_REQUIRE(!ec);
-        const path ret2 = canonical(TC.p, TC.base);
-        TEST_CHECK(ret == TC.expect);
-        TEST_CHECK(ret == ret2);
+        const path ret2 = canonical(TC.p);
+        TEST_CHECK(PathEq(ret, TC.expect));
+        TEST_CHECK(PathEq(ret, ret2));
         TEST_CHECK(ret.is_absolute());
     }
 }
 
 TEST_CASE(test_dne_path)
 {
-    std::error_code ec;
+    std::error_code ec = GetTestEC();
     {
         const path ret = canonical(StaticEnv::DNE, ec);
-        TEST_REQUIRE(ec);
-        TEST_CHECK(ret == path{});
-    }
-    ec.clear();
-    {
-        const path ret = canonical(StaticEnv::DNE, StaticEnv::Root, ec);
+        TEST_CHECK(ec != GetTestEC());
         TEST_REQUIRE(ec);
         TEST_CHECK(ret == path{});
     }
     {
         TEST_CHECK_THROW(filesystem_error, canonical(StaticEnv::DNE));
-        TEST_CHECK_THROW(filesystem_error, canonical(StaticEnv::DNE, StaticEnv::Root));
     }
 }
 
 TEST_CASE(test_exception_contains_paths)
 {
 #ifndef TEST_HAS_NO_EXCEPTIONS
+    CWDGuard guard;
     const path p = "blabla/dne";
-    const path base = StaticEnv::Root;
     try {
-        canonical(p, base);
+        canonical(p);
         TEST_REQUIRE(false);
     } catch (filesystem_error const& err) {
         TEST_CHECK(err.path1() == p);
-        TEST_CHECK(err.path2() == base);
+        // libc++ provides the current path as the second path in the exception
+        LIBCPP_ONLY(TEST_CHECK(err.path2() == current_path()));
     }
+    fs::current_path(StaticEnv::Dir);
     try {
         canonical(p);
         TEST_REQUIRE(false);
     } catch (filesystem_error const& err) {
         TEST_CHECK(err.path1() == p);
-        TEST_CHECK(err.path2() == current_path());
+        LIBCPP_ONLY(TEST_CHECK(err.path2() == StaticEnv::Dir));
     }
 #endif
 }

Modified: libcxx/trunk/test/std/experimental/filesystem/fs.op.funcs/fs.op.last_write_time/last_write_time.pass.cpp
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/std/experimental/filesystem/fs.op.funcs/fs.op.last_write_time/last_write_time.pass.cpp?rev=329028&r1=329027&r2=329028&view=diff
==============================================================================
--- libcxx/trunk/test/std/experimental/filesystem/fs.op.funcs/fs.op.last_write_time/last_write_time.pass.cpp (original)
+++ libcxx/trunk/test/std/experimental/filesystem/fs.op.funcs/fs.op.last_write_time/last_write_time.pass.cpp Mon Apr  2 16:03:41 2018
@@ -17,7 +17,6 @@
 // void last_write_time(const path& p, file_time_type new_type,
 //                      std::error_code& ec) noexcept;
 
-
 #include "filesystem_include.hpp"
 #include <type_traits>
 #include <chrono>
@@ -33,7 +32,6 @@
 
 using namespace fs;
 
-
 std::pair<std::time_t, std::time_t> GetTimes(path const& p) {
     using Clock = file_time_type::clock;
     struct ::stat st;

Added: libcxx/trunk/test/std/experimental/filesystem/fs.op.funcs/fs.op.proximate/proximate.pass.cpp
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/std/experimental/filesystem/fs.op.funcs/fs.op.proximate/proximate.pass.cpp?rev=329028&view=auto
==============================================================================
--- libcxx/trunk/test/std/experimental/filesystem/fs.op.funcs/fs.op.proximate/proximate.pass.cpp (added)
+++ libcxx/trunk/test/std/experimental/filesystem/fs.op.funcs/fs.op.proximate/proximate.pass.cpp Mon Apr  2 16:03:41 2018
@@ -0,0 +1,125 @@
+//===----------------------------------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++98, c++03
+
+// <experimental/filesystem>
+
+// path proximate(const path& p, error_code &ec)
+// path proximate(const path& p, const path& base = current_path())
+// path proximate(const path& p, const path& base, error_code& ec);
+
+#include "filesystem_include.hpp"
+#include <type_traits>
+#include <vector>
+#include <iostream>
+#include <cassert>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+#include "count_new.hpp"
+#include "rapid-cxx-test.hpp"
+#include "filesystem_test_helper.hpp"
+
+
+static int count_path_elems(const fs::path& p) {
+  int count = 0;
+  for (auto& elem : p) {
+    if (elem != "/" && elem != "")
+      ++count;
+  }
+  return count;
+}
+
+TEST_SUITE(filesystem_proximate_path_test_suite)
+
+
+TEST_CASE(signature_test)
+{
+    using fs::path;
+    const path p; ((void)p);
+    std::error_code ec; ((void)ec);
+    ASSERT_NOT_NOEXCEPT(proximate(p));
+    ASSERT_NOT_NOEXCEPT(proximate(p, p));
+    ASSERT_NOT_NOEXCEPT(proximate(p, ec));
+    ASSERT_NOT_NOEXCEPT(proximate(p, p, ec));
+}
+
+TEST_CASE(basic_test) {
+  using fs::path;
+  const path cwd = fs::current_path();
+  const path parent_cwd = cwd.parent_path();
+  const path curdir = cwd.filename();
+  TEST_REQUIRE(!cwd.native().empty());
+  int cwd_depth = count_path_elems(cwd);
+  path dot_dot_to_root;
+  for (int i=0; i < cwd_depth; ++i)
+    dot_dot_to_root /= "..";
+  path relative_cwd = cwd.native().substr(1);
+  // clang-format off
+  struct {
+    std::string input;
+    std::string base;
+    std::string expect;
+  } TestCases[] = {
+      {"", "", "."},
+      {cwd, "a", ".."},
+      {parent_cwd, "a", "../.."},
+      {"a", cwd, "a"},
+      {"a", parent_cwd, "fs.op.proximate/a"},
+      {"/", "a", dot_dot_to_root / ".."},
+      {"/", "a/b", dot_dot_to_root / "../.."},
+      {"/", "a/b/", dot_dot_to_root / "../../.."},
+      {"a", "/", relative_cwd / "a"},
+      {"a/b", "/", relative_cwd / "a/b"},
+      {"a", "/net", ".." / relative_cwd / "a"},
+      {"//net/", "//net", "/net/"},
+      {"//net", "//net/", ".."},
+      {"//net", "//net", "."},
+      {"//net/", "//net/", "."},
+      {"//base", "a", dot_dot_to_root / "../base"},
+      {"a", "a", "."},
+      {"a/b", "a/b", "."},
+      {"a/b/c/", "a/b/c/", "."},
+      {"//net/a/b", "//net/a/b", "."},
+      {"/a/d", "/a/b/c", "../../d"},
+      {"/a/b/c", "/a/d", "../b/c"},
+      {"a/b/c", "a", "b/c"},
+      {"a/b/c", "a/b/c/x/y", "../.."},
+      {"a/b/c", "a/b/c", "."},
+      {"a/b", "c/d", "../../a/b"}
+  };
+  // clang-format on
+  int ID = 0;
+  for (auto& TC : TestCases) {
+    ++ID;
+    std::error_code ec = GetTestEC();
+    fs::path p(TC.input);
+    const fs::path output = fs::proximate(p, TC.base, ec);
+    TEST_CHECK(!ec);
+    TEST_CHECK(PathEq(output, TC.expect));
+    if (!PathEq(output, TC.expect)) {
+      const path canon_input = fs::weakly_canonical(TC.input);
+      const path canon_base = fs::weakly_canonical(TC.base);
+      const path lexically_p = canon_input.lexically_proximate(canon_base);
+      std::cerr << "TEST CASE #" << ID << " FAILED: \n";
+      std::cerr << "  Input: '" << TC.input << "'\n";
+      std::cerr << "  Base: '" << TC.base << "'\n";
+      std::cerr << "  Expected: '" << TC.expect << "'\n";
+      std::cerr << "  Output:   '" << output.native() << "'\n";
+      std::cerr << "  Lex Prox: '" << lexically_p.native() << "'\n";
+      std::cerr << "  Canon Input: " <<  canon_input << "\n";
+      std::cerr << "  Canon Base: " << canon_base << "\n";
+
+      std::cerr << std::endl;
+    }
+  }
+}
+
+TEST_SUITE_END()

Added: libcxx/trunk/test/std/experimental/filesystem/fs.op.funcs/fs.op.relative/relative.pass.cpp
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/std/experimental/filesystem/fs.op.funcs/fs.op.relative/relative.pass.cpp?rev=329028&view=auto
==============================================================================
--- libcxx/trunk/test/std/experimental/filesystem/fs.op.funcs/fs.op.relative/relative.pass.cpp (added)
+++ libcxx/trunk/test/std/experimental/filesystem/fs.op.funcs/fs.op.relative/relative.pass.cpp Mon Apr  2 16:03:41 2018
@@ -0,0 +1,78 @@
+//===----------------------------------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++98, c++03
+
+// <experimental/filesystem>
+
+// path proximate(const path& p, error_code &ec)
+// path proximate(const path& p, const path& base = current_path())
+// path proximate(const path& p, const path& base, error_code& ec);
+
+#include "filesystem_include.hpp"
+#include <type_traits>
+#include <vector>
+#include <iostream>
+#include <cassert>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+#include "count_new.hpp"
+#include "rapid-cxx-test.hpp"
+#include "filesystem_test_helper.hpp"
+
+
+TEST_SUITE(filesystem_proximate_path_test_suite)
+
+TEST_CASE(test_signature) {
+
+}
+int main() {
+  // clang-format off
+  struct {
+    std::string input;
+    std::string expect;
+  } TestCases[] = {
+      {"", fs::current_path()},
+      {".", fs::current_path()},
+      {StaticEnv::File, StaticEnv::File},
+      {StaticEnv::Dir, StaticEnv::Dir},
+      {StaticEnv::SymlinkToDir, StaticEnv::Dir},
+      {StaticEnv::SymlinkToDir / "dir2/.", StaticEnv::Dir / "dir2"},
+      // FIXME? If the trailing separator occurs in a part of the path that exists,
+      // it is ommitted. Otherwise it is added to the end of the result.
+      {StaticEnv::SymlinkToDir / "dir2/./", StaticEnv::Dir / "dir2"},
+      {StaticEnv::SymlinkToDir / "dir2/DNE/./", StaticEnv::Dir / "dir2/DNE/"},
+      {StaticEnv::SymlinkToDir / "dir2", StaticEnv::Dir2},
+      {StaticEnv::SymlinkToDir / "dir2/../dir2/DNE/..", StaticEnv::Dir2 / ""},
+      {StaticEnv::SymlinkToDir / "dir2/dir3/../DNE/DNE2", StaticEnv::Dir2 / "DNE/DNE2"},
+      {StaticEnv::Dir / "../dir1", StaticEnv::Dir},
+      {StaticEnv::Dir / "./.", StaticEnv::Dir},
+      {StaticEnv::Dir / "DNE/../foo", StaticEnv::Dir / "foo"}
+  };
+  // clang-format on
+  int ID = 0;
+  bool Failed = false;
+  for (auto& TC : TestCases) {
+    ++ID;
+    fs::path p(TC.input);
+    const fs::path output = fs::weakly_canonical(p);
+    if (output != TC.expect) {
+      Failed = true;
+      std::cerr << "TEST CASE #" << ID << " FAILED: \n";
+      std::cerr << "  Input: '" << TC.input << "'\n";
+      std::cerr << "  Expected: '" << TC.expect << "'\n";
+      std::cerr << "  Output: '" << output.native() << "'";
+      std::cerr << std::endl;
+    }
+  }
+  return Failed;
+}
+
+TEST_SUITE_END()

Removed: libcxx/trunk/test/std/experimental/filesystem/fs.op.funcs/fs.op.system_complete/system_complete.pass.cpp
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/std/experimental/filesystem/fs.op.funcs/fs.op.system_complete/system_complete.pass.cpp?rev=329027&view=auto
==============================================================================
--- libcxx/trunk/test/std/experimental/filesystem/fs.op.funcs/fs.op.system_complete/system_complete.pass.cpp (original)
+++ libcxx/trunk/test/std/experimental/filesystem/fs.op.funcs/fs.op.system_complete/system_complete.pass.cpp (removed)
@@ -1,58 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-//                     The LLVM Compiler Infrastructure
-//
-// This file is dual licensed under the MIT and the University of Illinois Open
-// Source Licenses. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-
-// UNSUPPORTED: c++98, c++03
-
-// <experimental/filesystem>
-
-// path system_complete(const path& p);
-// path system_complete(const path& p, error_code& ec);
-
-// Note: For POSIX based operating systems, 'system_complete(p)' has the
-// same semantics as 'absolute(p, current_path())'.
-
-#include "filesystem_include.hpp"
-#include <type_traits>
-#include <cassert>
-
-#include "test_macros.h"
-#include "rapid-cxx-test.hpp"
-#include "filesystem_test_helper.hpp"
-
-using namespace fs;
-
-TEST_SUITE(filesystem_system_complete_test_suite)
-
-TEST_CASE(signature_test)
-{
-    const path p; ((void)p);
-    std::error_code ec; ((void)ec);
-    ASSERT_NOT_NOEXCEPT(system_complete(p));
-    ASSERT_NOT_NOEXCEPT(system_complete(p, ec));
-}
-
-
-TEST_CASE(basic_system_complete_tests)
-{
-    const path testCases[] = {
-        "//net/foo", //  has_root_name() &&  has_root_directory()
-        "/foo",      // !has_root_name() &&  has_root_directory()
-        "//net",     //  has_root_name() && !has_root_directory()
-        "bar/baz"    // !has_root_name() && !has_root_directory()
-    };
-    const path base = current_path();
-    for (auto& p : testCases) {
-        const path ret = system_complete(p);
-        const path expect = absolute(p, base);
-        TEST_CHECK(ret.is_absolute());
-        TEST_CHECK(ret == expect);
-    }
-}
-
-TEST_SUITE_END()

Added: libcxx/trunk/test/std/experimental/filesystem/fs.op.funcs/fs.op.weakly_canonical/weakly_canonical.pass.cpp
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/std/experimental/filesystem/fs.op.funcs/fs.op.weakly_canonical/weakly_canonical.pass.cpp?rev=329028&view=auto
==============================================================================
--- libcxx/trunk/test/std/experimental/filesystem/fs.op.funcs/fs.op.weakly_canonical/weakly_canonical.pass.cpp (added)
+++ libcxx/trunk/test/std/experimental/filesystem/fs.op.funcs/fs.op.weakly_canonical/weakly_canonical.pass.cpp Mon Apr  2 16:03:41 2018
@@ -0,0 +1,76 @@
+//===----------------------------------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++98, c++03
+
+// <experimental/filesystem>
+
+// path weakly_canonical(const path& p);
+// path weakly_canonical(const path& p, error_code& ec);
+
+#include "filesystem_include.hpp"
+#include <type_traits>
+#include <vector>
+#include <iostream>
+#include <cassert>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+#include "count_new.hpp"
+#include "filesystem_test_helper.hpp"
+
+
+int main() {
+  // clang-format off
+  struct {
+    std::string input;
+    std::string expect;
+  } TestCases[] = {
+      {"", fs::current_path()},
+      {".", fs::current_path()},
+      {"/", "/"},
+      {"/foo", "/foo"},
+      {"/.", "/"},
+      {"/./", "/"},
+      {"a/b", fs::current_path() / "a/b"},
+      {"a", fs::current_path() / "a"},
+      {"a/b/", fs::current_path() / "a/b/"},
+      {StaticEnv::File, StaticEnv::File},
+      {StaticEnv::Dir, StaticEnv::Dir},
+      {StaticEnv::SymlinkToDir, StaticEnv::Dir},
+      {StaticEnv::SymlinkToDir / "dir2/.", StaticEnv::Dir / "dir2"},
+      // FIXME? If the trailing separator occurs in a part of the path that exists,
+      // it is ommitted. Otherwise it is added to the end of the result.
+      {StaticEnv::SymlinkToDir / "dir2/./", StaticEnv::Dir / "dir2"},
+      {StaticEnv::SymlinkToDir / "dir2/DNE/./", StaticEnv::Dir / "dir2/DNE/"},
+      {StaticEnv::SymlinkToDir / "dir2", StaticEnv::Dir2},
+      {StaticEnv::SymlinkToDir / "dir2/../dir2/DNE/..", StaticEnv::Dir2 / ""},
+      {StaticEnv::SymlinkToDir / "dir2/dir3/../DNE/DNE2", StaticEnv::Dir2 / "DNE/DNE2"},
+      {StaticEnv::Dir / "../dir1", StaticEnv::Dir},
+      {StaticEnv::Dir / "./.", StaticEnv::Dir},
+      {StaticEnv::Dir / "DNE/../foo", StaticEnv::Dir / "foo"}
+  };
+  // clang-format on
+  int ID = 0;
+  bool Failed = false;
+  for (auto& TC : TestCases) {
+    ++ID;
+    fs::path p(TC.input);
+    const fs::path output = fs::weakly_canonical(p);
+    if (!PathEq(output, TC.expect)) {
+      Failed = true;
+      std::cerr << "TEST CASE #" << ID << " FAILED: \n";
+      std::cerr << "  Input: '" << TC.input << "'\n";
+      std::cerr << "  Expected: '" << TC.expect << "'\n";
+      std::cerr << "  Output: '" << output.native() << "'";
+      std::cerr << std::endl;
+    }
+  }
+  return Failed;
+}

Modified: libcxx/trunk/test/support/filesystem_test_helper.hpp
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/support/filesystem_test_helper.hpp?rev=329028&r1=329027&r2=329028&view=diff
==============================================================================
--- libcxx/trunk/test/support/filesystem_test_helper.hpp (original)
+++ libcxx/trunk/test/support/filesystem_test_helper.hpp Mon Apr  2 16:03:41 2018
@@ -9,7 +9,6 @@
 #include <random>
 #include <chrono>
 
-
 // static test helpers
 
 #ifndef LIBCXX_FILESYSTEM_STATIC_TEST_ROOT
@@ -400,4 +399,8 @@ void SleepFor(std::chrono::seconds dur)
         ;
 }
 
+inline bool PathEq(fs::path const& LHS, fs::path const& RHS) {
+  return LHS.native() == RHS.native();
+}
+
 #endif /* FILESYSTEM_TEST_HELPER_HPP */

Added: libcxx/trunk/test/support/verbose_assert.h
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/support/verbose_assert.h?rev=329028&view=auto
==============================================================================
--- libcxx/trunk/test/support/verbose_assert.h (added)
+++ libcxx/trunk/test/support/verbose_assert.h Mon Apr  2 16:03:41 2018
@@ -0,0 +1,222 @@
+#ifndef TEST_SUPPORT_VERBOSE_ASSERT
+#define TEST_SUPPORT_VERBOSE_ASSERT
+
+#include <iostream>
+#include <cstdio>
+#include <sstream>
+#include <string>
+#include "test_macros.h"
+
+namespace verbose_assert {
+
+typedef std::basic_ostream<char>&(EndLType)(std::basic_ostream<char>&);
+
+template <class Stream, class Tp,
+    class = decltype(std::declval<Stream&>() << std::declval<Tp const&>())>
+std::true_type IsStreamableImp(int);
+template <class Stream, class Tp> std::false_type IsStreamableImp(long);
+
+template <class Stream, class Tp>
+struct IsStreamable : decltype(IsStreamableImp<Stream, Tp>(0)) {};
+
+template <class Tp, int ST = (IsStreamable<decltype(std::cerr), Tp>::value ? 1
+        : (IsStreamable<decltype(std::wcerr), Tp>::value ? 2 : -1))>
+struct SelectStream {
+  static_assert(ST == -1, "specialization required for ST != -1");
+  static void Print(Tp const&) { std::clog << "Value Not Streamable!\n"; }
+};
+
+template <class Tp>
+struct SelectStream<Tp, 1> {
+  static void Print(Tp const& val) { std::cerr << val; }
+};
+
+template <class Tp>
+struct SelectStream<Tp, 2> {
+  static void Print(Tp const& val) { std::wcerr << val; }
+};
+
+struct AssertData {
+  AssertData(const char* xcheck, const char* xfile, const char* xfunc,
+             unsigned long xline, bool xpassed = true)
+      : passed(xpassed), check(xcheck), file(xfile), func(xfunc), line(xline),
+        msg() {}
+
+  AssertData& SetFailed(std::string xmsg = std::string()) {
+    msg = xmsg;
+    passed = false;
+    return *this;
+  }
+
+  void PrintFailed() const {
+    std::fprintf(stderr, "%s:%lu %s: Assertion '%s' failed.\n", file, line,
+                 func, check);
+    if (!msg.empty())
+      std::fprintf(stderr, "%s\n", msg.data());
+  }
+
+  bool passed;
+  const char* check;
+  const char* file;
+  const char* func;
+  unsigned long line;
+  std::string msg;
+};
+
+// AssertHandler is the class constructed by failing CHECK macros. AssertHandler
+// will log information about the failures and abort when it is destructed.
+class AssertHandler {
+public:
+  AssertHandler(AssertData const& Data)
+      : passed(Data.passed) {
+    if (!passed)
+      Data.PrintFailed();
+  }
+
+  ~AssertHandler() TEST_NOEXCEPT_FALSE {
+    if (!passed) {
+      error_log << std::endl;
+      std::abort();
+    }
+  }
+
+  class LogType {
+    friend class AssertHandler;
+
+    template <class Tp>
+    friend LogType& operator<<(LogType& log, Tp const& value) {
+      if (!log.is_disabled) {
+        SelectStream<Tp>::Print(value);
+      }
+      return log;
+    }
+
+    friend LogType& operator<<(LogType& log, EndLType* m) {
+      if (!log.is_disabled) {
+        SelectStream<EndLType*>::Print(m);
+      }
+      return log;
+    }
+
+  private:
+    LogType(bool disable) : is_disabled(disable) {}
+    bool is_disabled;
+
+    LogType(LogType const&);
+    LogType& operator=(LogType const&);
+  };
+
+  LogType& GetLog() {
+    if (passed)
+      return null_log;
+    return error_log;
+  }
+
+private:
+  static LogType null_log;
+  static LogType error_log;
+
+  AssertHandler& operator=(const AssertHandler&) = delete;
+  AssertHandler(const AssertHandler&) = delete;
+  AssertHandler() = delete;
+
+private:
+  bool passed;
+};
+
+AssertHandler::LogType AssertHandler::null_log(true);
+AssertHandler::LogType AssertHandler::error_log(false);
+
+template <class It1>
+std::string PrintRange(const char* Name, It1 F, It1 E) {
+  std::stringstream ss;
+  ss << "  " << Name << " = [";
+  while (F != E) {
+    ss << *F;
+    ++F;
+    if (F != E)
+      ss << ", ";
+  }
+  ss << "]\n";
+  return ss.str();
+}
+
+template <class Tp, class Up>
+std::string PrintMismatch(Tp const& LHS, Up const& RHS, int Elem) {
+  std::stringstream ss;
+  ss << "  Element " << Elem << " mismatched: `" << LHS << "` != `" << RHS
+     << "`!\n";
+  return ss.str();
+};
+
+struct EqualToComp {
+  template <class Tp, class Up>
+  bool operator()(Tp const& LHS, Up const& RHS) const {
+    return LHS == RHS;
+  }
+};
+
+template <class It1, class It2, class Comp>
+AssertData CheckCollectionsEqual(It1 F1, It1 E1, It2 F2, It2 E2,
+                                 AssertData Data, Comp C = EqualToComp()) {
+  const It1 F1Orig = F1;
+  const It2 F2Orig = F2;
+  bool Failed = false;
+  std::string ErrorMsg;
+  int Idx = 0;
+  while (F1 != E1 && F2 != E2) {
+    if (!(C(*F1, *F2))) {
+      ErrorMsg += PrintMismatch(*F1, *F2, Idx);
+      Failed = true;
+      break;
+    }
+    ++Idx;
+    ++F1;
+    ++F2;
+  }
+  if (!Failed && (F1 != E1 || F2 != E2)) {
+    ErrorMsg += "  Ranges have different sizes!\n";
+    Failed = true;
+  }
+  if (Failed) {
+    ErrorMsg += PrintRange("LHS", F1Orig, E1);
+    ErrorMsg += PrintRange("RHS", F2Orig, E2);
+    Data.SetFailed(ErrorMsg);
+  }
+  return Data;
+}
+} // namespace verbose_assert
+
+#ifdef __GNUC__
+#define ASSERT_FN_NAME() __PRETTY_FUNCTION__
+#else
+#define ASSERT_FN_NAME() __func__
+#endif
+
+#define DISPLAY(...) "    " #__VA_ARGS__ " = " << (__VA_ARGS__) << "\n"
+
+#define ASSERT(...)                                                            \
+  ::verbose_assert::AssertHandler(::verbose_assert::AssertData(                \
+    #__VA_ARGS__, __FILE__, ASSERT_FN_NAME(), __LINE__,(__VA_ARGS__))).GetLog()
+
+#define ASSERT_EQ(LHS, RHS) \
+  ASSERT(LHS == RHS) << DISPLAY(LHS) << DISPLAY(RHS)
+#define ASSERT_NEQ(LHS, RHS) \
+  ASSERT(LHS != RHS) << DISPLAY(LHS) << DISPLAY(RHS)
+#define ASSERT_PRED(PRED, LHS, RHS) \
+  ASSERT(PRED(LHS, RHS)) << DISPLAY(LHS) << DISPLAY(RHS)
+
+#define ASSERT_COLLECTION_EQ_COMP(F1, E1, F2, E2, Comp)                        \
+  (::verbose_assert::AssertHandler(                                            \
+       ::verbose_assert::CheckCollectionsEqual(                                \
+           F1, E1, F2, E2,                                                     \
+           ::verbose_assert::AssertData("CheckCollectionsEqual(" #F1 ", " #E1  \
+                                        ", " #F2 ", " #E2 ")",                 \
+                                        __FILE__, ASSERT_FN_NAME(), __LINE__), \
+           Comp))                                                              \
+       .GetLog())
+
+#define ASSERT_COLLECTION_EQ(F1, E1, F2, E2)                                   \
+  ASSERT_COLLECTION_EQ_COMP(F1, E1, F2, E2, ::verbose_assert::EqualToComp())
+
+#endif

Modified: libcxx/trunk/www/cxx1z_status.html
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/www/cxx1z_status.html?rev=329028&r1=329027&r2=329028&view=diff
==============================================================================
--- libcxx/trunk/www/cxx1z_status.html (original)
+++ libcxx/trunk/www/cxx1z_status.html Mon Apr  2 16:03:41 2018
@@ -111,7 +111,7 @@
 	<tr><td><a href="https://wg21.link/p0180r2">p0180r2</a></td><td>LWG</td><td>Reserve a New Library Namespace for Future Standardization</td><td>Oulu</td><td><i>Nothing to do</i></td><td>n/a</td></tr>
 	<tr><td><a href="https://wg21.link/p0181r1">p0181r1</a></td><td>LWG</td><td>Ordered by Default</td><td>Oulu</td><td><i>Removed in Kona</i></td><td>n/a</td></tr>
 	<tr><td><a href="https://wg21.link/p0209r2">p0209r2</a></td><td>LWG</td><td>make_from_tuple: apply for construction</td><td>Oulu</td><td>Complete</td><td>3.9</td></tr>
-	<tr><td><a href="https://wg21.link/p0219r1">p0219r1</a></td><td>LWG</td><td>Relative Paths for Filesystem</td><td>Oulu</td><td></td><td></td></tr>
+	<tr><td><a href="https://wg21.link/p0219r1">p0219r1</a></td><td>LWG</td><td>Relative Paths for Filesystem</td><td>Oulu</td><td>Complete</td><td>7.0</td></tr>
 	<tr><td><a href="https://wg21.link/p0254r2">p0254r2</a></td><td>LWG</td><td>Integrating std::string_view and std::string</td><td>Oulu</td><td>Complete</td><td>4.0</td></tr>
 	<tr><td><a href="https://wg21.link/p0258r2">p0258r2</a></td><td>LWG</td><td>has_unique_object_representations</td><td>Oulu</td><td>Complete</td><td>6.0</td></tr>
 	<tr><td><a href="https://wg21.link/p0295r0">p0295r0</a></td><td>LWG</td><td>Adopt Selected Library Fundamentals V2 Components for C++17</td><td>Oulu</td><td>Complete</td><td>4.0</td></tr>
@@ -153,7 +153,7 @@
 	<tr><td><a href="https://wg21.link/P0433R2">P0433R2</a></td><td>LWG</td><td>Toward a resolution of US7 and US14: Integrating template deduction for class templates into the standard library</td><td>Kona</td><td><i>In progress</i></td><td>7.0</td></tr>
 	<tr><td><a href="https://wg21.link/P0452R1">P0452R1</a></td><td>LWG</td><td>Unifying <numeric> Parallel Algorithms</td><td>Kona</td><td></td><td></td></tr>
 	<tr><td><a href="https://wg21.link/P0467R2">P0467R2</a></td><td>LWG</td><td>Iterator Concerns for Parallel Algorithms</td><td>Kona</td><td></td><td></td></tr>
-	<tr><td><a href="https://wg21.link/P0492R2">P0492R2</a></td><td>LWG</td><td>Proposed Resolution of C++17 National Body Comments for Filesystems</td><td>Kona</td><td></td><td></td></tr>
+	<tr><td><a href="https://wg21.link/P0492R2">P0492R2</a></td><td>LWG</td><td>Proposed Resolution of C++17 National Body Comments for Filesystems</td><td>Kona</td><td>Complete</td><td>7.0</td></tr>
 	<tr><td><a href="https://wg21.link/P0518R1">P0518R1</a></td><td>LWG</td><td>Allowing copies as arguments to function objects given to parallel algorithms in response to CH11</td><td>Kona</td><td></td><td></td></tr>
 	<tr><td><a href="https://wg21.link/P0523R1">P0523R1</a></td><td>LWG</td><td>Wording for CH 10: Complexity of parallel algorithms</td><td>Kona</td><td></td><td></td></tr>
 	<tr><td><a href="https://wg21.link/P0548R1">P0548R1</a></td><td>LWG</td><td>common_type and duration</td><td>Kona</td><td>Complete</td><td>5.0</td></tr>

Modified: libcxx/trunk/www/cxx2a_status.html
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/www/cxx2a_status.html?rev=329028&r1=329027&r2=329028&view=diff
==============================================================================
--- libcxx/trunk/www/cxx2a_status.html (original)
+++ libcxx/trunk/www/cxx2a_status.html Mon Apr  2 16:03:41 2018
@@ -164,7 +164,7 @@
 	<tr><td><a href="https://wg21.link/LWG3015">3015</a></td><td><tt>copy_options::<i>unspecified</i></tt> underspecified</td><td>Jacksonville</td><td><i>Nothing to do</i></td></tr>
 	<tr><td><a href="https://wg21.link/LWG3017">3017</a></td><td><tt>list splice</tt> functions should use <tt>addressof</tt></td><td>Jacksonville</td><td></td></tr>
 	<tr><td><a href="https://wg21.link/LWG3020">3020</a></td><td>[networking.ts] Remove spurious nested <tt>value_type</tt> buffer sequence requirement</td><td>Jacksonville</td><td></td></tr>
-	<tr><td><a href="https://wg21.link/LWG3026">3026</a></td><td><tt>filesystem::weakly_canonical</tt> still defined in terms of <tt>canonical(p, base)</tt></td><td>Jacksonville</td><td></td></tr>
+	<tr><td><a href="https://wg21.link/LWG3026">3026</a></td><td><tt>filesystem::weakly_canonical</tt> still defined in terms of <tt>canonical(p, base)</tt></td><td>Jacksonville</td><td>Complete</td></tr>
 	<tr><td><a href="https://wg21.link/LWG3030">3030</a></td><td>Who shall meet the requirements of <tt>try_lock</tt>?</td><td>Jacksonville</td><td><i>Nothing to do</i></td></tr>
 	<tr><td><a href="https://wg21.link/LWG3034">3034</a></td><td>P0767R1 breaks previously-standard-layout types</td><td>Jacksonville</td><td>Complete</td></tr>
 	<tr><td><a href="https://wg21.link/LWG3035">3035</a></td><td><tt>std::allocator</tt>'s constructors should be <tt>constexpr</tt></td><td>Jacksonville</td><td>Complete</td></tr>




More information about the cfe-commits mailing list