about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--absl/container/BUILD.bazel1
-rw-r--r--absl/container/CMakeLists.txt2
-rw-r--r--absl/container/inlined_vector.h2
-rw-r--r--absl/container/internal/inlined_vector.h8
-rw-r--r--absl/container/internal/test_instance_tracker.h10
-rw-r--r--absl/container/internal/test_instance_tracker_test.cc2
-rw-r--r--absl/copts/GENERATED_AbseilCopts.cmake2
-rw-r--r--absl/copts/GENERATED_copts.bzl2
-rw-r--r--absl/copts/copts.py2
-rw-r--r--absl/hash/BUILD.bazel2
-rw-r--r--absl/time/internal/cctz/include/cctz/civil_time_detail.h38
-rw-r--r--absl/time/internal/cctz/src/cctz_benchmark.cc50
-rw-r--r--absl/types/BUILD.bazel24
-rw-r--r--absl/types/CMakeLists.txt26
-rw-r--r--absl/types/compare.h506
-rw-r--r--absl/types/compare_test.cc315
-rwxr-xr-xci/linux_clang-latest_libcxx_bazel.sh2
-rwxr-xr-xci/linux_clang-latest_libstdcxx_bazel.sh2
18 files changed, 982 insertions, 14 deletions
diff --git a/absl/container/BUILD.bazel b/absl/container/BUILD.bazel
index f25a9ff96b..0488857e7c 100644
--- a/absl/container/BUILD.bazel
+++ b/absl/container/BUILD.bazel
@@ -213,6 +213,7 @@ cc_library(
     visibility = [
         "//absl:__subpackages__",
     ],
+    deps = ["//absl/types:compare"],
 )
 
 cc_test(
diff --git a/absl/container/CMakeLists.txt b/absl/container/CMakeLists.txt
index 9531d7f924..1e203dbf1c 100644
--- a/absl/container/CMakeLists.txt
+++ b/absl/container/CMakeLists.txt
@@ -204,6 +204,8 @@ absl_cc_library(
     "internal/test_instance_tracker.cc"
   COPTS
     ${ABSL_DEFAULT_COPTS}
+  DEPS
+    absl::compare
   TESTONLY
 )
 
diff --git a/absl/container/inlined_vector.h b/absl/container/inlined_vector.h
index 34e9aa0a12..16865272c4 100644
--- a/absl/container/inlined_vector.h
+++ b/absl/container/inlined_vector.h
@@ -69,7 +69,7 @@ class InlinedVector {
   static_assert(
       N > 0, "InlinedVector cannot be instantiated with `0` inlined elements.");
 
-  using Storage = inlined_vector_internal::Storage<InlinedVector>;
+  using Storage = inlined_vector_internal::Storage<T, N, A>;
   using AllocatorTraits = typename Storage::AllocatorTraits;
 
   template <typename Iterator>
diff --git a/absl/container/internal/inlined_vector.h b/absl/container/internal/inlined_vector.h
index b8b4f4ce3b..6a5a75be1f 100644
--- a/absl/container/internal/inlined_vector.h
+++ b/absl/container/internal/inlined_vector.h
@@ -31,12 +31,8 @@ using IsAtLeastForwardIterator = std::is_convertible<
     typename std::iterator_traits<Iterator>::iterator_category,
     std::forward_iterator_tag>;
 
-template <typename InlinedVector>
-class Storage;
-
-template <template <typename, size_t, typename> class InlinedVector, typename T,
-          size_t N, typename A>
-class Storage<InlinedVector<T, N, A>> {
+template <typename T, size_t N, typename A>
+class Storage {
  public:
   using allocator_type = A;
   using value_type = typename allocator_type::value_type;
diff --git a/absl/container/internal/test_instance_tracker.h b/absl/container/internal/test_instance_tracker.h
index 032d16d33f..3d4b2980ea 100644
--- a/absl/container/internal/test_instance_tracker.h
+++ b/absl/container/internal/test_instance_tracker.h
@@ -18,6 +18,8 @@
 #include <cstdlib>
 #include <ostream>
 
+#include "absl/types/compare.h"
+
 namespace absl {
 namespace test_internal {
 
@@ -96,6 +98,14 @@ class BaseCountedInstance {
     return value_ >= x.value_;
   }
 
+  absl::weak_ordering compare(const BaseCountedInstance& x) const {
+    ++num_comparisons_;
+    return value_ < x.value_
+               ? absl::weak_ordering::less
+               : value_ == x.value_ ? absl::weak_ordering::equivalent
+                                    : absl::weak_ordering::greater;
+  }
+
   int value() const {
     if (!is_live_) std::abort();
     return value_;
diff --git a/absl/container/internal/test_instance_tracker_test.cc b/absl/container/internal/test_instance_tracker_test.cc
index 091f428da4..1c6a4fa715 100644
--- a/absl/container/internal/test_instance_tracker_test.cc
+++ b/absl/container/internal/test_instance_tracker_test.cc
@@ -174,6 +174,8 @@ TEST(TestInstanceTracker, Comparisons) {
   EXPECT_EQ(5, tracker.comparisons());
   EXPECT_FALSE(one >= two);
   EXPECT_EQ(6, tracker.comparisons());
+  EXPECT_TRUE(one.compare(two) < 0);  // NOLINT
+  EXPECT_EQ(7, tracker.comparisons());
 
   tracker.ResetCopiesMovesSwaps();
   EXPECT_EQ(0, tracker.comparisons());
diff --git a/absl/copts/GENERATED_AbseilCopts.cmake b/absl/copts/GENERATED_AbseilCopts.cmake
index f968ba72a7..c4948d42d4 100644
--- a/absl/copts/GENERATED_AbseilCopts.cmake
+++ b/absl/copts/GENERATED_AbseilCopts.cmake
@@ -41,6 +41,7 @@ list(APPEND ABSL_CLANG_CL_FLAGS
     "-Wno-unreachable-code"
     "-Wno-unused-macros"
     "-Wno-weak-vtables"
+    "-Wno-zero-as-null-pointer-constant"
     "-Wbitfield-enum-conversion"
     "-Wbool-conversion"
     "-Wconstant-conversion"
@@ -149,6 +150,7 @@ list(APPEND ABSL_LLVM_FLAGS
     "-Wno-unreachable-code"
     "-Wno-unused-macros"
     "-Wno-weak-vtables"
+    "-Wno-zero-as-null-pointer-constant"
     "-Wbitfield-enum-conversion"
     "-Wbool-conversion"
     "-Wconstant-conversion"
diff --git a/absl/copts/GENERATED_copts.bzl b/absl/copts/GENERATED_copts.bzl
index 2b4790a8ae..422b3a9b1c 100644
--- a/absl/copts/GENERATED_copts.bzl
+++ b/absl/copts/GENERATED_copts.bzl
@@ -42,6 +42,7 @@ ABSL_CLANG_CL_FLAGS = [
     "-Wno-unreachable-code",
     "-Wno-unused-macros",
     "-Wno-weak-vtables",
+    "-Wno-zero-as-null-pointer-constant",
     "-Wbitfield-enum-conversion",
     "-Wbool-conversion",
     "-Wconstant-conversion",
@@ -150,6 +151,7 @@ ABSL_LLVM_FLAGS = [
     "-Wno-unreachable-code",
     "-Wno-unused-macros",
     "-Wno-weak-vtables",
+    "-Wno-zero-as-null-pointer-constant",
     "-Wbitfield-enum-conversion",
     "-Wbool-conversion",
     "-Wconstant-conversion",
diff --git a/absl/copts/copts.py b/absl/copts/copts.py
index 8a0ecf6b75..5bede34cb8 100644
--- a/absl/copts/copts.py
+++ b/absl/copts/copts.py
@@ -70,6 +70,8 @@ LLVM_DISABLE_WARNINGS_FLAGS = [
     # Causes warnings on include guards
     "-Wno-unused-macros",
     "-Wno-weak-vtables",
+    # Causes warnings on usage of types/compare.h comparison operators.
+    "-Wno-zero-as-null-pointer-constant",
     ###
     # Implicit conversion warnings turned off by -Wno-conversion
     # which are re-enabled below.
diff --git a/absl/hash/BUILD.bazel b/absl/hash/BUILD.bazel
index 8c2daf703b..e1e6eae851 100644
--- a/absl/hash/BUILD.bazel
+++ b/absl/hash/BUILD.bazel
@@ -35,10 +35,10 @@ cc_library(
     copts = ABSL_DEFAULT_COPTS,
     linkopts = ABSL_DEFAULT_LINKOPTS,
     deps = [
-        ":city",
         "//absl/base:core_headers",
         "//absl/base:endian",
         "//absl/container:fixed_array",
+        "//absl/hash:city",
         "//absl/meta:type_traits",
         "//absl/numeric:int128",
         "//absl/strings",
diff --git a/absl/time/internal/cctz/include/cctz/civil_time_detail.h b/absl/time/internal/cctz/include/cctz/civil_time_detail.h
index bb191b3fae..3e24340339 100644
--- a/absl/time/internal/cctz/include/cctz/civil_time_detail.h
+++ b/absl/time/internal/cctz/include/cctz/civil_time_detail.h
@@ -555,13 +555,43 @@ CONSTEXPR_F weekday get_weekday(const civil_day& cd) noexcept {
 ////////////////////////////////////////////////////////////////////////
 
 CONSTEXPR_F civil_day next_weekday(civil_day cd, weekday wd) noexcept {
-  do { cd += 1; } while (get_weekday(cd) != wd);
-  return cd;
+  CONSTEXPR_D weekday k_weekdays_forw[14] = {
+      weekday::monday,    weekday::tuesday,  weekday::wednesday,
+      weekday::thursday,  weekday::friday,   weekday::saturday,
+      weekday::sunday,    weekday::monday,   weekday::tuesday,
+      weekday::wednesday, weekday::thursday, weekday::friday,
+      weekday::saturday,  weekday::sunday,
+  };
+  weekday base = get_weekday(cd);
+  for (int i = 0;; ++i) {
+    if (base == k_weekdays_forw[i]) {
+      for (int j = i + 1;; ++j) {
+        if (wd == k_weekdays_forw[j]) {
+          return cd + (j - i);
+        }
+      }
+    }
+  }
 }
 
 CONSTEXPR_F civil_day prev_weekday(civil_day cd, weekday wd) noexcept {
-  do { cd -= 1; } while (get_weekday(cd) != wd);
-  return cd;
+  CONSTEXPR_D weekday k_weekdays_back[14] = {
+      weekday::sunday,   weekday::saturday,  weekday::friday,
+      weekday::thursday, weekday::wednesday, weekday::tuesday,
+      weekday::monday,   weekday::sunday,    weekday::saturday,
+      weekday::friday,   weekday::thursday,  weekday::wednesday,
+      weekday::tuesday,  weekday::monday,
+  };
+  weekday base = get_weekday(cd);
+  for (int i = 0;; ++i) {
+    if (base == k_weekdays_back[i]) {
+      for (int j = i + 1;; ++j) {
+        if (wd == k_weekdays_back[j]) {
+          return cd - (j - i);
+        }
+      }
+    }
+  }
 }
 
 CONSTEXPR_F int get_yearday(const civil_day& cd) noexcept {
diff --git a/absl/time/internal/cctz/src/cctz_benchmark.cc b/absl/time/internal/cctz/src/cctz_benchmark.cc
index 445366eae5..a40f504e06 100644
--- a/absl/time/internal/cctz/src/cctz_benchmark.cc
+++ b/absl/time/internal/cctz/src/cctz_benchmark.cc
@@ -47,6 +47,56 @@ void BM_Step_Days(benchmark::State& state) {
 }
 BENCHMARK(BM_Step_Days);
 
+void BM_GetWeekday(benchmark::State& state) {
+  const cctz::civil_day c(2014, 8, 22);
+  while (state.KeepRunning()) {
+    benchmark::DoNotOptimize(cctz::get_weekday(c));
+  }
+}
+BENCHMARK(BM_GetWeekday);
+
+void BM_NextWeekday(benchmark::State& state) {
+  const cctz::civil_day kStart(2014, 8, 22);
+  const cctz::civil_day kDays[7] = {
+      kStart + 0, kStart + 1, kStart + 2, kStart + 3,
+      kStart + 4, kStart + 5, kStart + 6,
+  };
+  const cctz::weekday kWeekdays[7] = {
+      cctz::weekday::monday,   cctz::weekday::tuesday, cctz::weekday::wednesday,
+      cctz::weekday::thursday, cctz::weekday::friday,  cctz::weekday::saturday,
+      cctz::weekday::sunday,
+  };
+  while (state.KeepRunningBatch(7 * 7)) {
+    for (const auto from : kDays) {
+      for (const auto to : kWeekdays) {
+        benchmark::DoNotOptimize(cctz::next_weekday(from, to));
+      }
+    }
+  }
+}
+BENCHMARK(BM_NextWeekday);
+
+void BM_PrevWeekday(benchmark::State& state) {
+  const cctz::civil_day kStart(2014, 8, 22);
+  const cctz::civil_day kDays[7] = {
+      kStart + 0, kStart + 1, kStart + 2, kStart + 3,
+      kStart + 4, kStart + 5, kStart + 6,
+  };
+  const cctz::weekday kWeekdays[7] = {
+      cctz::weekday::monday,   cctz::weekday::tuesday, cctz::weekday::wednesday,
+      cctz::weekday::thursday, cctz::weekday::friday,  cctz::weekday::saturday,
+      cctz::weekday::sunday,
+  };
+  while (state.KeepRunningBatch(7 * 7)) {
+    for (const auto from : kDays) {
+      for (const auto to : kWeekdays) {
+        benchmark::DoNotOptimize(cctz::prev_weekday(from, to));
+      }
+    }
+  }
+}
+BENCHMARK(BM_PrevWeekday);
+
 const char RFC3339_full[] = "%Y-%m-%dT%H:%M:%E*S%Ez";
 const char RFC3339_sec[] = "%Y-%m-%dT%H:%M:%S%Ez";
 
diff --git a/absl/types/BUILD.bazel b/absl/types/BUILD.bazel
index a25487184b..134f9f227e 100644
--- a/absl/types/BUILD.bazel
+++ b/absl/types/BUILD.bazel
@@ -308,3 +308,27 @@ cc_test(
         "@com_google_googletest//:gtest_main",
     ],
 )
+
+cc_library(
+    name = "compare",
+    hdrs = ["compare.h"],
+    copts = ABSL_DEFAULT_COPTS,
+    deps = [
+        "//absl/base:core_headers",
+        "//absl/meta:type_traits",
+    ],
+)
+
+cc_test(
+    name = "compare_test",
+    size = "small",
+    srcs = [
+        "compare_test.cc",
+    ],
+    copts = ABSL_TEST_COPTS,
+    deps = [
+        ":compare",
+        "//absl/base",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
diff --git a/absl/types/CMakeLists.txt b/absl/types/CMakeLists.txt
index 9da94eb561..4ce685da4e 100644
--- a/absl/types/CMakeLists.txt
+++ b/absl/types/CMakeLists.txt
@@ -299,6 +299,32 @@ absl_cc_test(
     gmock_main
 )
 
+absl_cc_library(
+  NAME
+    compare
+  HDRS
+    "compare.h"
+  COPTS
+    ${ABSL_DEFAULT_COPTS}
+  DEPS
+    absl::core_headers
+    absl::type_traits
+  PUBLIC
+)
+
+absl_cc_test(
+  NAME
+    compare_test
+  SRCS
+    "compare_test.cc"
+  COPTS
+    ${ABSL_TEST_COPTS}
+  DEPS
+    absl::base
+    absl::compare
+    gmock_main
+)
+
 # TODO(cohenjon,zhangxy) Figure out why this test is failing on gcc 4.8
 if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9)
 else()
diff --git a/absl/types/compare.h b/absl/types/compare.h
new file mode 100644
index 0000000000..7fed308140
--- /dev/null
+++ b/absl/types/compare.h
@@ -0,0 +1,506 @@
+// Copyright 2018 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// -----------------------------------------------------------------------------
+// compare.h
+// -----------------------------------------------------------------------------
+//
+// This header file defines the `absl::weak_equality`, `absl::strong_equality`,
+// `absl::partial_ordering`, `absl::weak_ordering`, and `absl::strong_ordering`
+// types for storing the results of three way comparisons.
+//
+// Example:
+//   absl::weak_ordering compare(const std::string& a, const std::string& b);
+//
+// These are C++11 compatible versions of the C++20 corresponding types
+// (`std::weak_equality`, etc.) and are designed to be drop-in replacements
+// for code compliant with C++20.
+
+#ifndef ABSL_TYPES_COMPARE_H_
+#define ABSL_TYPES_COMPARE_H_
+
+#include <cstddef>
+#include <cstdint>
+#include <cstdlib>
+#include <type_traits>
+
+#include "absl/base/attributes.h"
+#include "absl/meta/type_traits.h"
+
+namespace absl {
+namespace compare_internal {
+
+using value_type = int8_t;
+
+template <typename T>
+struct Fail {
+  static_assert(sizeof(T) < 0, "Only literal `0` is allowed.");
+};
+
+// We need the NullPtrT template to avoid triggering the modernize-use-nullptr
+// ClangTidy warning in user code.
+template <typename NullPtrT = std::nullptr_t>
+struct OnlyLiteralZero {
+  constexpr OnlyLiteralZero(NullPtrT) noexcept {}  // NOLINT
+
+  // Fails compilation when `nullptr` or integral type arguments other than
+  // `int` are passed. This constructor doesn't accept `int` because literal `0`
+  // has type `int`. Literal `0` arguments will be implicitly converted to
+  // `std::nullptr_t` and accepted by the above constructor, while other `int`
+  // arguments will fail to be converted and cause compilation failure.
+  template <
+      typename T,
+      typename = typename std::enable_if<
+          std::is_same<T, std::nullptr_t>::value ||
+          (std::is_integral<T>::value && !std::is_same<T, int>::value)>::type,
+      typename = typename Fail<T>::type>
+  OnlyLiteralZero(T);  // NOLINT
+};
+
+enum class eq : value_type {
+  equal = 0,
+  equivalent = equal,
+  nonequal = 1,
+  nonequivalent = nonequal,
+};
+
+enum class ord : value_type { less = -1, greater = 1 };
+
+enum class ncmp : value_type { unordered = -127 };
+
+// These template base classes allow for defining the values of the constants
+// in the header file (for performance) without using inline variables (which
+// aren't available in C++11).
+template <typename T>
+struct weak_equality_base {
+  ABSL_CONST_INIT static const T equivalent;
+  ABSL_CONST_INIT static const T nonequivalent;
+};
+template <typename T>
+const T weak_equality_base<T>::equivalent(eq::equivalent);
+template <typename T>
+const T weak_equality_base<T>::nonequivalent(eq::nonequivalent);
+
+template <typename T>
+struct strong_equality_base {
+  ABSL_CONST_INIT static const T equal;
+  ABSL_CONST_INIT static const T nonequal;
+  ABSL_CONST_INIT static const T equivalent;
+  ABSL_CONST_INIT static const T nonequivalent;
+};
+template <typename T>
+const T strong_equality_base<T>::equal(eq::equal);
+template <typename T>
+const T strong_equality_base<T>::nonequal(eq::nonequal);
+template <typename T>
+const T strong_equality_base<T>::equivalent(eq::equivalent);
+template <typename T>
+const T strong_equality_base<T>::nonequivalent(eq::nonequivalent);
+
+template <typename T>
+struct partial_ordering_base {
+  ABSL_CONST_INIT static const T less;
+  ABSL_CONST_INIT static const T equivalent;
+  ABSL_CONST_INIT static const T greater;
+  ABSL_CONST_INIT static const T unordered;
+};
+template <typename T>
+const T partial_ordering_base<T>::less(ord::less);
+template <typename T>
+const T partial_ordering_base<T>::equivalent(eq::equivalent);
+template <typename T>
+const T partial_ordering_base<T>::greater(ord::greater);
+template <typename T>
+const T partial_ordering_base<T>::unordered(ncmp::unordered);
+
+template <typename T>
+struct weak_ordering_base {
+  ABSL_CONST_INIT static const T less;
+  ABSL_CONST_INIT static const T equivalent;
+  ABSL_CONST_INIT static const T greater;
+};
+template <typename T>
+const T weak_ordering_base<T>::less(ord::less);
+template <typename T>
+const T weak_ordering_base<T>::equivalent(eq::equivalent);
+template <typename T>
+const T weak_ordering_base<T>::greater(ord::greater);
+
+template <typename T>
+struct strong_ordering_base {
+  ABSL_CONST_INIT static const T less;
+  ABSL_CONST_INIT static const T equal;
+  ABSL_CONST_INIT static const T equivalent;
+  ABSL_CONST_INIT static const T greater;
+};
+template <typename T>
+const T strong_ordering_base<T>::less(ord::less);
+template <typename T>
+const T strong_ordering_base<T>::equal(eq::equal);
+template <typename T>
+const T strong_ordering_base<T>::equivalent(eq::equivalent);
+template <typename T>
+const T strong_ordering_base<T>::greater(ord::greater);
+
+}  // namespace compare_internal
+
+class weak_equality
+    : public compare_internal::weak_equality_base<weak_equality> {
+  explicit constexpr weak_equality(compare_internal::eq v) noexcept
+      : value_(static_cast<compare_internal::value_type>(v)) {}
+  friend struct compare_internal::weak_equality_base<weak_equality>;
+
+ public:
+  // Comparisons
+  friend constexpr bool operator==(
+      weak_equality v, compare_internal::OnlyLiteralZero<>) noexcept {
+    return v.value_ == 0;
+  }
+  friend constexpr bool operator!=(
+      weak_equality v, compare_internal::OnlyLiteralZero<>) noexcept {
+    return v.value_ != 0;
+  }
+  friend constexpr bool operator==(compare_internal::OnlyLiteralZero<>,
+                                   weak_equality v) noexcept {
+    return 0 == v.value_;
+  }
+  friend constexpr bool operator!=(compare_internal::OnlyLiteralZero<>,
+                                   weak_equality v) noexcept {
+    return 0 != v.value_;
+  }
+
+ private:
+  compare_internal::value_type value_;
+};
+
+class strong_equality
+    : public compare_internal::strong_equality_base<strong_equality> {
+  explicit constexpr strong_equality(compare_internal::eq v) noexcept
+      : value_(static_cast<compare_internal::value_type>(v)) {}
+  friend struct compare_internal::strong_equality_base<strong_equality>;
+
+ public:
+  // Conversion
+  constexpr operator weak_equality() const noexcept {  // NOLINT
+    return value_ == 0 ? weak_equality::equivalent
+                       : weak_equality::nonequivalent;
+  }
+  // Comparisons
+  friend constexpr bool operator==(
+      strong_equality v, compare_internal::OnlyLiteralZero<>) noexcept {
+    return v.value_ == 0;
+  }
+  friend constexpr bool operator!=(
+      strong_equality v, compare_internal::OnlyLiteralZero<>) noexcept {
+    return v.value_ != 0;
+  }
+  friend constexpr bool operator==(compare_internal::OnlyLiteralZero<>,
+                                   strong_equality v) noexcept {
+    return 0 == v.value_;
+  }
+  friend constexpr bool operator!=(compare_internal::OnlyLiteralZero<>,
+                                   strong_equality v) noexcept {
+    return 0 != v.value_;
+  }
+
+ private:
+  compare_internal::value_type value_;
+};
+
+class partial_ordering
+    : public compare_internal::partial_ordering_base<partial_ordering> {
+  explicit constexpr partial_ordering(compare_internal::eq v) noexcept
+      : value_(static_cast<compare_internal::value_type>(v)) {}
+  explicit constexpr partial_ordering(compare_internal::ord v) noexcept
+      : value_(static_cast<compare_internal::value_type>(v)) {}
+  explicit constexpr partial_ordering(compare_internal::ncmp v) noexcept
+      : value_(static_cast<compare_internal::value_type>(v)) {}
+  friend struct compare_internal::partial_ordering_base<partial_ordering>;
+
+  constexpr bool is_ordered() const noexcept {
+    return value_ !=
+           compare_internal::value_type(compare_internal::ncmp::unordered);
+  }
+
+ public:
+  // Conversion
+  constexpr operator weak_equality() const noexcept {  // NOLINT
+    return value_ == 0 ? weak_equality::equivalent
+                       : weak_equality::nonequivalent;
+  }
+  // Comparisons
+  friend constexpr bool operator==(
+      partial_ordering v, compare_internal::OnlyLiteralZero<>) noexcept {
+    return v.is_ordered() && v.value_ == 0;
+  }
+  friend constexpr bool operator!=(
+      partial_ordering v, compare_internal::OnlyLiteralZero<>) noexcept {
+    return !v.is_ordered() || v.value_ != 0;
+  }
+  friend constexpr bool operator<(
+      partial_ordering v, compare_internal::OnlyLiteralZero<>) noexcept {
+    return v.is_ordered() && v.value_ < 0;
+  }
+  friend constexpr bool operator<=(
+      partial_ordering v, compare_internal::OnlyLiteralZero<>) noexcept {
+    return v.is_ordered() && v.value_ <= 0;
+  }
+  friend constexpr bool operator>(
+      partial_ordering v, compare_internal::OnlyLiteralZero<>) noexcept {
+    return v.is_ordered() && v.value_ > 0;
+  }
+  friend constexpr bool operator>=(
+      partial_ordering v, compare_internal::OnlyLiteralZero<>) noexcept {
+    return v.is_ordered() && v.value_ >= 0;
+  }
+  friend constexpr bool operator==(compare_internal::OnlyLiteralZero<>,
+                                   partial_ordering v) noexcept {
+    return v.is_ordered() && 0 == v.value_;
+  }
+  friend constexpr bool operator!=(compare_internal::OnlyLiteralZero<>,
+                                   partial_ordering v) noexcept {
+    return !v.is_ordered() || 0 != v.value_;
+  }
+  friend constexpr bool operator<(compare_internal::OnlyLiteralZero<>,
+                                  partial_ordering v) noexcept {
+    return v.is_ordered() && 0 < v.value_;
+  }
+  friend constexpr bool operator<=(compare_internal::OnlyLiteralZero<>,
+                                   partial_ordering v) noexcept {
+    return v.is_ordered() && 0 <= v.value_;
+  }
+  friend constexpr bool operator>(compare_internal::OnlyLiteralZero<>,
+                                  partial_ordering v) noexcept {
+    return v.is_ordered() && 0 > v.value_;
+  }
+  friend constexpr bool operator>=(compare_internal::OnlyLiteralZero<>,
+                                   partial_ordering v) noexcept {
+    return v.is_ordered() && 0 >= v.value_;
+  }
+
+ private:
+  compare_internal::value_type value_;
+};
+
+class weak_ordering
+    : public compare_internal::weak_ordering_base<weak_ordering> {
+  explicit constexpr weak_ordering(compare_internal::eq v) noexcept
+      : value_(static_cast<compare_internal::value_type>(v)) {}
+  explicit constexpr weak_ordering(compare_internal::ord v) noexcept
+      : value_(static_cast<compare_internal::value_type>(v)) {}
+  friend struct compare_internal::weak_ordering_base<weak_ordering>;
+
+ public:
+  // Conversions
+  constexpr operator weak_equality() const noexcept {  // NOLINT
+    return value_ == 0 ? weak_equality::equivalent
+                       : weak_equality::nonequivalent;
+  }
+  constexpr operator partial_ordering() const noexcept {  // NOLINT
+    return value_ == 0 ? partial_ordering::equivalent
+                       : (value_ < 0 ? partial_ordering::less
+                                     : partial_ordering::greater);
+  }
+  // Comparisons
+  friend constexpr bool operator==(
+      weak_ordering v, compare_internal::OnlyLiteralZero<>) noexcept {
+    return v.value_ == 0;
+  }
+  friend constexpr bool operator!=(
+      weak_ordering v, compare_internal::OnlyLiteralZero<>) noexcept {
+    return v.value_ != 0;
+  }
+  friend constexpr bool operator<(
+      weak_ordering v, compare_internal::OnlyLiteralZero<>) noexcept {
+    return v.value_ < 0;
+  }
+  friend constexpr bool operator<=(
+      weak_ordering v, compare_internal::OnlyLiteralZero<>) noexcept {
+    return v.value_ <= 0;
+  }
+  friend constexpr bool operator>(
+      weak_ordering v, compare_internal::OnlyLiteralZero<>) noexcept {
+    return v.value_ > 0;
+  }
+  friend constexpr bool operator>=(
+      weak_ordering v, compare_internal::OnlyLiteralZero<>) noexcept {
+    return v.value_ >= 0;
+  }
+  friend constexpr bool operator==(compare_internal::OnlyLiteralZero<>,
+                                   weak_ordering v) noexcept {
+    return 0 == v.value_;
+  }
+  friend constexpr bool operator!=(compare_internal::OnlyLiteralZero<>,
+                                   weak_ordering v) noexcept {
+    return 0 != v.value_;
+  }
+  friend constexpr bool operator<(compare_internal::OnlyLiteralZero<>,
+                                  weak_ordering v) noexcept {
+    return 0 < v.value_;
+  }
+  friend constexpr bool operator<=(compare_internal::OnlyLiteralZero<>,
+                                   weak_ordering v) noexcept {
+    return 0 <= v.value_;
+  }
+  friend constexpr bool operator>(compare_internal::OnlyLiteralZero<>,
+                                  weak_ordering v) noexcept {
+    return 0 > v.value_;
+  }
+  friend constexpr bool operator>=(compare_internal::OnlyLiteralZero<>,
+                                   weak_ordering v) noexcept {
+    return 0 >= v.value_;
+  }
+
+ private:
+  compare_internal::value_type value_;
+};
+
+class strong_ordering
+    : public compare_internal::strong_ordering_base<strong_ordering> {
+  explicit constexpr strong_ordering(compare_internal::eq v) noexcept
+      : value_(static_cast<compare_internal::value_type>(v)) {}
+  explicit constexpr strong_ordering(compare_internal::ord v) noexcept
+      : value_(static_cast<compare_internal::value_type>(v)) {}
+  friend struct compare_internal::strong_ordering_base<strong_ordering>;
+
+ public:
+  // Conversions
+  constexpr operator weak_equality() const noexcept {  // NOLINT
+    return value_ == 0 ? weak_equality::equivalent
+                       : weak_equality::nonequivalent;
+  }
+  constexpr operator strong_equality() const noexcept {  // NOLINT
+    return value_ == 0 ? strong_equality::equal : strong_equality::nonequal;
+  }
+  constexpr operator partial_ordering() const noexcept {  // NOLINT
+    return value_ == 0 ? partial_ordering::equivalent
+                       : (value_ < 0 ? partial_ordering::less
+                                     : partial_ordering::greater);
+  }
+  constexpr operator weak_ordering() const noexcept {  // NOLINT
+    return value_ == 0
+               ? weak_ordering::equivalent
+               : (value_ < 0 ? weak_ordering::less : weak_ordering::greater);
+  }
+  // Comparisons
+  friend constexpr bool operator==(
+      strong_ordering v, compare_internal::OnlyLiteralZero<>) noexcept {
+    return v.value_ == 0;
+  }
+  friend constexpr bool operator!=(
+      strong_ordering v, compare_internal::OnlyLiteralZero<>) noexcept {
+    return v.value_ != 0;
+  }
+  friend constexpr bool operator<(
+      strong_ordering v, compare_internal::OnlyLiteralZero<>) noexcept {
+    return v.value_ < 0;
+  }
+  friend constexpr bool operator<=(
+      strong_ordering v, compare_internal::OnlyLiteralZero<>) noexcept {
+    return v.value_ <= 0;
+  }
+  friend constexpr bool operator>(
+      strong_ordering v, compare_internal::OnlyLiteralZero<>) noexcept {
+    return v.value_ > 0;
+  }
+  friend constexpr bool operator>=(
+      strong_ordering v, compare_internal::OnlyLiteralZero<>) noexcept {
+    return v.value_ >= 0;
+  }
+  friend constexpr bool operator==(compare_internal::OnlyLiteralZero<>,
+                                   strong_ordering v) noexcept {
+    return 0 == v.value_;
+  }
+  friend constexpr bool operator!=(compare_internal::OnlyLiteralZero<>,
+                                   strong_ordering v) noexcept {
+    return 0 != v.value_;
+  }
+  friend constexpr bool operator<(compare_internal::OnlyLiteralZero<>,
+                                  strong_ordering v) noexcept {
+    return 0 < v.value_;
+  }
+  friend constexpr bool operator<=(compare_internal::OnlyLiteralZero<>,
+                                   strong_ordering v) noexcept {
+    return 0 <= v.value_;
+  }
+  friend constexpr bool operator>(compare_internal::OnlyLiteralZero<>,
+                                  strong_ordering v) noexcept {
+    return 0 > v.value_;
+  }
+  friend constexpr bool operator>=(compare_internal::OnlyLiteralZero<>,
+                                   strong_ordering v) noexcept {
+    return 0 >= v.value_;
+  }
+
+ private:
+  compare_internal::value_type value_;
+};
+
+namespace compare_internal {
+// We also provide these comparator adapter functions for internal absl use.
+
+// Helper functions to do a boolean comparison of two keys given a boolean
+// or three-way comparator.
+constexpr bool compare_result_as_less_than(const bool r) { return r; }
+constexpr bool compare_result_as_less_than(const int r) { return r < 0; }
+constexpr bool compare_result_as_less_than(const absl::weak_ordering r) {
+  return r < 0;
+}
+
+template <typename Compare, typename K, typename LK>
+constexpr bool do_less_than_comparison(const Compare &compare, const K &x,
+                                       const LK &y) {
+  return compare_result_as_less_than(compare(x, y));
+}
+
+// Helper functions to do a three-way comparison of two keys given a boolean or
+// three-way comparator.
+// SFINAE prevents implicit conversions to int (such as from bool).
+template <typename Int,
+          absl::enable_if_t<std::is_same<int, Int>::value, int> = 0>
+constexpr absl::weak_ordering compare_result_as_ordering(const Int c) {
+  return c < 0 ? absl::weak_ordering::less
+               : c == 0 ? absl::weak_ordering::equivalent
+                        : absl::weak_ordering::greater;
+}
+constexpr absl::weak_ordering compare_result_as_ordering(
+    const absl::weak_ordering c) {
+  return c;
+}
+
+template <
+    typename Compare, typename K, typename LK,
+    absl::enable_if_t<!std::is_same<bool, absl::result_of_t<Compare(
+                                              const K &, const LK &)>>::value,
+                      int> = 0>
+constexpr absl::weak_ordering do_three_way_comparison(const Compare &compare,
+                                                      const K &x, const LK &y) {
+  return compare_result_as_ordering(compare(x, y));
+}
+template <
+    typename Compare, typename K, typename LK,
+    absl::enable_if_t<std::is_same<bool, absl::result_of_t<Compare(
+                                             const K &, const LK &)>>::value,
+                      int> = 0>
+constexpr absl::weak_ordering do_three_way_comparison(const Compare &compare,
+                                                      const K &x, const LK &y) {
+  return compare(x, y) ? absl::weak_ordering::less
+                       : compare(y, x) ? absl::weak_ordering::greater
+                                       : absl::weak_ordering::equivalent;
+}
+
+}  // namespace compare_internal
+}  // namespace absl
+
+#endif  // ABSL_TYPES_COMPARE_H_
diff --git a/absl/types/compare_test.cc b/absl/types/compare_test.cc
new file mode 100644
index 0000000000..dd0388c154
--- /dev/null
+++ b/absl/types/compare_test.cc
@@ -0,0 +1,315 @@
+// Copyright 2018 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "absl/types/compare.h"
+
+#include "gtest/gtest.h"
+#include "absl/base/casts.h"
+
+namespace absl {
+namespace {
+
+// This is necessary to avoid a bunch of lint warnings suggesting that we use
+// EXPECT_EQ/etc., which doesn't work in this case because they convert the `0`
+// to an int, which can't be converted to the unspecified zero type.
+bool Identity(bool b) { return b; }
+
+TEST(Compare, WeakEquality) {
+  EXPECT_TRUE(Identity(weak_equality::equivalent == 0));
+  EXPECT_TRUE(Identity(0 == weak_equality::equivalent));
+  EXPECT_TRUE(Identity(weak_equality::nonequivalent != 0));
+  EXPECT_TRUE(Identity(0 != weak_equality::nonequivalent));
+}
+
+TEST(Compare, StrongEquality) {
+  EXPECT_TRUE(Identity(strong_equality::equal == 0));
+  EXPECT_TRUE(Identity(0 == strong_equality::equal));
+  EXPECT_TRUE(Identity(strong_equality::nonequal != 0));
+  EXPECT_TRUE(Identity(0 != strong_equality::nonequal));
+  EXPECT_TRUE(Identity(strong_equality::equivalent == 0));
+  EXPECT_TRUE(Identity(0 == strong_equality::equivalent));
+  EXPECT_TRUE(Identity(strong_equality::nonequivalent != 0));
+  EXPECT_TRUE(Identity(0 != strong_equality::nonequivalent));
+}
+
+TEST(Compare, PartialOrdering) {
+  EXPECT_TRUE(Identity(partial_ordering::less < 0));
+  EXPECT_TRUE(Identity(0 > partial_ordering::less));
+  EXPECT_TRUE(Identity(partial_ordering::less <= 0));
+  EXPECT_TRUE(Identity(0 >= partial_ordering::less));
+  EXPECT_TRUE(Identity(partial_ordering::equivalent == 0));
+  EXPECT_TRUE(Identity(0 == partial_ordering::equivalent));
+  EXPECT_TRUE(Identity(partial_ordering::greater > 0));
+  EXPECT_TRUE(Identity(0 < partial_ordering::greater));
+  EXPECT_TRUE(Identity(partial_ordering::greater >= 0));
+  EXPECT_TRUE(Identity(0 <= partial_ordering::greater));
+  EXPECT_TRUE(Identity(partial_ordering::unordered != 0));
+  EXPECT_TRUE(Identity(0 != partial_ordering::unordered));
+  EXPECT_FALSE(Identity(partial_ordering::unordered < 0));
+  EXPECT_FALSE(Identity(0 < partial_ordering::unordered));
+  EXPECT_FALSE(Identity(partial_ordering::unordered <= 0));
+  EXPECT_FALSE(Identity(0 <= partial_ordering::unordered));
+  EXPECT_FALSE(Identity(partial_ordering::unordered > 0));
+  EXPECT_FALSE(Identity(0 > partial_ordering::unordered));
+  EXPECT_FALSE(Identity(partial_ordering::unordered >= 0));
+  EXPECT_FALSE(Identity(0 >= partial_ordering::unordered));
+}
+
+TEST(Compare, WeakOrdering) {
+  EXPECT_TRUE(Identity(weak_ordering::less < 0));
+  EXPECT_TRUE(Identity(0 > weak_ordering::less));
+  EXPECT_TRUE(Identity(weak_ordering::less <= 0));
+  EXPECT_TRUE(Identity(0 >= weak_ordering::less));
+  EXPECT_TRUE(Identity(weak_ordering::equivalent == 0));
+  EXPECT_TRUE(Identity(0 == weak_ordering::equivalent));
+  EXPECT_TRUE(Identity(weak_ordering::greater > 0));
+  EXPECT_TRUE(Identity(0 < weak_ordering::greater));
+  EXPECT_TRUE(Identity(weak_ordering::greater >= 0));
+  EXPECT_TRUE(Identity(0 <= weak_ordering::greater));
+}
+
+TEST(Compare, StrongOrdering) {
+  EXPECT_TRUE(Identity(strong_ordering::less < 0));
+  EXPECT_TRUE(Identity(0 > strong_ordering::less));
+  EXPECT_TRUE(Identity(strong_ordering::less <= 0));
+  EXPECT_TRUE(Identity(0 >= strong_ordering::less));
+  EXPECT_TRUE(Identity(strong_ordering::equal == 0));
+  EXPECT_TRUE(Identity(0 == strong_ordering::equal));
+  EXPECT_TRUE(Identity(strong_ordering::equivalent == 0));
+  EXPECT_TRUE(Identity(0 == strong_ordering::equivalent));
+  EXPECT_TRUE(Identity(strong_ordering::greater > 0));
+  EXPECT_TRUE(Identity(0 < strong_ordering::greater));
+  EXPECT_TRUE(Identity(strong_ordering::greater >= 0));
+  EXPECT_TRUE(Identity(0 <= strong_ordering::greater));
+}
+
+TEST(Compare, Conversions) {
+  EXPECT_TRUE(
+      Identity(implicit_cast<weak_equality>(strong_equality::equal) == 0));
+  EXPECT_TRUE(
+      Identity(implicit_cast<weak_equality>(strong_equality::nonequal) != 0));
+  EXPECT_TRUE(
+      Identity(implicit_cast<weak_equality>(strong_equality::equivalent) == 0));
+  EXPECT_TRUE(Identity(
+      implicit_cast<weak_equality>(strong_equality::nonequivalent) != 0));
+
+  EXPECT_TRUE(
+      Identity(implicit_cast<weak_equality>(partial_ordering::less) != 0));
+  EXPECT_TRUE(Identity(
+      implicit_cast<weak_equality>(partial_ordering::equivalent) == 0));
+  EXPECT_TRUE(
+      Identity(implicit_cast<weak_equality>(partial_ordering::greater) != 0));
+  EXPECT_TRUE(
+      Identity(implicit_cast<weak_equality>(partial_ordering::unordered) != 0));
+
+  EXPECT_TRUE(implicit_cast<weak_equality>(weak_ordering::less) != 0);
+  EXPECT_TRUE(
+      Identity(implicit_cast<weak_equality>(weak_ordering::equivalent) == 0));
+  EXPECT_TRUE(
+      Identity(implicit_cast<weak_equality>(weak_ordering::greater) != 0));
+
+  EXPECT_TRUE(
+      Identity(implicit_cast<partial_ordering>(weak_ordering::less) != 0));
+  EXPECT_TRUE(
+      Identity(implicit_cast<partial_ordering>(weak_ordering::less) < 0));
+  EXPECT_TRUE(
+      Identity(implicit_cast<partial_ordering>(weak_ordering::less) <= 0));
+  EXPECT_TRUE(Identity(
+      implicit_cast<partial_ordering>(weak_ordering::equivalent) == 0));
+  EXPECT_TRUE(
+      Identity(implicit_cast<partial_ordering>(weak_ordering::greater) != 0));
+  EXPECT_TRUE(
+      Identity(implicit_cast<partial_ordering>(weak_ordering::greater) > 0));
+  EXPECT_TRUE(
+      Identity(implicit_cast<partial_ordering>(weak_ordering::greater) >= 0));
+
+  EXPECT_TRUE(
+      Identity(implicit_cast<weak_equality>(strong_ordering::less) != 0));
+  EXPECT_TRUE(
+      Identity(implicit_cast<weak_equality>(strong_ordering::equal) == 0));
+  EXPECT_TRUE(
+      Identity(implicit_cast<weak_equality>(strong_ordering::equivalent) == 0));
+  EXPECT_TRUE(
+      Identity(implicit_cast<weak_equality>(strong_ordering::greater) != 0));
+
+  EXPECT_TRUE(
+      Identity(implicit_cast<strong_equality>(strong_ordering::less) != 0));
+  EXPECT_TRUE(
+      Identity(implicit_cast<strong_equality>(strong_ordering::equal) == 0));
+  EXPECT_TRUE(Identity(
+      implicit_cast<strong_equality>(strong_ordering::equivalent) == 0));
+  EXPECT_TRUE(
+      Identity(implicit_cast<strong_equality>(strong_ordering::greater) != 0));
+
+  EXPECT_TRUE(
+      Identity(implicit_cast<partial_ordering>(strong_ordering::less) != 0));
+  EXPECT_TRUE(
+      Identity(implicit_cast<partial_ordering>(strong_ordering::less) < 0));
+  EXPECT_TRUE(
+      Identity(implicit_cast<partial_ordering>(strong_ordering::less) <= 0));
+  EXPECT_TRUE(
+      Identity(implicit_cast<partial_ordering>(strong_ordering::equal) == 0));
+  EXPECT_TRUE(Identity(
+      implicit_cast<partial_ordering>(strong_ordering::equivalent) == 0));
+  EXPECT_TRUE(
+      Identity(implicit_cast<partial_ordering>(strong_ordering::greater) != 0));
+  EXPECT_TRUE(
+      Identity(implicit_cast<partial_ordering>(strong_ordering::greater) > 0));
+  EXPECT_TRUE(
+      Identity(implicit_cast<partial_ordering>(strong_ordering::greater) >= 0));
+
+  EXPECT_TRUE(
+      Identity(implicit_cast<weak_ordering>(strong_ordering::less) != 0));
+  EXPECT_TRUE(
+      Identity(implicit_cast<weak_ordering>(strong_ordering::less) < 0));
+  EXPECT_TRUE(
+      Identity(implicit_cast<weak_ordering>(strong_ordering::less) <= 0));
+  EXPECT_TRUE(
+      Identity(implicit_cast<weak_ordering>(strong_ordering::equal) == 0));
+  EXPECT_TRUE(
+      Identity(implicit_cast<weak_ordering>(strong_ordering::equivalent) == 0));
+  EXPECT_TRUE(
+      Identity(implicit_cast<weak_ordering>(strong_ordering::greater) != 0));
+  EXPECT_TRUE(
+      Identity(implicit_cast<weak_ordering>(strong_ordering::greater) > 0));
+  EXPECT_TRUE(
+      Identity(implicit_cast<weak_ordering>(strong_ordering::greater) >= 0));
+}
+
+struct WeakOrderingLess {
+  template <typename T>
+  absl::weak_ordering operator()(const T &a, const T &b) const {
+    return a < b ? absl::weak_ordering::less
+                 : a == b ? absl::weak_ordering::equivalent
+                          : absl::weak_ordering::greater;
+  }
+};
+
+TEST(CompareResultAsLessThan, SanityTest) {
+  EXPECT_FALSE(absl::compare_internal::compare_result_as_less_than(false));
+  EXPECT_TRUE(absl::compare_internal::compare_result_as_less_than(true));
+
+  EXPECT_TRUE(absl::compare_internal::compare_result_as_less_than(-1));
+  EXPECT_FALSE(absl::compare_internal::compare_result_as_less_than(0));
+  EXPECT_FALSE(absl::compare_internal::compare_result_as_less_than(1));
+
+  EXPECT_TRUE(
+      absl::compare_internal::compare_result_as_less_than(weak_ordering::less));
+  EXPECT_FALSE(absl::compare_internal::compare_result_as_less_than(
+      weak_ordering::equivalent));
+  EXPECT_FALSE(absl::compare_internal::compare_result_as_less_than(
+      weak_ordering::greater));
+}
+
+TEST(DoLessThanComparison, SanityTest) {
+  std::less<int> less;
+  WeakOrderingLess weak;
+
+  EXPECT_TRUE(absl::compare_internal::do_less_than_comparison(less, -1, 0));
+  EXPECT_TRUE(absl::compare_internal::do_less_than_comparison(weak, -1, 0));
+
+  EXPECT_FALSE(absl::compare_internal::do_less_than_comparison(less, 10, 10));
+  EXPECT_FALSE(absl::compare_internal::do_less_than_comparison(weak, 10, 10));
+
+  EXPECT_FALSE(absl::compare_internal::do_less_than_comparison(less, 10, 5));
+  EXPECT_FALSE(absl::compare_internal::do_less_than_comparison(weak, 10, 5));
+}
+
+TEST(CompareResultAsOrdering, SanityTest) {
+  EXPECT_TRUE(Identity(
+      absl::compare_internal::compare_result_as_ordering(-1) < 0));
+  EXPECT_FALSE(Identity(
+      absl::compare_internal::compare_result_as_ordering(-1) == 0));
+  EXPECT_FALSE(
+      Identity(absl::compare_internal::compare_result_as_ordering(-1) > 0));
+  EXPECT_TRUE(Identity(absl::compare_internal::compare_result_as_ordering(
+                           weak_ordering::less) < 0));
+  EXPECT_FALSE(Identity(absl::compare_internal::compare_result_as_ordering(
+                            weak_ordering::less) == 0));
+  EXPECT_FALSE(Identity(absl::compare_internal::compare_result_as_ordering(
+                            weak_ordering::less) > 0));
+
+  EXPECT_FALSE(Identity(
+      absl::compare_internal::compare_result_as_ordering(0) < 0));
+  EXPECT_TRUE(Identity(
+      absl::compare_internal::compare_result_as_ordering(0) == 0));
+  EXPECT_FALSE(Identity(
+      absl::compare_internal::compare_result_as_ordering(0) > 0));
+  EXPECT_FALSE(Identity(absl::compare_internal::compare_result_as_ordering(
+                           weak_ordering::equivalent) < 0));
+  EXPECT_TRUE(Identity(absl::compare_internal::compare_result_as_ordering(
+                            weak_ordering::equivalent) == 0));
+  EXPECT_FALSE(Identity(absl::compare_internal::compare_result_as_ordering(
+                            weak_ordering::equivalent) > 0));
+
+  EXPECT_FALSE(Identity(
+      absl::compare_internal::compare_result_as_ordering(1) < 0));
+  EXPECT_FALSE(Identity(
+      absl::compare_internal::compare_result_as_ordering(1) == 0));
+  EXPECT_TRUE(Identity(
+      absl::compare_internal::compare_result_as_ordering(1) > 0));
+  EXPECT_FALSE(Identity(absl::compare_internal::compare_result_as_ordering(
+                           weak_ordering::greater) < 0));
+  EXPECT_FALSE(Identity(absl::compare_internal::compare_result_as_ordering(
+                            weak_ordering::greater) == 0));
+  EXPECT_TRUE(Identity(absl::compare_internal::compare_result_as_ordering(
+                            weak_ordering::greater) > 0));
+}
+
+TEST(DoThreeWayComparison, SanityTest) {
+  std::less<int> less;
+  WeakOrderingLess weak;
+
+  EXPECT_TRUE(Identity(
+      absl::compare_internal::do_three_way_comparison(less, -1, 0) < 0));
+  EXPECT_FALSE(Identity(
+      absl::compare_internal::do_three_way_comparison(less, -1, 0) == 0));
+  EXPECT_FALSE(Identity(
+      absl::compare_internal::do_three_way_comparison(less, -1, 0) > 0));
+  EXPECT_TRUE(Identity(
+      absl::compare_internal::do_three_way_comparison(weak, -1, 0) < 0));
+  EXPECT_FALSE(Identity(
+      absl::compare_internal::do_three_way_comparison(weak, -1, 0) == 0));
+  EXPECT_FALSE(Identity(
+      absl::compare_internal::do_three_way_comparison(weak, -1, 0) > 0));
+
+  EXPECT_FALSE(Identity(
+      absl::compare_internal::do_three_way_comparison(less, 10, 10) < 0));
+  EXPECT_TRUE(Identity(
+      absl::compare_internal::do_three_way_comparison(less, 10, 10) == 0));
+  EXPECT_FALSE(Identity(
+      absl::compare_internal::do_three_way_comparison(less, 10, 10) > 0));
+  EXPECT_FALSE(Identity(
+      absl::compare_internal::do_three_way_comparison(weak, 10, 10) < 0));
+  EXPECT_TRUE(Identity(
+      absl::compare_internal::do_three_way_comparison(weak, 10, 10) == 0));
+  EXPECT_FALSE(Identity(
+      absl::compare_internal::do_three_way_comparison(weak, 10, 10) > 0));
+
+  EXPECT_FALSE(Identity(
+      absl::compare_internal::do_three_way_comparison(less, 10, 5) < 0));
+  EXPECT_FALSE(Identity(
+      absl::compare_internal::do_three_way_comparison(less, 10, 5) == 0));
+  EXPECT_TRUE(Identity(
+      absl::compare_internal::do_three_way_comparison(less, 10, 5) > 0));
+  EXPECT_FALSE(Identity(
+      absl::compare_internal::do_three_way_comparison(weak, 10, 5) < 0));
+  EXPECT_FALSE(Identity(
+      absl::compare_internal::do_three_way_comparison(weak, 10, 5) == 0));
+  EXPECT_TRUE(Identity(
+      absl::compare_internal::do_three_way_comparison(weak, 10, 5) > 0));
+}
+
+}  // namespace
+}  // namespace absl
diff --git a/ci/linux_clang-latest_libcxx_bazel.sh b/ci/linux_clang-latest_libcxx_bazel.sh
index fe9d6aaffb..f32cbef7bf 100755
--- a/ci/linux_clang-latest_libcxx_bazel.sh
+++ b/ci/linux_clang-latest_libcxx_bazel.sh
@@ -32,7 +32,7 @@ if [ -z ${COMPILATION_MODE:-} ]; then
   COMPILATION_MODE="fastbuild opt"
 fi
 
-readonly DOCKER_CONTAINER="gcr.io/google.com/absl-177019/linux_clang-latest:20190329"
+readonly DOCKER_CONTAINER="gcr.io/google.com/absl-177019/linux_clang-latest:20190508"
 
 # USE_BAZEL_CACHE=1 only works on Kokoro.
 # Without access to the credentials this won't work.
diff --git a/ci/linux_clang-latest_libstdcxx_bazel.sh b/ci/linux_clang-latest_libstdcxx_bazel.sh
index 611b7f246d..7bbecd6f58 100755
--- a/ci/linux_clang-latest_libstdcxx_bazel.sh
+++ b/ci/linux_clang-latest_libstdcxx_bazel.sh
@@ -32,7 +32,7 @@ if [ -z ${COMPILATION_MODE:-} ]; then
   COMPILATION_MODE="fastbuild opt"
 fi
 
-readonly DOCKER_CONTAINER="gcr.io/google.com/absl-177019/linux_clang-latest:20190329"
+readonly DOCKER_CONTAINER="gcr.io/google.com/absl-177019/linux_clang-latest:20190508"
 
 # USE_BAZEL_CACHE=1 only works on Kokoro.
 # Without access to the credentials this won't work.