about summary refs log tree commit diff
diff options
context:
space:
mode:
authorKane York <kanepyork@gmail.com>2020-07-18T23·23-0700
committerKane York <rikingcoding@gmail.com>2020-07-19T00·51+0000
commit9f5f71616affc7c3c588bfb97d578bfc61839b37 (patch)
tree36dcd97947d8669e65a64202c9bd96fc439ab2f5
parentbd02cae03244cbeff1e90278ee0e8c4e2f5f4d0d (diff)
feat(3p/absl): import StatusOr from tensorflow r/1391
Additionally, add tests for the macros. A future CL will enable the tests in CI.

Change-Id: Id4445a1aa65bf6751b87606f37654f3fc6d20efc
Tested-By: kanepyork <rikingcoding@gmail.com>
Reviewed-on: https://cl.tvl.fyi/c/depot/+/1274
Reviewed-by: tazjin <mail@tazj.in>
Reviewed-by: glittershark <grfn@gws.fyi>
Tested-by: BuildkiteCI
-rw-r--r--third_party/abseil_cpp/absl/status/CMakeLists.txt32
-rw-r--r--third_party/abseil_cpp/absl/status/statusor.cc48
-rw-r--r--third_party/abseil_cpp/absl/status/statusor.h394
-rw-r--r--third_party/abseil_cpp/absl/status/statusor_internals.h250
-rw-r--r--third_party/abseil_cpp/absl/status/statusor_test.cc753
-rw-r--r--third_party/abseil_cpp/default.nix5
6 files changed, 1481 insertions, 1 deletions
diff --git a/third_party/abseil_cpp/absl/status/CMakeLists.txt b/third_party/abseil_cpp/absl/status/CMakeLists.txt
index c041d69517..3b8917e030 100644
--- a/third_party/abseil_cpp/absl/status/CMakeLists.txt
+++ b/third_party/abseil_cpp/absl/status/CMakeLists.txt
@@ -39,6 +39,24 @@ absl_cc_library(
   PUBLIC
 )
 
+absl_cc_library(
+  NAME
+    statusor
+  HDRS
+    "statusor.h"
+  SRCS
+    "statusor.cc"
+    "statusor_internals.h"
+  COPTS
+    ${ABSL_DEFAULT_COPTS}
+  DEPS
+    absl::status
+    absl::atomic_hook
+    absl::raw_logging_internal
+    absl::strings
+  PUBLIC
+)
+
 absl_cc_test(
   NAME
     status_test
@@ -51,3 +69,17 @@ absl_cc_test(
     absl::strings
     gmock_main
 )
+
+absl_cc_test(
+  NAME
+    statusor_test
+  SRCS
+   "statusor_test.cc"
+  COPTS
+    ${ABSL_TEST_COPTS}
+  DEPS
+    absl::status
+    absl::statusor
+    absl::strings
+    gmock_main
+)
diff --git a/third_party/abseil_cpp/absl/status/statusor.cc b/third_party/abseil_cpp/absl/status/statusor.cc
new file mode 100644
index 0000000000..2d22adb276
--- /dev/null
+++ b/third_party/abseil_cpp/absl/status/statusor.cc
@@ -0,0 +1,48 @@
+/* Copyright 2017 The TensorFlow Authors. All Rights Reserved.
+
+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
+
+    http://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/status/statusor.h"
+
+#include "absl/base/internal/raw_logging.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+
+namespace internal_statusor {
+
+#define ABSL_STATUSOR_INTERNAL_BAD_OK_MSG "An OK status is not a valid " \
+  "constructor argument to StatusOr<T>"
+
+void Helper::HandleInvalidStatusCtorArg(Status* status) {
+  ABSL_RAW_LOG(ERROR, ABSL_STATUSOR_INTERNAL_BAD_OK_MSG);
+  // Fall back to kInternal.
+  *status = InternalError(ABSL_STATUSOR_INTERNAL_BAD_OK_MSG);
+}
+
+#undef ABSL_STATUSOR_INTERNAL_BAD_OK_MSG
+
+void Helper::Crash(const Status& status) {
+#ifdef ABSL_HAVE_EXCEPTIONS
+  throw status;
+#else
+  std::string status_debug = status.ToString();
+  ABSL_RAW_LOG(FATAL, "Attempting to fetch value instead of handling error: %s", status_debug.c_str());
+  abort();   // TODO(calabrese) Remove once RAW_LOG FATAL is noreturn.
+#endif
+}
+}  // namespace internal_statusor
+
+ABSL_NAMESPACE_END
+}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/status/statusor.h b/third_party/abseil_cpp/absl/status/statusor.h
new file mode 100644
index 0000000000..59a52cb782
--- /dev/null
+++ b/third_party/abseil_cpp/absl/status/statusor.h
@@ -0,0 +1,394 @@
+/* Copyright 2017 The TensorFlow Authors. All Rights Reserved.
+
+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
+
+    http://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.
+==============================================================================*/
+
+// StatusOr<T> is the union of a Status object and a T object. StatusOr models
+// the concept of an object that is either a value, or an error Status
+// explaining why such a value is not present. To this end, StatusOr<T> does not
+// allow its Status value to be StatusCode::kOk.
+//
+// The primary use-case for StatusOr<T> is as the return value of a
+// function which may fail.
+//
+// Example client usage for a StatusOr<T>, where T is not a pointer:
+//
+//  StatusOr<float> result = DoBigCalculationThatCouldFail();
+//  if (result.ok()) {
+//    float answer = result.ValueOrDie();
+//    printf("Big calculation yielded: %f", answer);
+//  } else {
+//    LOG(ERROR) << result.status();
+//  }
+//
+// Example client usage for a StatusOr<T*>:
+//
+//  StatusOr<Foo*> result = FooFactory::MakeNewFoo(arg);
+//  if (result.ok()) {
+//    std::unique_ptr<Foo> foo(result.ValueOrDie());
+//    foo->DoSomethingCool();
+//  } else {
+//    LOG(ERROR) << result.status();
+//  }
+//
+// Example client usage for a StatusOr<std::unique_ptr<T>>:
+//
+//  StatusOr<std::unique_ptr<Foo>> result = FooFactory::MakeNewFoo(arg);
+//  if (result.ok()) {
+//    std::unique_ptr<Foo> foo = std::move(result.ValueOrDie());
+//    foo->DoSomethingCool();
+//  } else {
+//    LOG(ERROR) << result.status();
+//  }
+//
+// Example factory implementation returning StatusOr<T*>:
+//
+//  StatusOr<Foo*> FooFactory::MakeNewFoo(int arg) {
+//    if (arg <= 0) {
+//      return absl::InvalidArgumentError("Arg must be positive");
+//    } else {
+//      return new Foo(arg);
+//    }
+//  }
+//
+// Note that the assignment operators require that destroying the currently
+// stored value cannot invalidate the argument; in other words, the argument
+// cannot be an alias for the current value, or anything owned by the current
+// value.
+#ifndef ABSL_STATUS_STATUSOR_H_
+#define ABSL_STATUS_STATUSOR_H_
+
+#include "absl/status/status.h"
+#include "absl/status/statusor_internals.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+
+template <typename T>
+class StatusOr : private internal_statusor::StatusOrData<T>,
+                 private internal_statusor::TraitsBase<
+                     std::is_copy_constructible<T>::value,
+                     std::is_move_constructible<T>::value> {
+  template <typename U>
+  friend class StatusOr;
+
+  typedef internal_statusor::StatusOrData<T> Base;
+
+ public:
+  typedef T element_type;  // DEPRECATED: use `value_type`.
+  typedef T value_type;
+
+  // Constructs a new StatusOr with Status::UNKNOWN status.  This is marked
+  // 'explicit' to try to catch cases like 'return {};', where people think
+  // StatusOr<std::vector<int>> will be initialized with an empty vector,
+  // instead of a Status::UNKNOWN status.
+  explicit StatusOr();
+
+  // StatusOr<T> will be copy constructible/assignable if T is copy
+  // constructible.
+  StatusOr(const StatusOr&) = default;
+  StatusOr& operator=(const StatusOr&) = default;
+
+  // StatusOr<T> will be move constructible/assignable if T is move
+  // constructible.
+  StatusOr(StatusOr&&) = default;
+  StatusOr& operator=(StatusOr&&) = default;
+
+  // Conversion copy/move constructor, T must be convertible from U.
+  template <typename U, typename std::enable_if<
+                            std::is_convertible<U, T>::value>::type* = nullptr>
+  StatusOr(const StatusOr<U>& other);
+  template <typename U, typename std::enable_if<
+                            std::is_convertible<U, T>::value>::type* = nullptr>
+  StatusOr(StatusOr<U>&& other);
+
+  // Conversion copy/move assignment operator, T must be convertible from U.
+  template <typename U, typename std::enable_if<
+                            std::is_convertible<U, T>::value>::type* = nullptr>
+  StatusOr& operator=(const StatusOr<U>& other);
+  template <typename U, typename std::enable_if<
+                            std::is_convertible<U, T>::value>::type* = nullptr>
+  StatusOr& operator=(StatusOr<U>&& other);
+
+  // Constructs a new StatusOr with the given value. After calling this
+  // constructor, calls to ValueOrDie() will succeed, and calls to status() will
+  // return OK.
+  //
+  // NOTE: Not explicit - we want to use StatusOr<T> as a return type
+  // so it is convenient and sensible to be able to do 'return T()'
+  // when the return type is StatusOr<T>.
+  //
+  // REQUIRES: T is copy constructible.
+  StatusOr(const T& value);
+
+  // Constructs a new StatusOr with the given non-ok status. After calling
+  // this constructor, calls to ValueOrDie() will CHECK-fail.
+  //
+  // NOTE: Not explicit - we want to use StatusOr<T> as a return
+  // value, so it is convenient and sensible to be able to do 'return
+  // Status()' when the return type is StatusOr<T>.
+  //
+  // REQUIRES: !status.ok(). This requirement is enforced with either an
+  // exception (the passed absl::Status) or a FATAL log.
+  StatusOr(const Status& status);
+  StatusOr& operator=(const Status& status);
+
+  // TODO(b/62186997): Add operator=(T) overloads.
+
+  // Similar to the `const T&` overload.
+  //
+  // REQUIRES: T is move constructible.
+  StatusOr(T&& value);
+
+  // RValue versions of the operations declared above.
+  StatusOr(Status&& status);
+  StatusOr& operator=(Status&& status);
+
+  // Returns this->status().ok()
+  bool ok() const { return this->status_.ok(); }
+
+  // Returns a reference to our status. If this contains a T, then
+  // returns OkStatus().
+  const Status& status() const &;
+  Status status() &&;
+
+  // Returns a reference to our current value, or CHECK-fails if !this->ok().
+  //
+  // Note: for value types that are cheap to copy, prefer simple code:
+  //
+  //   T value = statusor.ValueOrDie();
+  //
+  // Otherwise, if the value type is expensive to copy, but can be left
+  // in the StatusOr, simply assign to a reference:
+  //
+  //   T& value = statusor.ValueOrDie();  // or `const T&`
+  //
+  // Otherwise, if the value type supports an efficient move, it can be
+  // used as follows:
+  //
+  //   T value = std::move(statusor).ValueOrDie();
+  //
+  // The std::move on statusor instead of on the whole expression enables
+  // warnings about possible uses of the statusor object after the move.
+  // C++ style guide waiver for ref-qualified overloads granted in cl/143176389
+  // See go/ref-qualifiers for more details on such overloads.
+  const T& ValueOrDie() const &;
+  T& ValueOrDie() &;
+  const T&& ValueOrDie() const &&;
+  T&& ValueOrDie() &&;
+
+  // Returns a reference to the current value.
+  //
+  // REQUIRES: this->ok() == true, otherwise the behavior is undefined.
+  //
+  // Use this->ok() or `operator bool()` to verify that there is a current
+  // value. Alternatively, see ValueOrDie() for a similar API that guarantees
+  // CHECK-failing if there is no current value.
+  const T& operator*() const&;
+  T& operator*() &;
+  const T&& operator*() const&&;
+  T&& operator*() &&;
+
+  // Returns a pointer to the current value.
+  //
+  // REQUIRES: this->ok() == true, otherwise the behavior is undefined.
+  //
+  // Use this->ok() or `operator bool()` to verify that there is a current
+  // value.
+  const T* operator->() const;
+  T* operator->();
+
+  T ConsumeValueOrDie() { return std::move(ValueOrDie()); }
+
+  // Ignores any errors. This method does nothing except potentially suppress
+  // complaints from any tools that are checking that errors are not dropped on
+  // the floor.
+  void IgnoreError() const;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// Implementation details for StatusOr<T>
+
+template <typename T>
+StatusOr<T>::StatusOr() : Base(Status(StatusCode::kUnknown, "")) {}
+
+template <typename T>
+StatusOr<T>::StatusOr(const T& value) : Base(value) {}
+
+template <typename T>
+StatusOr<T>::StatusOr(const Status& status) : Base(status) {}
+
+template <typename T>
+StatusOr<T>& StatusOr<T>::operator=(const Status& status) {
+  this->Assign(status);
+  return *this;
+}
+
+template <typename T>
+StatusOr<T>::StatusOr(T&& value) : Base(std::move(value)) {}
+
+template <typename T>
+StatusOr<T>::StatusOr(Status&& status) : Base(std::move(status)) {}
+
+template <typename T>
+StatusOr<T>& StatusOr<T>::operator=(Status&& status) {
+  this->Assign(std::move(status));
+  return *this;
+}
+
+template <typename T>
+template <typename U,
+          typename std::enable_if<std::is_convertible<U, T>::value>::type*>
+inline StatusOr<T>::StatusOr(const StatusOr<U>& other)
+    : Base(static_cast<const typename StatusOr<U>::Base&>(other)) {}
+
+template <typename T>
+template <typename U,
+          typename std::enable_if<std::is_convertible<U, T>::value>::type*>
+inline StatusOr<T>& StatusOr<T>::operator=(const StatusOr<U>& other) {
+  if (other.ok())
+    this->Assign(other.ValueOrDie());
+  else
+    this->Assign(other.status());
+  return *this;
+}
+
+template <typename T>
+template <typename U,
+          typename std::enable_if<std::is_convertible<U, T>::value>::type*>
+inline StatusOr<T>::StatusOr(StatusOr<U>&& other)
+    : Base(static_cast<typename StatusOr<U>::Base&&>(other)) {}
+
+template <typename T>
+template <typename U,
+          typename std::enable_if<std::is_convertible<U, T>::value>::type*>
+inline StatusOr<T>& StatusOr<T>::operator=(StatusOr<U>&& other) {
+  if (other.ok()) {
+    this->Assign(std::move(other).ValueOrDie());
+  } else {
+    this->Assign(std::move(other).status());
+  }
+  return *this;
+}
+
+template <typename T>
+const Status& StatusOr<T>::status() const & {
+  return this->status_;
+}
+template <typename T>
+Status StatusOr<T>::status() && {
+  // Note that we copy instead of moving the status here so that
+  // ~StatusOrData() can call ok() without invoking UB.
+  return ok() ? OkStatus() : this->status_;
+}
+
+template <typename T>
+const T& StatusOr<T>::ValueOrDie() const & {
+  this->EnsureOk();
+  return this->data_;
+}
+
+template <typename T>
+T& StatusOr<T>::ValueOrDie() & {
+  this->EnsureOk();
+  return this->data_;
+}
+
+template <typename T>
+const T&& StatusOr<T>::ValueOrDie() const && {
+  this->EnsureOk();
+  return std::move(this->data_);
+}
+
+template <typename T>
+T&& StatusOr<T>::ValueOrDie() && {
+  this->EnsureOk();
+  return std::move(this->data_);
+}
+
+template <typename T>
+const T* StatusOr<T>::operator->() const {
+  this->EnsureOk();
+  return &this->data_;
+}
+
+template <typename T>
+T* StatusOr<T>::operator->() {
+  this->EnsureOk();
+  return &this->data_;
+}
+
+template <typename T>
+const T& StatusOr<T>::operator*() const& {
+  this->EnsureOk();
+  return this->data_;
+}
+
+template <typename T>
+T& StatusOr<T>::operator*() & {
+  this->EnsureOk();
+  return this->data_;
+}
+
+template <typename T>
+const T&& StatusOr<T>::operator*() const&& {
+  this->EnsureOk();
+  return std::move(this->data_);
+}
+
+template <typename T>
+T&& StatusOr<T>::operator*() && {
+  this->EnsureOk();
+  return std::move(this->data_);
+}
+
+template <typename T>
+void StatusOr<T>::IgnoreError() const {
+  // no-op
+}
+
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#define ASSERT_OK_AND_ASSIGN(lhs, rexpr)                                  \
+  ABSL_ASSERT_OK_AND_ASSIGN_IMPL(                                         \
+      ABSL_STATUS_MACROS_CONCAT_NAME(_status_or_value, __COUNTER__), lhs, \
+      rexpr);
+
+#define ABSL_ASSERT_OK_AND_ASSIGN_IMPL(statusor, lhs, rexpr)  \
+  auto statusor = (rexpr);                                    \
+  ASSERT_TRUE(statusor.status().ok()) << statusor.status();   \
+  lhs = std::move(statusor.ValueOrDie())
+
+#define ABSL_STATUS_MACROS_CONCAT_NAME(x, y) ABSL_STATUS_MACROS_CONCAT_IMPL(x, y)
+#define ABSL_STATUS_MACROS_CONCAT_IMPL(x, y) x##y
+
+#define ASSIGN_OR_RETURN(lhs, rexpr) \
+  ABSL_ASSIGN_OR_RETURN_IMPL(        \
+      ABSL_STATUS_MACROS_CONCAT_NAME(_status_or_value, __COUNTER__), lhs, rexpr)
+
+#define ABSL_ASSIGN_OR_RETURN_IMPL(statusor, lhs, rexpr) \
+  auto statusor = (rexpr);                               \
+  if (ABSL_PREDICT_FALSE(!statusor.ok())) {              \
+    return statusor.status();                            \
+  }                                                      \
+  lhs = std::move(statusor.ValueOrDie())
+
+#define RETURN_IF_ERROR(status)             \
+  do {                                      \
+    if (ABSL_PREDICT_FALSE(!status.ok())) { \
+      return status;                        \
+    }                                       \
+  } while(0)
+
+#endif  // ABSL_STATUS_STATUSOR_H_
diff --git a/third_party/abseil_cpp/absl/status/statusor_internals.h b/third_party/abseil_cpp/absl/status/statusor_internals.h
new file mode 100644
index 0000000000..5366c2840c
--- /dev/null
+++ b/third_party/abseil_cpp/absl/status/statusor_internals.h
@@ -0,0 +1,250 @@
+/* Copyright 2017 The TensorFlow Authors. All Rights Reserved.
+
+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
+
+    http://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.
+==============================================================================*/
+
+#ifndef ABSL_STATUS_STATUSOR_INTERNALS_H_
+#define ABSL_STATUS_STATUSOR_INTERNALS_H_
+
+#include "absl/status/status.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+
+namespace internal_statusor {
+
+class Helper {
+ public:
+  // Move type-agnostic error handling to the .cc.
+  static void HandleInvalidStatusCtorArg(Status*);
+  ABSL_ATTRIBUTE_NORETURN static void Crash(const Status& status);
+};
+
+// Construct an instance of T in `p` through placement new, passing Args... to
+// the constructor.
+// This abstraction is here mostly for the gcc performance fix.
+template <typename T, typename... Args>
+void PlacementNew(void* p, Args&&... args) {
+#if defined(__GNUC__) && !defined(__clang__)
+  // Teach gcc that 'p' cannot be null, fixing code size issues.
+  if (p == nullptr) __builtin_unreachable();
+#endif
+  new (p) T(std::forward<Args>(args)...);
+}
+
+// Helper base class to hold the data and all operations.
+// We move all this to a base class to allow mixing with the appropriate
+// TraitsBase specialization.
+template <typename T>
+class StatusOrData {
+  template <typename U>
+  friend class StatusOrData;
+
+ public:
+  StatusOrData() = delete;
+
+  StatusOrData(const StatusOrData& other) {
+    if (other.ok()) {
+      MakeValue(other.data_);
+      MakeStatus();
+    } else {
+      MakeStatus(other.status_);
+    }
+  }
+
+  StatusOrData(StatusOrData&& other) noexcept {
+    if (other.ok()) {
+      MakeValue(std::move(other.data_));
+      MakeStatus();
+    } else {
+      MakeStatus(other.status_);
+    }
+  }
+
+  template <typename U>
+  StatusOrData(const StatusOrData<U>& other) {
+    if (other.ok()) {
+      MakeValue(other.data_);
+      MakeStatus();
+    } else {
+      MakeStatus(other.status_);
+    }
+  }
+
+  template <typename U>
+  StatusOrData(StatusOrData<U>&& other) {
+    if (other.ok()) {
+      MakeValue(std::move(other.data_));
+      MakeStatus();
+    } else {
+      MakeStatus(other.status_);
+    }
+  }
+
+  explicit StatusOrData(const T& value) : data_(value) { MakeStatus(); }
+  explicit StatusOrData(T&& value) : data_(std::move(value)) { MakeStatus(); }
+
+  explicit StatusOrData(const Status& status) : status_(status) {
+    EnsureNotOk();
+  }
+  explicit StatusOrData(Status&& status) : status_(std::move(status)) {
+    EnsureNotOk();
+  }
+
+  StatusOrData& operator=(const StatusOrData& other) {
+    if (this == &other) return *this;
+    if (other.ok())
+      Assign(other.data_);
+    else
+      Assign(other.status_);
+    return *this;
+  }
+
+  StatusOrData& operator=(StatusOrData&& other) {
+    if (this == &other) return *this;
+    if (other.ok())
+      Assign(std::move(other.data_));
+    else
+      Assign(std::move(other.status_));
+    return *this;
+  }
+
+  ~StatusOrData() {
+    if (ok()) {
+      status_.~Status();
+      data_.~T();
+    } else {
+      status_.~Status();
+    }
+  }
+
+  void Assign(const T& value) {
+    if (ok()) {
+      data_.~T();
+      MakeValue(value);
+    } else {
+      MakeValue(value);
+      status_ = OkStatus();
+    }
+  }
+
+  void Assign(T&& value) {
+    if (ok()) {
+      data_.~T();
+      MakeValue(std::move(value));
+    } else {
+      MakeValue(std::move(value));
+      status_ = OkStatus();
+    }
+  }
+
+  void Assign(const Status& status) {
+    Clear();
+    status_ = status;
+    EnsureNotOk();
+  }
+
+  void Assign(Status&& status) {
+    Clear();
+    // Note that we copy instead of moving the status here so that
+    // status.~StatusOrData() can call ok() without invoking UB.
+    status_ = status;
+    EnsureNotOk();
+  }
+
+  bool ok() const { return status_.ok(); }
+
+ protected:
+  // status_ will always be active after the constructor.
+  // We make it a union to be able to initialize exactly how we need without
+  // waste.
+  // Eg. in the copy constructor we use the default constructor of Status in
+  // the ok() path to avoid an extra Ref call.
+  union {
+    Status status_;
+  };
+
+  // data_ is active iff status_.ok()==true
+  struct Dummy {};
+  union {
+    // When T is const, we need some non-const object we can cast to void* for
+    // the placement new. dummy_ is that object.
+    Dummy dummy_;
+    T data_;
+  };
+
+  void Clear() {
+    if (ok()) data_.~T();
+  }
+
+  void EnsureOk() const {
+    if (!ok()) Helper::Crash(status_);
+  }
+
+  void EnsureNotOk() {
+    if (ok()) Helper::HandleInvalidStatusCtorArg(&status_);
+  }
+
+  // Construct the value (ie. data_) through placement new with the passed
+  // argument.
+  template <typename Arg>
+  void MakeValue(Arg&& arg) {
+    internal_statusor::PlacementNew<T>(&dummy_, std::forward<Arg>(arg));
+  }
+
+  // Construct the status (ie. status_) through placement new with the passed
+  // argument.
+  template <typename... Args>
+  void MakeStatus(Args&&... args) {
+    internal_statusor::PlacementNew<Status>(&status_,
+                                            std::forward<Args>(args)...);
+  }
+};
+
+// Helper base class to allow implicitly deleted constructors and assignment
+// operations in StatusOr.
+// TraitsBase will explicitly delete what it can't support and StatusOr will
+// inherit that behavior implicitly.
+template <bool Copy, bool Move>
+struct TraitsBase {
+  TraitsBase() = default;
+  TraitsBase(const TraitsBase&) = default;
+  TraitsBase(TraitsBase&&) = default;
+  TraitsBase& operator=(const TraitsBase&) = default;
+  TraitsBase& operator=(TraitsBase&&) = default;
+};
+
+template <>
+struct TraitsBase<false, true> {
+  TraitsBase() = default;
+  TraitsBase(const TraitsBase&) = delete;
+  TraitsBase(TraitsBase&&) = default;
+  TraitsBase& operator=(const TraitsBase&) = delete;
+  TraitsBase& operator=(TraitsBase&&) = default;
+};
+
+template <>
+struct TraitsBase<false, false> {
+  TraitsBase() = default;
+  TraitsBase(const TraitsBase&) = delete;
+  TraitsBase(TraitsBase&&) = delete;
+  TraitsBase& operator=(const TraitsBase&) = delete;
+  TraitsBase& operator=(TraitsBase&&) = delete;
+};
+
+}  // namespace internal_statusor
+
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif  // ABSL_STATUS_STATUSOR_INTERNALS_H_
diff --git a/third_party/abseil_cpp/absl/status/statusor_test.cc b/third_party/abseil_cpp/absl/status/statusor_test.cc
new file mode 100644
index 0000000000..fc849515ca
--- /dev/null
+++ b/third_party/abseil_cpp/absl/status/statusor_test.cc
@@ -0,0 +1,753 @@
+/* Copyright 2017 The TensorFlow Authors. All Rights Reserved.
+
+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
+
+    http://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.
+==============================================================================*/
+
+// Unit tests for StatusOr
+
+#include "absl/status/statusor.h"
+
+#include <memory>
+#include <type_traits>
+
+#include "absl/base/internal/exception_testing.h"
+#include "gtest/gtest.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+
+namespace {
+
+class Base1 {
+ public:
+  virtual ~Base1() {}
+  int pad_;
+};
+
+class Base2 {
+ public:
+  virtual ~Base2() {}
+  int yetotherpad_;
+};
+
+class Derived : public Base1, public Base2 {
+ public:
+  ~Derived() override {}
+  int evenmorepad_;
+};
+
+class CopyNoAssign {
+ public:
+  explicit CopyNoAssign(int value) : foo_(value) {}
+  CopyNoAssign(const CopyNoAssign& other) : foo_(other.foo_) {}
+  int foo_;
+
+ private:
+  const CopyNoAssign& operator=(const CopyNoAssign&);
+};
+
+class NoDefaultConstructor {
+ public:
+  explicit NoDefaultConstructor(int foo);
+};
+
+static_assert(!std::is_default_constructible<NoDefaultConstructor>(),
+              "Should not be default-constructible.");
+
+StatusOr<std::unique_ptr<int>> ReturnUniquePtr() {
+  // Uses implicit constructor from T&&
+  return std::unique_ptr<int>(new int(0));
+}
+
+TEST(StatusOr, ElementType) {
+  static_assert(std::is_same<StatusOr<int>::element_type, int>(), "");
+  static_assert(std::is_same<StatusOr<char>::element_type, char>(), "");
+}
+
+TEST(StatusOr, NullPointerStatusOr) {
+  // As a very special case, null-plain-pointer StatusOr used to be an
+  // error. Test that it no longer is.
+  StatusOr<int*> null_status(nullptr);
+  EXPECT_TRUE(null_status.ok());
+  EXPECT_EQ(null_status.ValueOrDie(), nullptr);
+}
+
+TEST(StatusOr, TestNoDefaultConstructorInitialization) {
+  // Explicitly initialize it with an error code.
+  StatusOr<NoDefaultConstructor> statusor(CancelledError(""));
+  EXPECT_FALSE(statusor.ok());
+  EXPECT_EQ(statusor.status().code(), absl::StatusCode::kCancelled);
+
+  // Default construction of StatusOr initializes it with an UNKNOWN error code.
+  StatusOr<NoDefaultConstructor> statusor2;
+  EXPECT_FALSE(statusor2.ok());
+  EXPECT_EQ(statusor2.status().code(), absl::StatusCode::kUnknown);
+}
+
+TEST(StatusOr, TestMoveOnlyInitialization) {
+  StatusOr<std::unique_ptr<int>> thing(ReturnUniquePtr());
+  ASSERT_TRUE(thing.ok());
+  EXPECT_EQ(0, *thing.ValueOrDie());
+  int* previous = thing.ValueOrDie().get();
+
+  thing = ReturnUniquePtr();
+  EXPECT_TRUE(thing.ok());
+  EXPECT_EQ(0, *thing.ValueOrDie());
+  EXPECT_NE(previous, thing.ValueOrDie().get());
+}
+
+TEST(StatusOr, TestMoveOnlyStatusCtr) {
+  StatusOr<std::unique_ptr<int>> thing(CancelledError(""));
+  ASSERT_FALSE(thing.ok());
+}
+
+TEST(StatusOr, TestMoveOnlyValueExtraction) {
+  StatusOr<std::unique_ptr<int>> thing(ReturnUniquePtr());
+  ASSERT_TRUE(thing.ok());
+  std::unique_ptr<int> ptr = thing.ConsumeValueOrDie();
+  EXPECT_EQ(0, *ptr);
+
+  thing = std::move(ptr);
+  ptr = std::move(thing.ValueOrDie());
+  EXPECT_EQ(0, *ptr);
+}
+
+TEST(StatusOr, TestMoveOnlyConversion) {
+  StatusOr<std::unique_ptr<const int>> const_thing(ReturnUniquePtr());
+  EXPECT_TRUE(const_thing.ok());
+  EXPECT_EQ(0, *const_thing.ValueOrDie());
+
+  // Test rvalue converting assignment
+  const int* const_previous = const_thing.ValueOrDie().get();
+  const_thing = ReturnUniquePtr();
+  EXPECT_TRUE(const_thing.ok());
+  EXPECT_EQ(0, *const_thing.ValueOrDie());
+  EXPECT_NE(const_previous, const_thing.ValueOrDie().get());
+}
+
+TEST(StatusOr, TestMoveOnlyVector) {
+  // Sanity check that StatusOr<MoveOnly> works in vector.
+  std::vector<StatusOr<std::unique_ptr<int>>> vec;
+  vec.push_back(ReturnUniquePtr());
+  vec.resize(2);
+  auto another_vec = std::move(vec);
+  EXPECT_EQ(0, *another_vec[0].ValueOrDie());
+  EXPECT_EQ(absl::StatusCode::kUnknown, another_vec[1].status().code());
+}
+
+TEST(StatusOr, TestMoveWithValuesAndErrors) {
+  StatusOr<std::string> status_or(std::string(1000, '0'));
+  StatusOr<std::string> value1(std::string(1000, '1'));
+  StatusOr<std::string> value2(std::string(1000, '2'));
+  StatusOr<std::string> error1(UnknownError("error1"));
+  StatusOr<std::string> error2(UnknownError("error2"));
+
+  ASSERT_TRUE(status_or.ok());
+  EXPECT_EQ(std::string(1000, '0'), status_or.ValueOrDie());
+
+  // Overwrite the value in status_or with another value.
+  status_or = std::move(value1);
+  ASSERT_TRUE(status_or.ok());
+  EXPECT_EQ(std::string(1000, '1'), status_or.ValueOrDie());
+
+  // Overwrite the value in status_or with an error.
+  status_or = std::move(error1);
+  ASSERT_FALSE(status_or.ok());
+  EXPECT_EQ("error1", status_or.status().message());
+
+  // Overwrite the error in status_or with another error.
+  status_or = std::move(error2);
+  ASSERT_FALSE(status_or.ok());
+  EXPECT_EQ("error2", status_or.status().message());
+
+  // Overwrite the error with a value.
+  status_or = std::move(value2);
+  ASSERT_TRUE(status_or.ok());
+  EXPECT_EQ(std::string(1000, '2'), status_or.ValueOrDie());
+}
+
+TEST(StatusOr, TestCopyWithValuesAndErrors) {
+  StatusOr<std::string> status_or(std::string(1000, '0'));
+  StatusOr<std::string> value1(std::string(1000, '1'));
+  StatusOr<std::string> value2(std::string(1000, '2'));
+  StatusOr<std::string> error1(UnknownError("error1"));
+  StatusOr<std::string> error2(UnknownError("error2"));
+
+  ASSERT_TRUE(status_or.ok());
+  EXPECT_EQ(std::string(1000, '0'), status_or.ValueOrDie());
+
+  // Overwrite the value in status_or with another value.
+  status_or = value1;
+  ASSERT_TRUE(status_or.ok());
+  EXPECT_EQ(std::string(1000, '1'), status_or.ValueOrDie());
+
+  // Overwrite the value in status_or with an error.
+  status_or = error1;
+  ASSERT_FALSE(status_or.ok());
+  EXPECT_EQ("error1", status_or.status().message());
+
+  // Overwrite the error in status_or with another error.
+  status_or = error2;
+  ASSERT_FALSE(status_or.ok());
+  EXPECT_EQ("error2", status_or.status().message());
+
+  // Overwrite the error with a value.
+  status_or = value2;
+  ASSERT_TRUE(status_or.ok());
+  EXPECT_EQ(std::string(1000, '2'), status_or.ValueOrDie());
+
+  // Verify original values unchanged.
+  EXPECT_EQ(std::string(1000, '1'), value1.ValueOrDie());
+  EXPECT_EQ("error1", error1.status().message());
+  EXPECT_EQ("error2", error2.status().message());
+  EXPECT_EQ(std::string(1000, '2'), value2.ValueOrDie());
+}
+
+TEST(StatusOr, TestDefaultCtor) {
+  StatusOr<int> thing;
+  EXPECT_FALSE(thing.ok());
+  EXPECT_EQ(thing.status().code(), absl::StatusCode::kUnknown);
+}
+
+TEST(StatusOrDeathTest, TestDefaultCtorValue) {
+  StatusOr<int> thing;
+  ABSL_BASE_INTERNAL_EXPECT_FAIL(thing.ValueOrDie(), absl::Status, "");
+
+  const StatusOr<int> thing2;
+  ABSL_BASE_INTERNAL_EXPECT_FAIL(thing.ValueOrDie(), absl::Status, "");
+}
+
+TEST(StatusOr, TestStatusCtor) {
+  StatusOr<int> thing(Status(absl::StatusCode::kCancelled, ""));
+  EXPECT_FALSE(thing.ok());
+  EXPECT_EQ(thing.status().code(), absl::StatusCode::kCancelled);
+}
+
+TEST(StatusOr, TestValueCtor) {
+  const int kI = 4;
+  const StatusOr<int> thing(kI);
+  EXPECT_TRUE(thing.ok());
+  EXPECT_EQ(kI, thing.ValueOrDie());
+}
+
+TEST(StatusOr, TestCopyCtorStatusOk) {
+  const int kI = 4;
+  const StatusOr<int> original(kI);
+  const StatusOr<int> copy(original);
+  EXPECT_EQ(copy.status(), original.status());
+  EXPECT_EQ(original.ValueOrDie(), copy.ValueOrDie());
+}
+
+TEST(StatusOr, TestCopyCtorStatusNotOk) {
+  StatusOr<int> original(Status(absl::StatusCode::kCancelled, ""));
+  StatusOr<int> copy(original);
+  EXPECT_EQ(copy.status(), original.status());
+}
+
+TEST(StatusOr, TestCopyCtorNonAssignable) {
+  const int kI = 4;
+  CopyNoAssign value(kI);
+  StatusOr<CopyNoAssign> original(value);
+  StatusOr<CopyNoAssign> copy(original);
+  EXPECT_EQ(copy.status(), original.status());
+  EXPECT_EQ(original.ValueOrDie().foo_, copy.ValueOrDie().foo_);
+}
+
+TEST(StatusOr, TestCopyCtorStatusOKConverting) {
+  const int kI = 4;
+  StatusOr<int> original(kI);
+  StatusOr<double> copy(original);
+  EXPECT_EQ(copy.status(), original.status());
+  EXPECT_DOUBLE_EQ(original.ValueOrDie(), copy.ValueOrDie());
+}
+
+TEST(StatusOr, TestCopyCtorStatusNotOkConverting) {
+  StatusOr<int> original(Status(absl::StatusCode::kCancelled, ""));
+  StatusOr<double> copy(original);
+  EXPECT_EQ(copy.status(), original.status());
+}
+
+TEST(StatusOr, TestAssignmentStatusOk) {
+  const int kI = 4;
+  StatusOr<int> source(kI);
+  StatusOr<int> target;
+  target = source;
+  EXPECT_EQ(target.status(), source.status());
+  EXPECT_EQ(source.ValueOrDie(), target.ValueOrDie());
+}
+
+TEST(StatusOr, TestAssignmentStatusNotOk) {
+  StatusOr<int> source(Status(absl::StatusCode::kCancelled, ""));
+  StatusOr<int> target;
+  target = source;
+  EXPECT_EQ(target.status(), source.status());
+}
+
+TEST(StatusOr, TestStatus) {
+  StatusOr<int> good(4);
+  EXPECT_TRUE(good.ok());
+  StatusOr<int> bad(Status(absl::StatusCode::kCancelled, ""));
+  EXPECT_FALSE(bad.ok());
+  EXPECT_EQ(bad.status(), Status(absl::StatusCode::kCancelled, ""));
+}
+
+TEST(StatusOr, TestValue) {
+  const int kI = 4;
+  StatusOr<int> thing(kI);
+  EXPECT_EQ(kI, thing.ValueOrDie());
+}
+
+TEST(StatusOr, TestValueConst) {
+  const int kI = 4;
+  const StatusOr<int> thing(kI);
+  EXPECT_EQ(kI, thing.ValueOrDie());
+}
+
+TEST(StatusOrDeathTest, TestValueNotOk) {
+  StatusOr<int> thing(Status(absl::StatusCode::kCancelled, "cancelled"));
+  ABSL_BASE_INTERNAL_EXPECT_FAIL(thing.ValueOrDie(), absl::Status, "cancelled");
+}
+
+TEST(StatusOrDeathTest, TestValueNotOkConst) {
+  const StatusOr<int> thing(Status(absl::StatusCode::kUnknown, ""));
+  ABSL_BASE_INTERNAL_EXPECT_FAIL(thing.ValueOrDie(), absl::Status, "");
+}
+
+TEST(StatusOr, TestPointerDefaultCtor) {
+  StatusOr<int*> thing;
+  EXPECT_FALSE(thing.ok());
+  EXPECT_EQ(thing.status().code(), absl::StatusCode::kUnknown);
+}
+
+TEST(StatusOrDeathTest, TestPointerDefaultCtorValue) {
+  StatusOr<int*> thing;
+  ABSL_BASE_INTERNAL_EXPECT_FAIL(thing.ValueOrDie(), absl::Status, "");
+}
+
+TEST(StatusOr, TestPointerStatusCtor) {
+  StatusOr<int*> thing(Status(absl::StatusCode::kCancelled, ""));
+  EXPECT_FALSE(thing.ok());
+  EXPECT_EQ(thing.status(), Status(absl::StatusCode::kCancelled, ""));
+}
+
+TEST(StatusOr, TestPointerValueCtor) {
+  const int kI = 4;
+  StatusOr<const int*> thing(&kI);
+  EXPECT_TRUE(thing.ok());
+  EXPECT_EQ(&kI, thing.ValueOrDie());
+}
+
+TEST(StatusOr, TestPointerCopyCtorStatusOk) {
+  const int kI = 0;
+  StatusOr<const int*> original(&kI);
+  StatusOr<const int*> copy(original);
+  EXPECT_EQ(copy.status(), original.status());
+  EXPECT_EQ(original.ValueOrDie(), copy.ValueOrDie());
+}
+
+TEST(StatusOr, TestPointerCopyCtorStatusNotOk) {
+  StatusOr<int*> original(Status(absl::StatusCode::kCancelled, ""));
+  StatusOr<int*> copy(original);
+  EXPECT_EQ(copy.status(), original.status());
+}
+
+TEST(StatusOr, TestPointerCopyCtorStatusOKConverting) {
+  Derived derived;
+  StatusOr<Derived*> original(&derived);
+  StatusOr<Base2*> copy(original);
+  EXPECT_EQ(copy.status(), original.status());
+  EXPECT_EQ(static_cast<const Base2*>(original.ValueOrDie()),
+            copy.ValueOrDie());
+}
+
+TEST(StatusOr, TestPointerCopyCtorStatusNotOkConverting) {
+  StatusOr<Derived*> original(Status(absl::StatusCode::kCancelled, ""));
+  StatusOr<Base2*> copy(original);
+  EXPECT_EQ(copy.status(), original.status());
+}
+
+TEST(StatusOr, TestPointerAssignmentStatusOk) {
+  const int kI = 0;
+  StatusOr<const int*> source(&kI);
+  StatusOr<const int*> target;
+  target = source;
+  EXPECT_EQ(target.status(), source.status());
+  EXPECT_EQ(source.ValueOrDie(), target.ValueOrDie());
+}
+
+TEST(StatusOr, TestPointerAssignmentStatusNotOk) {
+  StatusOr<int*> source(Status(absl::StatusCode::kCancelled, ""));
+  StatusOr<int*> target;
+  target = source;
+  EXPECT_EQ(target.status(), source.status());
+}
+
+TEST(StatusOr, TestPointerStatus) {
+  const int kI = 0;
+  StatusOr<const int*> good(&kI);
+  EXPECT_TRUE(good.ok());
+  StatusOr<const int*> bad(Status(absl::StatusCode::kCancelled, ""));
+  EXPECT_EQ(bad.status(), Status(absl::StatusCode::kCancelled, ""));
+}
+
+TEST(StatusOr, TestPointerValue) {
+  const int kI = 0;
+  StatusOr<const int*> thing(&kI);
+  EXPECT_EQ(&kI, thing.ValueOrDie());
+}
+
+TEST(StatusOr, TestPointerValueConst) {
+  const int kI = 0;
+  const StatusOr<const int*> thing(&kI);
+  EXPECT_EQ(&kI, thing.ValueOrDie());
+}
+
+TEST(StatusOr, TestArrowOperator) {
+  StatusOr<std::unique_ptr<int>> uptr = ReturnUniquePtr();
+  EXPECT_EQ(*uptr->get(), 0);
+}
+
+TEST(StatusOr, TestArrowOperatorNotOk) {
+  StatusOr<Base1> error(Status(absl::StatusCode::kCancelled, "cancelled"));
+  ABSL_BASE_INTERNAL_EXPECT_FAIL(error->pad_++, absl::Status, "cancelled");
+}
+
+TEST(StatusOr, TestStarOperator) {
+  StatusOr<std::unique_ptr<int>> uptr = ReturnUniquePtr();
+  EXPECT_EQ(**uptr, 0);
+}
+
+TEST(StatusOr, TestStarOperatorDeath) {
+  StatusOr<Base1> error(Status(absl::StatusCode::kCancelled, "cancelled"));
+  ABSL_BASE_INTERNAL_EXPECT_FAIL(*error, absl::Status, "cancelled");
+}
+
+// NOTE(tucker): StatusOr does not support this kind
+// of resize op.
+// TEST(StatusOr, StatusOrVectorOfUniquePointerCanResize) {
+//   using EvilType = std::vector<std::unique_ptr<int>>;
+//   static_assert(std::is_copy_constructible<EvilType>::value, "");
+//   std::vector<StatusOr<EvilType>> v(5);
+//   v.reserve(v.capacity() + 10);
+// }
+
+TEST(StatusOrDeathTest, TestPointerValueNotOk) {
+  StatusOr<int*> thing(Status(absl::StatusCode::kCancelled, "cancelled"));
+  ABSL_BASE_INTERNAL_EXPECT_FAIL(thing.ValueOrDie(), absl::Status, "cancelled");
+}
+
+TEST(StatusOrDeathTest, TestPointerValueNotOkConst) {
+  const StatusOr<int*> thing(Status(absl::StatusCode::kCancelled, "cancelled"));
+  ABSL_BASE_INTERNAL_EXPECT_FAIL(thing.ValueOrDie(), absl::Status, "cancelled");
+}
+
+static void AssertOkAndAssignBody(absl::StatusOr<int> consume) {
+  ASSERT_OK_AND_ASSIGN(int value, consume);
+  EXPECT_EQ(value, 1);
+}
+
+TEST(StatusOr, TestAssertOkAndAssign) {
+  const int kI = 1;
+  AssertOkAndAssignBody(kI);
+}
+
+TEST(StatusOrDeathTest, TestAssertOkAndAssignNotOk) {
+  // Can't actually test this, as calling ASSERT_TRUE fails the test.
+}
+
+static absl::Status AssignOrReturnBody(absl::StatusOr<int*> maybe) {
+  ASSIGN_OR_RETURN(int *iptr, maybe);
+  EXPECT_EQ(*iptr, 1);
+  *iptr = 4;
+  return OkStatus();
+}
+
+TEST(StatusOr, TestAssignOrReturn) {
+  int i = 1;
+  EXPECT_TRUE(AssignOrReturnBody(&i).ok());
+  EXPECT_EQ(i, 4);
+}
+
+TEST(StatusOr, TestAssignOrReturnNotOk) {
+  const StatusOr<int*> thing(Status(absl::StatusCode::kCancelled, "cancelled"));
+  const Status result = AssignOrReturnBody(thing);
+  EXPECT_FALSE(result.ok());
+  EXPECT_EQ(result, thing.status());
+}
+
+static absl::Status ReturnIfErrorBody(absl::Status status, int* iptr) {
+  RETURN_IF_ERROR(status);
+  EXPECT_EQ(*iptr, 1);
+  *iptr = 4;
+  return OkStatus();
+}
+
+TEST(StatusOr, TestReturnIfError) {
+  int i = 1;
+  EXPECT_TRUE(ReturnIfErrorBody(OkStatus(), &i).ok());
+  EXPECT_EQ(i, 4);
+}
+
+TEST(StatusOr, TestReturnIfErrorNotOk) {
+  int i = 1;
+  Status thing(absl::StatusCode::kCancelled, "");
+  EXPECT_FALSE(ReturnIfErrorBody(thing, &i).ok());
+  EXPECT_EQ(i, 1);
+}
+
+/*
+static StatusOr<int> MakeStatus() { return 100; }
+
+// A factory to help us benchmark the various factory styles. All of
+// the factory methods are marked as non-inlineable so as to more
+// accurately simulate calling a factory for which you do not have
+// visibility of implementation. Similarly, the value_ variable is
+// marked volatile to prevent the compiler from getting too clever
+// about detecting that the same value is used in all loop iterations.
+template <typename T>
+class BenchmarkFactory {
+ public:
+  // Construct a new factory. Allocate an object which will always
+  // be the result of the factory methods.
+  BenchmarkFactory() : value_(new T) {}
+
+  // Destroy this factory, including the result value.
+  ~BenchmarkFactory() { delete value_; }
+
+  // A trivial factory that just returns the value. There is no status
+  // object that could be returned to encapsulate an error
+  T* TrivialFactory() ABSL_ATTRIBUTE_NOINLINE { return value_; }
+
+  // A more sophisticated factory, which returns a status to indicate
+  // the result of the operation. The factory result is populated into
+  // the user provided pointer result.
+  Status ArgumentFactory(T** result) ABSL_ATTRIBUTE_NOINLINE {
+    *result = value_;
+    return Status::OK();
+  }
+
+  Status ArgumentFactoryFail(T** result) ABSL_ATTRIBUTE_NOINLINE {
+    *result = nullptr;
+    return CancelledError("");
+  }
+
+  Status ArgumentFactoryFailShortMsg(T** result) ABSL_ATTRIBUTE_NOINLINE {
+    *result = nullptr;
+    return InternalError("");
+  }
+
+  Status ArgumentFactoryFailLongMsg(T** result) ABSL_ATTRIBUTE_NOINLINE {
+    *result = nullptr;
+    return InternalError(,
+                  "a big string of message junk that will never be read");
+  }
+
+  // A factory that returns a StatusOr<T*>. If the factory operation
+  // is OK, then the StatusOr<T*> will hold a T*. Otherwise, it will
+  // hold a status explaining the error.
+  StatusOr<T*> StatusOrFactory() ABSL_ATTRIBUTE_NOINLINE {
+    return static_cast<T*>(value_);
+  }
+
+  StatusOr<T*> StatusOrFactoryFail() ABSL_ATTRIBUTE_NOINLINE {
+    return CancelledError("");
+  }
+
+  StatusOr<T*> StatusOrFactoryFailShortMsg() ABSL_ATTRIBUTE_NOINLINE {
+    return InternalError("i");
+  }
+
+  StatusOr<T*> StatusOrFactoryFailLongMsg() ABSL_ATTRIBUTE_NOINLINE {
+    return InternalError(
+	"a big string of message junk that will never be read");
+  }
+
+ private:
+  T* volatile value_;
+  ABSL_DISALLOW_COPY_AND_ASSIGN(BenchmarkFactory);
+};
+
+// A simple type we use with the factory.
+class BenchmarkType {
+ public:
+  BenchmarkType() {}
+  virtual ~BenchmarkType() {}
+  virtual void DoWork() ABSL_ATTRIBUTE_NOINLINE {}
+
+ private:
+  ABSL_DISALLOW_COPY_AND_ASSIGN(BenchmarkType);
+};
+
+// Calibrate the amount of time spent just calling DoWork, since each of our
+// tests will do this, we can subtract this out of benchmark results.
+void BM_CalibrateWorkLoop(int iters) {
+  tensorflow::testing::StopTiming();
+  BenchmarkFactory<BenchmarkType> factory;
+  BenchmarkType* result = factory.TrivialFactory();
+  tensorflow::testing::StartTiming();
+  for (int i = 0; i != iters; ++i) {
+    if (result != nullptr) {
+      result->DoWork();
+    }
+  }
+}
+BENCHMARK(BM_CalibrateWorkLoop);
+
+// Measure the time taken to call into the factory, return the value,
+// determine that it is OK, and invoke a trivial function.
+void BM_TrivialFactory(int iters) {
+  tensorflow::testing::StopTiming();
+  BenchmarkFactory<BenchmarkType> factory;
+  tensorflow::testing::StartTiming();
+  for (int i = 0; i != iters; ++i) {
+    BenchmarkType* result = factory.TrivialFactory();
+    if (result != nullptr) {
+      result->DoWork();
+    }
+  }
+}
+BENCHMARK(BM_TrivialFactory);
+
+// Measure the time taken to call into the factory, providing an
+// out-param for the result, evaluating the status result and the
+// result pointer, and invoking the trivial function.
+void BM_ArgumentFactory(int iters) {
+  tensorflow::testing::StopTiming();
+  BenchmarkFactory<BenchmarkType> factory;
+  tensorflow::testing::StartTiming();
+  for (int i = 0; i != iters; ++i) {
+    BenchmarkType* result = nullptr;
+    Status status = factory.ArgumentFactory(&result);
+    if (status.ok() && result != nullptr) {
+      result->DoWork();
+    }
+  }
+}
+BENCHMARK(BM_ArgumentFactory);
+
+// Measure the time to use the StatusOr<T*> factory, evaluate the result,
+// and invoke the trivial function.
+void BM_StatusOrFactory(int iters) {
+  tensorflow::testing::StopTiming();
+  BenchmarkFactory<BenchmarkType> factory;
+  tensorflow::testing::StartTiming();
+  for (int i = 0; i != iters; ++i) {
+    StatusOr<BenchmarkType*> result = factory.StatusOrFactory();
+    if (result.ok()) {
+      result.ValueOrDie()->DoWork();
+    }
+  }
+}
+BENCHMARK(BM_StatusOrFactory);
+
+// Measure the time taken to call into the factory, providing an
+// out-param for the result, evaluating the status result and the
+// result pointer, and invoking the trivial function.
+void BM_ArgumentFactoryFail(int iters) {
+  tensorflow::testing::StopTiming();
+  BenchmarkFactory<BenchmarkType> factory;
+  tensorflow::testing::StartTiming();
+  for (int i = 0; i != iters; ++i) {
+    BenchmarkType* result = nullptr;
+    Status status = factory.ArgumentFactoryFail(&result);
+    if (status.ok() && result != nullptr) {
+      result->DoWork();
+    }
+  }
+}
+BENCHMARK(BM_ArgumentFactoryFail);
+
+// Measure the time to use the StatusOr<T*> factory, evaluate the result,
+// and invoke the trivial function.
+void BM_StatusOrFactoryFail(int iters) {
+  tensorflow::testing::StopTiming();
+  BenchmarkFactory<BenchmarkType> factory;
+  tensorflow::testing::StartTiming();
+  for (int i = 0; i != iters; ++i) {
+    StatusOr<BenchmarkType*> result = factory.StatusOrFactoryFail();
+    if (result.ok()) {
+      result.ValueOrDie()->DoWork();
+    }
+  }
+}
+BENCHMARK(BM_StatusOrFactoryFail);
+
+// Measure the time taken to call into the factory, providing an
+// out-param for the result, evaluating the status result and the
+// result pointer, and invoking the trivial function.
+void BM_ArgumentFactoryFailShortMsg(int iters) {
+  tensorflow::testing::StopTiming();
+  BenchmarkFactory<BenchmarkType> factory;
+  tensorflow::testing::StartTiming();
+  for (int i = 0; i != iters; ++i) {
+    BenchmarkType* result = nullptr;
+    Status status = factory.ArgumentFactoryFailShortMsg(&result);
+    if (status.ok() && result != nullptr) {
+      result->DoWork();
+    }
+  }
+}
+BENCHMARK(BM_ArgumentFactoryFailShortMsg);
+
+// Measure the time to use the StatusOr<T*> factory, evaluate the result,
+// and invoke the trivial function.
+void BM_StatusOrFactoryFailShortMsg(int iters) {
+  tensorflow::testing::StopTiming();
+  BenchmarkFactory<BenchmarkType> factory;
+  tensorflow::testing::StartTiming();
+  for (int i = 0; i != iters; ++i) {
+    StatusOr<BenchmarkType*> result = factory.StatusOrFactoryFailShortMsg();
+    if (result.ok()) {
+      result.ValueOrDie()->DoWork();
+    }
+  }
+}
+BENCHMARK(BM_StatusOrFactoryFailShortMsg);
+
+// Measure the time taken to call into the factory, providing an
+// out-param for the result, evaluating the status result and the
+// result pointer, and invoking the trivial function.
+void BM_ArgumentFactoryFailLongMsg(int iters) {
+  tensorflow::testing::StopTiming();
+  BenchmarkFactory<BenchmarkType> factory;
+  tensorflow::testing::StartTiming();
+  for (int i = 0; i != iters; ++i) {
+    BenchmarkType* result = nullptr;
+    Status status = factory.ArgumentFactoryFailLongMsg(&result);
+    if (status.ok() && result != nullptr) {
+      result->DoWork();
+    }
+  }
+}
+BENCHMARK(BM_ArgumentFactoryFailLongMsg);
+
+// Measure the time to use the StatusOr<T*> factory, evaluate the result,
+// and invoke the trivial function.
+void BM_StatusOrFactoryFailLongMsg(int iters) {
+  tensorflow::testing::StopTiming();
+  BenchmarkFactory<BenchmarkType> factory;
+  tensorflow::testing::StartTiming();
+  for (int i = 0; i != iters; ++i) {
+    StatusOr<BenchmarkType*> result = factory.StatusOrFactoryFailLongMsg();
+    if (result.ok()) {
+      result.ValueOrDie()->DoWork();
+    }
+  }
+}
+BENCHMARK(BM_StatusOrFactoryFailLongMsg);
+*/
+
+}  // namespace
+
+ABSL_NAMESPACE_END
+}  // namespace absl
diff --git a/third_party/abseil_cpp/default.nix b/third_party/abseil_cpp/default.nix
index 849bbbc9d5..e154d3c95d 100644
--- a/third_party/abseil_cpp/default.nix
+++ b/third_party/abseil_cpp/default.nix
@@ -3,12 +3,15 @@
 let inherit (pkgs) cmake llvmPackages;
 in llvmPackages.libcxxStdenv.mkDerivation rec {
   pname = "abseil-cpp";
-  version = "20200519-768eb2ca";
+  version = "20200519-768eb2ca+tvl-1";
   src = ./.;
   nativeBuildInputs = [ cmake ];
+  # TODO: run tests
+  # doCheck = true;
 
   cmakeFlags = [
     "-DCMAKE_CXX_STANDARD=17"
+    #"-DABSL_RUN_TESTS=1"
   ];
 
   meta = with lib; {