diff --git a/CMake/AbseilDll.cmake b/CMake/AbseilDll.cmake
index 1927279..7f03025 100644
--- a/CMake/AbseilDll.cmake
+++ b/CMake/AbseilDll.cmake
@@ -150,6 +150,7 @@
   "hash/internal/low_level_hash.cc"
   "log/absl_check.h"
   "log/absl_log.h"
+  "log/absl_vlog_is_on.h"
   "log/check.h"
   "log/die_if_null.cc"
   "log/die_if_null.h"
@@ -180,6 +181,8 @@
   "log/internal/proto.cc"
   "log/internal/strip.h"
   "log/internal/structured.h"
+  "log/internal/vlog_config.cc"
+  "log/internal/vlog_config.h"
   "log/internal/voidify.h"
   "log/initialize.cc"
   "log/initialize.h"
@@ -191,6 +194,7 @@
   "log/log_sink_registry.h"
   "log/log_streamer.h"
   "log/structured.h"
+  "log/vlog_is_on.h"
   "memory/memory.h"
   "meta/type_traits.h"
   "numeric/bits.h"
diff --git a/absl/algorithm/BUILD.bazel b/absl/algorithm/BUILD.bazel
index a339dc1..ddf9e11 100644
--- a/absl/algorithm/BUILD.bazel
+++ b/absl/algorithm/BUILD.bazel
@@ -66,6 +66,7 @@
     deps = [
         ":algorithm",
         "//absl/base:core_headers",
+        "//absl/base:nullability",
         "//absl/meta:type_traits",
     ],
 )
diff --git a/absl/algorithm/CMakeLists.txt b/absl/algorithm/CMakeLists.txt
index 181b49c..5577164 100644
--- a/absl/algorithm/CMakeLists.txt
+++ b/absl/algorithm/CMakeLists.txt
@@ -50,6 +50,7 @@
     absl::algorithm
     absl::core_headers
     absl::meta
+    absl::nullability
   PUBLIC
 )
 
diff --git a/absl/algorithm/container.h b/absl/algorithm/container.h
index b6684c0..934dd17 100644
--- a/absl/algorithm/container.h
+++ b/absl/algorithm/container.h
@@ -52,6 +52,7 @@
 
 #include "absl/algorithm/algorithm.h"
 #include "absl/base/macros.h"
+#include "absl/base/nullability.h"
 #include "absl/meta/type_traits.h"
 
 namespace absl {
diff --git a/absl/base/BUILD.bazel b/absl/base/BUILD.bazel
index 60d05a8..06bd495 100644
--- a/absl/base/BUILD.bazel
+++ b/absl/base/BUILD.bazel
@@ -287,6 +287,7 @@
         ":cycleclock_internal",
         ":dynamic_annotations",
         ":log_severity",
+        ":nullability",
         ":raw_logging_internal",
         ":spinlock_wait",
         "//absl/meta:type_traits",
@@ -549,6 +550,7 @@
         ":base",
         ":config",
         ":core_headers",
+        ":nullability",
     ],
 )
 
diff --git a/absl/base/CMakeLists.txt b/absl/base/CMakeLists.txt
index 9ca5cf8..4cfc228 100644
--- a/absl/base/CMakeLists.txt
+++ b/absl/base/CMakeLists.txt
@@ -247,6 +247,7 @@
     absl::core_headers
     absl::dynamic_annotations
     absl::log_severity
+    absl::nullability
     absl::raw_logging_internal
     absl::spinlock_wait
     absl::type_traits
@@ -475,6 +476,7 @@
     absl::base
     absl::config
     absl::core_headers
+    absl::nullability
   PUBLIC
 )
 
diff --git a/absl/base/call_once.h b/absl/base/call_once.h
index 08436ba..7b0e69c 100644
--- a/absl/base/call_once.h
+++ b/absl/base/call_once.h
@@ -37,6 +37,7 @@
 #include "absl/base/internal/scheduling_mode.h"
 #include "absl/base/internal/spinlock_wait.h"
 #include "absl/base/macros.h"
+#include "absl/base/nullability.h"
 #include "absl/base/optimization.h"
 #include "absl/base/port.h"
 
@@ -46,7 +47,8 @@
 class once_flag;
 
 namespace base_internal {
-std::atomic<uint32_t>* ControlWord(absl::once_flag* flag);
+absl::Nonnull<std::atomic<uint32_t>*> ControlWord(
+    absl::Nonnull<absl::once_flag*> flag);
 }  // namespace base_internal
 
 // call_once()
@@ -89,7 +91,8 @@
   once_flag& operator=(const once_flag&) = delete;
 
  private:
-  friend std::atomic<uint32_t>* base_internal::ControlWord(once_flag* flag);
+  friend absl::Nonnull<std::atomic<uint32_t>*> base_internal::ControlWord(
+      absl::Nonnull<once_flag*> flag);
   std::atomic<uint32_t> control_;
 };
 
@@ -103,7 +106,8 @@
 // Like call_once, but uses KERNEL_ONLY scheduling. Intended to be used to
 // initialize entities used by the scheduler implementation.
 template <typename Callable, typename... Args>
-void LowLevelCallOnce(absl::once_flag* flag, Callable&& fn, Args&&... args);
+void LowLevelCallOnce(absl::Nonnull<absl::once_flag*> flag, Callable&& fn,
+                      Args&&... args);
 
 // Disables scheduling while on stack when scheduling mode is non-cooperative.
 // No effect for cooperative scheduling modes.
@@ -143,10 +147,10 @@
 };
 
 template <typename Callable, typename... Args>
-ABSL_ATTRIBUTE_NOINLINE
-void CallOnceImpl(std::atomic<uint32_t>* control,
-                  base_internal::SchedulingMode scheduling_mode, Callable&& fn,
-                  Args&&... args) {
+ABSL_ATTRIBUTE_NOINLINE void CallOnceImpl(
+    absl::Nonnull<std::atomic<uint32_t>*> control,
+    base_internal::SchedulingMode scheduling_mode, Callable&& fn,
+    Args&&... args) {
 #ifndef NDEBUG
   {
     uint32_t old_control = control->load(std::memory_order_relaxed);
@@ -185,12 +189,14 @@
   }  // else *control is already kOnceDone
 }
 
-inline std::atomic<uint32_t>* ControlWord(once_flag* flag) {
+inline absl::Nonnull<std::atomic<uint32_t>*> ControlWord(
+    absl::Nonnull<once_flag*> flag) {
   return &flag->control_;
 }
 
 template <typename Callable, typename... Args>
-void LowLevelCallOnce(absl::once_flag* flag, Callable&& fn, Args&&... args) {
+void LowLevelCallOnce(absl::Nonnull<absl::once_flag*> flag, Callable&& fn,
+                      Args&&... args) {
   std::atomic<uint32_t>* once = base_internal::ControlWord(flag);
   uint32_t s = once->load(std::memory_order_acquire);
   if (ABSL_PREDICT_FALSE(s != base_internal::kOnceDone)) {
diff --git a/absl/base/config.h b/absl/base/config.h
index b0e7fe8..0fb6692 100644
--- a/absl/base/config.h
+++ b/absl/base/config.h
@@ -609,6 +609,22 @@
 #define ABSL_HAVE_STD_STRING_VIEW 1
 #endif
 
+// ABSL_HAVE_STD_ORDERING
+//
+// Checks whether C++20 std::{partial,weak,strong}_ordering are available.
+//
+// __cpp_lib_three_way_comparison is missing on libc++
+// (https://github.com/llvm/llvm-project/issues/73953) so treat it as defined
+// when building in C++20 mode.
+#ifdef ABSL_HAVE_STD_ORDERING
+#error "ABSL_HAVE_STD_ORDERING cannot be directly set."
+#elif (defined(__cpp_lib_three_way_comparison) &&    \
+       __cpp_lib_three_way_comparison >= 201907L) || \
+    (defined(ABSL_INTERNAL_CPLUSPLUS_LANG) &&        \
+     ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L)
+#define ABSL_HAVE_STD_ORDERING 1
+#endif
+
 // ABSL_USES_STD_ANY
 //
 // Indicates whether absl::any is an alias for std::any.
@@ -671,6 +687,22 @@
 #error options.h is misconfigured.
 #endif
 
+// ABSL_USES_STD_ORDERING
+//
+// Indicates whether absl::{partial,weak,strong}_ordering are aliases for the
+// std:: ordering types.
+#if !defined(ABSL_OPTION_USE_STD_ORDERING)
+#error options.h is misconfigured.
+#elif ABSL_OPTION_USE_STD_ORDERING == 0 || \
+    (ABSL_OPTION_USE_STD_ORDERING == 2 && !defined(ABSL_HAVE_STD_ORDERING))
+#undef ABSL_USES_STD_ORDERING
+#elif ABSL_OPTION_USE_STD_ORDERING == 1 || \
+    (ABSL_OPTION_USE_STD_ORDERING == 2 && defined(ABSL_HAVE_STD_ORDERING))
+#define ABSL_USES_STD_ORDERING 1
+#else
+#error options.h is misconfigured.
+#endif
+
 // In debug mode, MSVC 2017's std::variant throws a EXCEPTION_ACCESS_VIOLATION
 // SEH exception from emplace for variant<SomeStruct> when constructing the
 // struct can throw. This defeats some of variant_test and
diff --git a/absl/base/internal/endian.h b/absl/base/internal/endian.h
index 50747d7..943f3d9 100644
--- a/absl/base/internal/endian.h
+++ b/absl/base/internal/endian.h
@@ -22,6 +22,7 @@
 #include "absl/base/casts.h"
 #include "absl/base/config.h"
 #include "absl/base/internal/unaligned_access.h"
+#include "absl/base/nullability.h"
 #include "absl/base/port.h"
 
 namespace absl {
@@ -160,27 +161,27 @@
 }
 
 // Functions to do unaligned loads and stores in little-endian order.
-inline uint16_t Load16(const void *p) {
+inline uint16_t Load16(absl::Nonnull<const void *> p) {
   return ToHost16(ABSL_INTERNAL_UNALIGNED_LOAD16(p));
 }
 
-inline void Store16(void *p, uint16_t v) {
+inline void Store16(absl::Nonnull<void *> p, uint16_t v) {
   ABSL_INTERNAL_UNALIGNED_STORE16(p, FromHost16(v));
 }
 
-inline uint32_t Load32(const void *p) {
+inline uint32_t Load32(absl::Nonnull<const void *> p) {
   return ToHost32(ABSL_INTERNAL_UNALIGNED_LOAD32(p));
 }
 
-inline void Store32(void *p, uint32_t v) {
+inline void Store32(absl::Nonnull<void *> p, uint32_t v) {
   ABSL_INTERNAL_UNALIGNED_STORE32(p, FromHost32(v));
 }
 
-inline uint64_t Load64(const void *p) {
+inline uint64_t Load64(absl::Nonnull<const void *> p) {
   return ToHost64(ABSL_INTERNAL_UNALIGNED_LOAD64(p));
 }
 
-inline void Store64(void *p, uint64_t v) {
+inline void Store64(absl::Nonnull<void *> p, uint64_t v) {
   ABSL_INTERNAL_UNALIGNED_STORE64(p, FromHost64(v));
 }
 
@@ -250,27 +251,27 @@
 }
 
 // Functions to do unaligned loads and stores in big-endian order.
-inline uint16_t Load16(const void *p) {
+inline uint16_t Load16(absl::Nonnull<const void *> p) {
   return ToHost16(ABSL_INTERNAL_UNALIGNED_LOAD16(p));
 }
 
-inline void Store16(void *p, uint16_t v) {
+inline void Store16(absl::Nonnull<void *> p, uint16_t v) {
   ABSL_INTERNAL_UNALIGNED_STORE16(p, FromHost16(v));
 }
 
-inline uint32_t Load32(const void *p) {
+inline uint32_t Load32(absl::Nonnull<const void *> p) {
   return ToHost32(ABSL_INTERNAL_UNALIGNED_LOAD32(p));
 }
 
-inline void Store32(void *p, uint32_t v) {
+inline void Store32(absl::Nonnull<void *>p, uint32_t v) {
   ABSL_INTERNAL_UNALIGNED_STORE32(p, FromHost32(v));
 }
 
-inline uint64_t Load64(const void *p) {
+inline uint64_t Load64(absl::Nonnull<const void *> p) {
   return ToHost64(ABSL_INTERNAL_UNALIGNED_LOAD64(p));
 }
 
-inline void Store64(void *p, uint64_t v) {
+inline void Store64(absl::Nonnull<void *> p, uint64_t v) {
   ABSL_INTERNAL_UNALIGNED_STORE64(p, FromHost64(v));
 }
 
diff --git a/absl/base/internal/raw_logging.cc b/absl/base/internal/raw_logging.cc
index 69b9426..d32b40a 100644
--- a/absl/base/internal/raw_logging.cc
+++ b/absl/base/internal/raw_logging.cc
@@ -42,8 +42,9 @@
 // This preprocessor token is also defined in raw_io.cc.  If you need to copy
 // this, consider moving both to config.h instead.
 #if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \
-    defined(__Fuchsia__) || defined(__native_client__) ||               \
-    defined(__OpenBSD__) || defined(__EMSCRIPTEN__) || defined(__ASYLO__)
+    defined(__hexagon__) || defined(__Fuchsia__) ||                     \
+    defined(__native_client__) || defined(__OpenBSD__) ||               \
+    defined(__EMSCRIPTEN__) || defined(__ASYLO__)
 
 #include <unistd.h>
 
diff --git a/absl/base/internal/unaligned_access.h b/absl/base/internal/unaligned_access.h
index 093dd9b..4fea457 100644
--- a/absl/base/internal/unaligned_access.h
+++ b/absl/base/internal/unaligned_access.h
@@ -23,6 +23,7 @@
 
 #include "absl/base/attributes.h"
 #include "absl/base/config.h"
+#include "absl/base/nullability.h"
 
 // unaligned APIs
 
@@ -35,29 +36,35 @@
 ABSL_NAMESPACE_BEGIN
 namespace base_internal {
 
-inline uint16_t UnalignedLoad16(const void *p) {
+inline uint16_t UnalignedLoad16(absl::Nonnull<const void *> p) {
   uint16_t t;
   memcpy(&t, p, sizeof t);
   return t;
 }
 
-inline uint32_t UnalignedLoad32(const void *p) {
+inline uint32_t UnalignedLoad32(absl::Nonnull<const void *> p) {
   uint32_t t;
   memcpy(&t, p, sizeof t);
   return t;
 }
 
-inline uint64_t UnalignedLoad64(const void *p) {
+inline uint64_t UnalignedLoad64(absl::Nonnull<const void *> p) {
   uint64_t t;
   memcpy(&t, p, sizeof t);
   return t;
 }
 
-inline void UnalignedStore16(void *p, uint16_t v) { memcpy(p, &v, sizeof v); }
+inline void UnalignedStore16(absl::Nonnull<void *> p, uint16_t v) {
+  memcpy(p, &v, sizeof v);
+}
 
-inline void UnalignedStore32(void *p, uint32_t v) { memcpy(p, &v, sizeof v); }
+inline void UnalignedStore32(absl::Nonnull<void *> p, uint32_t v) {
+  memcpy(p, &v, sizeof v);
+}
 
-inline void UnalignedStore64(void *p, uint64_t v) { memcpy(p, &v, sizeof v); }
+inline void UnalignedStore64(absl::Nonnull<void *> p, uint64_t v) {
+  memcpy(p, &v, sizeof v);
+}
 
 }  // namespace base_internal
 ABSL_NAMESPACE_END
diff --git a/absl/base/log_severity.h b/absl/base/log_severity.h
index c8bcd2f..d0795a2 100644
--- a/absl/base/log_severity.h
+++ b/absl/base/log_severity.h
@@ -99,13 +99,13 @@
 // Returns the all-caps string representation (e.g. "INFO") of the specified
 // severity level if it is one of the standard levels and "UNKNOWN" otherwise.
 constexpr const char* LogSeverityName(absl::LogSeverity s) {
-  return s == absl::LogSeverity::kInfo
-             ? "INFO"
-             : s == absl::LogSeverity::kWarning
-                   ? "WARNING"
-                   : s == absl::LogSeverity::kError
-                         ? "ERROR"
-                         : s == absl::LogSeverity::kFatal ? "FATAL" : "UNKNOWN";
+  switch (s) {
+    case absl::LogSeverity::kInfo: return "INFO";
+    case absl::LogSeverity::kWarning: return "WARNING";
+    case absl::LogSeverity::kError: return "ERROR";
+    case absl::LogSeverity::kFatal: return "FATAL";
+    default: return "UNKNOWN";
+  }
 }
 
 // NormalizeLogSeverity()
@@ -113,9 +113,10 @@
 // Values less than `kInfo` normalize to `kInfo`; values greater than `kFatal`
 // normalize to `kError` (**NOT** `kFatal`).
 constexpr absl::LogSeverity NormalizeLogSeverity(absl::LogSeverity s) {
-  return s < absl::LogSeverity::kInfo
-             ? absl::LogSeverity::kInfo
-             : s > absl::LogSeverity::kFatal ? absl::LogSeverity::kError : s;
+  absl::LogSeverity n = s;
+  if (n < absl::LogSeverity::kInfo) n = absl::LogSeverity::kInfo;
+  if (n > absl::LogSeverity::kFatal) n = absl::LogSeverity::kError;
+  return n;
 }
 constexpr absl::LogSeverity NormalizeLogSeverity(int s) {
   return absl::NormalizeLogSeverity(static_cast<absl::LogSeverity>(s));
diff --git a/absl/base/options.h b/absl/base/options.h
index 5c162a3..a169658 100644
--- a/absl/base/options.h
+++ b/absl/base/options.h
@@ -176,6 +176,32 @@
 
 #define ABSL_OPTION_USE_STD_VARIANT 2
 
+// ABSL_OPTION_USE_STD_ORDERING
+//
+// This option controls whether absl::{partial,weak,strong}_ordering are
+// implemented as aliases to the std:: ordering types, or as an independent
+// implementation.
+//
+// A value of 0 means to use Abseil's implementation.  This requires only C++11
+// support, and is expected to work on every toolchain we support.
+//
+// A value of 1 means to use aliases.  This requires that all code using Abseil
+// is built in C++20 mode or later.
+//
+// A value of 2 means to detect the C++ version being used to compile Abseil,
+// and use an alias only if working std:: ordering types are available.  This
+// option is useful when you are building your program from source.  It should
+// not be used otherwise -- for example, if you are distributing Abseil in a
+// binary package manager -- since in mode 2, they will name different types,
+// with different mangled names and binary layout, depending on the compiler
+// flags passed by the end user.  For more info, see
+// https://abseil.io/about/design/dropin-types.
+//
+// User code should not inspect this macro.  To check in the preprocessor if
+// the ordering types are aliases of std:: ordering types, use the feature macro
+// ABSL_USES_STD_ORDERING.
+
+#define ABSL_OPTION_USE_STD_ORDERING 2
 
 // ABSL_OPTION_USE_INLINE_NAMESPACE
 // ABSL_OPTION_INLINE_NAMESPACE_NAME
diff --git a/absl/container/internal/raw_hash_set.cc b/absl/container/internal/raw_hash_set.cc
index 6e5941d..6bc5f7b 100644
--- a/absl/container/internal/raw_hash_set.cc
+++ b/absl/container/internal/raw_hash_set.cc
@@ -17,11 +17,13 @@
 #include <atomic>
 #include <cassert>
 #include <cstddef>
+#include <cstdint>
 #include <cstring>
 
 #include "absl/base/attributes.h"
 #include "absl/base/config.h"
 #include "absl/base/dynamic_annotations.h"
+#include "absl/container/internal/container_memory.h"
 #include "absl/hash/hash.h"
 
 namespace absl {
@@ -126,14 +128,6 @@
   return find_first_non_full(common, hash);
 }
 
-// Returns the address of the ith slot in slots where each slot occupies
-// slot_size.
-static inline void* SlotAddress(void* slot_array, size_t slot,
-                                size_t slot_size) {
-  return reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(slot_array) +
-                                 (slot * slot_size));
-}
-
 // Returns the address of the slot just after slot assuming each slot has the
 // specified size.
 static inline void* NextSlot(void* slot, size_t slot_size) {
@@ -254,6 +248,7 @@
   c.set_size(0);
   if (reuse) {
     ResetCtrl(c, policy.slot_size);
+    ResetGrowthLeft(c);
     c.infoz().RecordStorageChanged(0, c.capacity());
   } else {
     // We need to record infoz before calling dealloc, which will unregister
@@ -268,6 +263,109 @@
   }
 }
 
+void HashSetResizeHelper::GrowIntoSingleGroupShuffleControlBytes(
+    ctrl_t* new_ctrl, size_t new_capacity) const {
+  assert(is_single_group(new_capacity));
+  constexpr size_t kHalfWidth = Group::kWidth / 2;
+  assert(old_capacity_ < kHalfWidth);
+
+  const size_t half_old_capacity = old_capacity_ / 2;
+
+  // NOTE: operations are done with compile time known size = kHalfWidth.
+  // Compiler optimizes that into single ASM operation.
+
+  // Copy second half of bytes to the beginning.
+  // We potentially copy more bytes in order to have compile time known size.
+  // Mirrored bytes from the old_ctrl_ will also be copied.
+  // In case of old_capacity_ == 3, we will copy 1st element twice.
+  // Examples:
+  // old_ctrl = 0S0EEEEEEE...
+  // new_ctrl = S0EEEEEEEE...
+  //
+  // old_ctrl = 01S01EEEEE...
+  // new_ctrl = 1S01EEEEEE...
+  //
+  // old_ctrl = 0123456S0123456EE...
+  // new_ctrl = 456S0123?????????...
+  std::memcpy(new_ctrl, old_ctrl_ + half_old_capacity + 1, kHalfWidth);
+  // Clean up copied kSentinel from old_ctrl.
+  new_ctrl[half_old_capacity] = ctrl_t::kEmpty;
+
+  // Clean up damaged or uninitialized bytes.
+
+  // Clean bytes after the intended size of the copy.
+  // Example:
+  // new_ctrl = 1E01EEEEEEE????
+  // *new_ctrl= 1E0EEEEEEEE????
+  // position      /
+  std::memset(new_ctrl + old_capacity_ + 1, static_cast<int8_t>(ctrl_t::kEmpty),
+              kHalfWidth);
+  // Clean non-mirrored bytes that are not initialized.
+  // For small old_capacity that may be inside of mirrored bytes zone.
+  // Examples:
+  // new_ctrl = 1E0EEEEEEEE??????????....
+  // *new_ctrl= 1E0EEEEEEEEEEEEE?????....
+  // position           /
+  //
+  // new_ctrl = 456E0123???????????...
+  // *new_ctrl= 456E0123EEEEEEEE???...
+  // position           /
+  std::memset(new_ctrl + kHalfWidth, static_cast<int8_t>(ctrl_t::kEmpty),
+              kHalfWidth);
+  // Clean last mirrored bytes that are not initialized
+  // and will not be overwritten by mirroring.
+  // Examples:
+  // new_ctrl = 1E0EEEEEEEEEEEEE????????
+  // *new_ctrl= 1E0EEEEEEEEEEEEEEEEEEEEE
+  // position           S       /
+  //
+  // new_ctrl = 456E0123EEEEEEEE???????????????
+  // *new_ctrl= 456E0123EEEEEEEE???????EEEEEEEE
+  // position                  S       /
+  std::memset(new_ctrl + new_capacity + kHalfWidth,
+              static_cast<int8_t>(ctrl_t::kEmpty), kHalfWidth);
+
+  // Create mirrored bytes. old_capacity_ < kHalfWidth
+  // Example:
+  // new_ctrl = 456E0123EEEEEEEE???????EEEEEEEE
+  // *new_ctrl= 456E0123EEEEEEEE456E0123EEEEEEE
+  // position                  S/
+  ctrl_t g[kHalfWidth];
+  std::memcpy(g, new_ctrl, kHalfWidth);
+  std::memcpy(new_ctrl + new_capacity + 1, g, kHalfWidth);
+
+  // Finally set sentinel to its place.
+  new_ctrl[new_capacity] = ctrl_t::kSentinel;
+}
+
+void HashSetResizeHelper::GrowIntoSingleGroupShuffleTransferableSlots(
+    void* old_slots, void* new_slots, size_t slot_size) const {
+  assert(old_capacity_ > 0);
+  const size_t half_old_capacity = old_capacity_ / 2;
+
+  SanitizerUnpoisonMemoryRegion(old_slots, slot_size * old_capacity_);
+  std::memcpy(new_slots,
+              SlotAddress(old_slots, half_old_capacity + 1, slot_size),
+              slot_size * half_old_capacity);
+  std::memcpy(SlotAddress(new_slots, half_old_capacity + 1, slot_size),
+              old_slots, slot_size * (half_old_capacity + 1));
+}
+
+void HashSetResizeHelper::GrowSizeIntoSingleGroupTransferable(
+    CommonFields& c, void* old_slots, size_t slot_size) {
+  assert(old_capacity_ < Group::kWidth / 2);
+  assert(is_single_group(c.capacity()));
+  assert(IsGrowingIntoSingleGroupApplicable(old_capacity_, c.capacity()));
+
+  GrowIntoSingleGroupShuffleControlBytes(c.control(), c.capacity());
+  GrowIntoSingleGroupShuffleTransferableSlots(old_slots, c.slot_array(),
+                                              slot_size);
+
+  // We poison since GrowIntoSingleGroupShuffleTransferableSlots
+  // may leave empty slots unpoisoned.
+  PoisonSingleGroupEmptySlots(c, slot_size);
+}
+
 }  // namespace container_internal
 ABSL_NAMESPACE_END
 }  // namespace absl
diff --git a/absl/container/internal/raw_hash_set.h b/absl/container/internal/raw_hash_set.h
index 067ea0d..b6d2cf9 100644
--- a/absl/container/internal/raw_hash_set.h
+++ b/absl/container/internal/raw_hash_set.h
@@ -1378,6 +1378,12 @@
 // `ShouldInsertBackwards()` for small tables.
 inline bool is_small(size_t capacity) { return capacity < Group::kWidth - 1; }
 
+// Whether a table fits entirely into a probing group.
+// Arbitrary order of elements in such tables is correct.
+inline bool is_single_group(size_t capacity) {
+  return capacity <= Group::kWidth;
+}
+
 // Begins a probing operation on `common.control`, using `hash`.
 inline probe_seq<Group::kWidth> probe(const ctrl_t* ctrl, const size_t capacity,
                                       size_t hash) {
@@ -1440,7 +1446,6 @@
               capacity + 1 + NumClonedBytes());
   ctrl[capacity] = ctrl_t::kSentinel;
   SanitizerPoisonMemoryRegion(common.slot_array(), slot_size * capacity);
-  ResetGrowthLeft(common);
 }
 
 // Sets `ctrl[i]` to `h`.
@@ -1475,41 +1480,263 @@
   return (std::max)(align_of_slot, alignof(size_t));
 }
 
-template <typename Alloc, size_t SizeOfSlot, size_t AlignOfSlot>
-ABSL_ATTRIBUTE_NOINLINE void InitializeSlots(CommonFields& c, Alloc alloc) {
-  assert(c.capacity());
-  // Folks with custom allocators often make unwarranted assumptions about the
-  // behavior of their classes vis-a-vis trivial destructability and what
-  // calls they will or won't make.  Avoid sampling for people with custom
-  // allocators to get us out of this mess.  This is not a hard guarantee but
-  // a workaround while we plan the exact guarantee we want to provide.
-  const size_t sample_size =
-      (std::is_same<Alloc, std::allocator<char>>::value &&
-       c.slot_array() == nullptr)
-          ? SizeOfSlot
-          : 0;
-  HashtablezInfoHandle infoz =
-      sample_size > 0 ? Sample(sample_size) : c.infoz();
-
-  const bool has_infoz = infoz.IsSampled();
-  const size_t cap = c.capacity();
-  const size_t alloc_size = AllocSize(cap, SizeOfSlot, AlignOfSlot, has_infoz);
-  char* mem = static_cast<char*>(
-      Allocate<BackingArrayAlignment(AlignOfSlot)>(&alloc, alloc_size));
-  const GenerationType old_generation = c.generation();
-  c.set_generation_ptr(reinterpret_cast<GenerationType*>(
-      mem + GenerationOffset(cap, has_infoz)));
-  c.set_generation(NextGeneration(old_generation));
-  c.set_control(reinterpret_cast<ctrl_t*>(mem + ControlOffset(has_infoz)));
-  c.set_slots(mem + SlotOffset(cap, AlignOfSlot, has_infoz));
-  ResetCtrl(c, SizeOfSlot);
-  c.set_has_infoz(has_infoz);
-  if (has_infoz) {
-    infoz.RecordStorageChanged(c.size(), cap);
-    c.set_infoz(infoz);
-  }
+// Returns the address of the ith slot in slots where each slot occupies
+// slot_size.
+inline void* SlotAddress(void* slot_array, size_t slot, size_t slot_size) {
+  return reinterpret_cast<void*>(reinterpret_cast<char*>(slot_array) +
+                                 (slot * slot_size));
 }
 
+// Helper class to perform resize of the hash set.
+//
+// It contains special optimizations for small group resizes.
+// See GrowIntoSingleGroupShuffleControlBytes for details.
+class HashSetResizeHelper {
+ public:
+  explicit HashSetResizeHelper(CommonFields& c)
+      : old_ctrl_(c.control()),
+        old_capacity_(c.capacity()),
+        had_infoz_(c.has_infoz()) {}
+
+  // Optimized for small groups version of `find_first_non_full` applicable
+  // only right after calling `raw_hash_set::resize`.
+  // It has implicit assumption that `resize` will call
+  // `GrowSizeIntoSingleGroup*` in case `IsGrowingIntoSingleGroupApplicable`.
+  // Falls back to `find_first_non_full` in case of big groups, so it is
+  // safe to use after `rehash_and_grow_if_necessary`.
+  static FindInfo FindFirstNonFullAfterResize(const CommonFields& c,
+                                              size_t old_capacity,
+                                              size_t hash) {
+    if (!IsGrowingIntoSingleGroupApplicable(old_capacity, c.capacity())) {
+      return find_first_non_full(c, hash);
+    }
+    // Find a location for the new element non-deterministically.
+    // Note that any position is correct.
+    // It will located at `half_old_capacity` or one of the other
+    // empty slots with approximately 50% probability each.
+    size_t offset = probe(c, hash).offset();
+
+    // Note that we intentionally use unsigned int underflow.
+    if (offset - (old_capacity + 1) >= old_capacity) {
+      // Offset fall on kSentinel or into the mostly occupied first half.
+      offset = old_capacity / 2;
+    }
+    assert(IsEmpty(c.control()[offset]));
+    return FindInfo{offset, 0};
+  }
+
+  ctrl_t* old_ctrl() const { return old_ctrl_; }
+  size_t old_capacity() const { return old_capacity_; }
+
+  // Allocates a backing array for the hashtable.
+  // Reads `capacity` and updates all other fields based on the result of
+  // the allocation.
+  //
+  // It also may do the folowing actions:
+  // 1. initialize control bytes
+  // 2. initialize slots
+  // 3. deallocate old slots.
+  //
+  // We are bundling a lot of functionality
+  // in one ABSL_ATTRIBUTE_NOINLINE function in order to minimize binary code
+  // duplication in raw_hash_set<>::resize.
+  //
+  // `c.capacity()` must be nonzero.
+  // POSTCONDITIONS:
+  //  1. CommonFields is initialized.
+  //
+  //  if IsGrowingIntoSingleGroupApplicable && TransferUsesMemcpy
+  //    Both control bytes and slots are fully initialized.
+  //    old_slots are deallocated.
+  //    infoz.RecordRehash is called.
+  //
+  //  if IsGrowingIntoSingleGroupApplicable && !TransferUsesMemcpy
+  //    Control bytes are fully initialized.
+  //    infoz.RecordRehash is called.
+  //    GrowSizeIntoSingleGroup must be called to finish slots initialization.
+  //
+  //  if !IsGrowingIntoSingleGroupApplicable
+  //    Control bytes are initialized to empty table via ResetCtrl.
+  //    raw_hash_set<>::resize must insert elements regularly.
+  //    infoz.RecordRehash is called if old_capacity == 0.
+  //
+  //  Returns IsGrowingIntoSingleGroupApplicable result to avoid recomputation.
+  template <typename Alloc, size_t SizeOfSlot, bool TransferUsesMemcpy,
+            size_t AlignOfSlot>
+  ABSL_ATTRIBUTE_NOINLINE bool InitializeSlots(CommonFields& c, void* old_slots,
+                                               Alloc alloc) {
+    assert(c.capacity());
+    // Folks with custom allocators often make unwarranted assumptions about the
+    // behavior of their classes vis-a-vis trivial destructability and what
+    // calls they will or won't make.  Avoid sampling for people with custom
+    // allocators to get us out of this mess.  This is not a hard guarantee but
+    // a workaround while we plan the exact guarantee we want to provide.
+    const size_t sample_size =
+        (std::is_same<Alloc, std::allocator<char>>::value &&
+         c.slot_array() == nullptr)
+            ? SizeOfSlot
+            : 0;
+    HashtablezInfoHandle infoz =
+        sample_size > 0 ? Sample(sample_size) : c.infoz();
+
+    const bool has_infoz = infoz.IsSampled();
+    const size_t cap = c.capacity();
+    const size_t alloc_size =
+        AllocSize(cap, SizeOfSlot, AlignOfSlot, has_infoz);
+    char* mem = static_cast<char*>(
+        Allocate<BackingArrayAlignment(AlignOfSlot)>(&alloc, alloc_size));
+    const GenerationType old_generation = c.generation();
+    c.set_generation_ptr(reinterpret_cast<GenerationType*>(
+        mem + GenerationOffset(cap, has_infoz)));
+    c.set_generation(NextGeneration(old_generation));
+    c.set_control(reinterpret_cast<ctrl_t*>(mem + ControlOffset(has_infoz)));
+    c.set_slots(mem + SlotOffset(cap, AlignOfSlot, has_infoz));
+    ResetGrowthLeft(c);
+
+    const bool grow_single_group =
+        IsGrowingIntoSingleGroupApplicable(old_capacity_, c.capacity());
+    if (old_capacity_ != 0 && grow_single_group) {
+      if (TransferUsesMemcpy) {
+        GrowSizeIntoSingleGroupTransferable(c, old_slots, SizeOfSlot);
+        DeallocateOld<AlignOfSlot>(alloc, SizeOfSlot, old_slots);
+      } else {
+        GrowIntoSingleGroupShuffleControlBytes(c.control(), c.capacity());
+      }
+    } else {
+      ResetCtrl(c, SizeOfSlot);
+    }
+
+    c.set_has_infoz(has_infoz);
+    if (has_infoz) {
+      infoz.RecordStorageChanged(c.size(), cap);
+      if (grow_single_group || old_capacity_ == 0) {
+        infoz.RecordRehash(0);
+      }
+      c.set_infoz(infoz);
+    }
+    return grow_single_group;
+  }
+
+  // Relocates slots into new single group consistent with
+  // GrowIntoSingleGroupShuffleControlBytes.
+  //
+  // PRECONDITIONS:
+  // 1. GrowIntoSingleGroupShuffleControlBytes was already called.
+  template <class PolicyTraits, class Alloc>
+  void GrowSizeIntoSingleGroup(CommonFields& c, Alloc& alloc_ref,
+                               typename PolicyTraits::slot_type* old_slots) {
+    assert(old_capacity_ < Group::kWidth / 2);
+    assert(IsGrowingIntoSingleGroupApplicable(old_capacity_, c.capacity()));
+    using slot_type = typename PolicyTraits::slot_type;
+    assert(is_single_group(c.capacity()));
+
+    auto* new_slots = reinterpret_cast<slot_type*>(c.slot_array());
+
+    size_t shuffle_bit = old_capacity_ / 2 + 1;
+    for (size_t i = 0; i < old_capacity_; ++i) {
+      if (IsFull(old_ctrl_[i])) {
+        size_t new_i = i ^ shuffle_bit;
+        SanitizerUnpoisonMemoryRegion(new_slots + new_i, sizeof(slot_type));
+        PolicyTraits::transfer(&alloc_ref, new_slots + new_i, old_slots + i);
+      }
+    }
+    PoisonSingleGroupEmptySlots(c, sizeof(slot_type));
+  }
+
+  // Deallocates old backing array.
+  template <size_t AlignOfSlot, class CharAlloc>
+  void DeallocateOld(CharAlloc alloc_ref, size_t slot_size, void* old_slots) {
+    SanitizerUnpoisonMemoryRegion(old_slots, slot_size * old_capacity_);
+    Deallocate<BackingArrayAlignment(AlignOfSlot)>(
+        &alloc_ref, old_ctrl_ - ControlOffset(had_infoz_),
+        AllocSize(old_capacity_, slot_size, AlignOfSlot, had_infoz_));
+  }
+
+ private:
+  // Returns true if `GrowSizeIntoSingleGroup` can be used for resizing.
+  static bool IsGrowingIntoSingleGroupApplicable(size_t old_capacity,
+                                                 size_t new_capacity) {
+    // NOTE that `old_capacity < new_capacity` in order to have
+    // `old_capacity < Group::kWidth / 2` to make faster copies of 8 bytes.
+    return is_single_group(new_capacity) && old_capacity < new_capacity;
+  }
+
+  // Relocates control bytes and slots into new single group for
+  // transferable objects.
+  // Must be called only if IsGrowingIntoSingleGroupApplicable returned true.
+  void GrowSizeIntoSingleGroupTransferable(CommonFields& c, void* old_slots,
+                                           size_t slot_size);
+
+  // Shuffle control bits deterministically to the next capacity.
+  // Returns offset for newly added element with given hash.
+  //
+  // PRECONDITIONs:
+  // 1. new_ctrl is allocated for new_capacity,
+  //    but not initialized.
+  // 2. new_capacity is a single group.
+  //
+  // All elements are transferred into the first `old_capacity + 1` positions
+  // of the new_ctrl. Elements are rotated by `old_capacity_ / 2 + 1` positions
+  // in order to change an order and keep it non deterministic.
+  // Although rotation itself deterministic, position of the new added element
+  // will be based on `H1` and is not deterministic.
+  //
+  // Examples:
+  // S = kSentinel, E = kEmpty
+  //
+  // old_ctrl = SEEEEEEEE...
+  // new_ctrl = ESEEEEEEE...
+  //
+  // old_ctrl = 0SEEEEEEE...
+  // new_ctrl = E0ESE0EEE...
+  //
+  // old_ctrl = 012S012EEEEEEEEE...
+  // new_ctrl = 2E01EEES2E01EEE...
+  //
+  // old_ctrl = 0123456S0123456EEEEEEEEEEE...
+  // new_ctrl = 456E0123EEEEEES456E0123EEE...
+  void GrowIntoSingleGroupShuffleControlBytes(ctrl_t* new_ctrl,
+                                              size_t new_capacity) const;
+
+  // Shuffle trivially transferable slots in the way consistent with
+  // GrowIntoSingleGroupShuffleControlBytes.
+  //
+  // PRECONDITIONs:
+  // 1. old_capacity must be non-zero.
+  // 2. new_ctrl is fully initialized using
+  //    GrowIntoSingleGroupShuffleControlBytes.
+  // 3. new_slots is allocated and *not* poisoned.
+  //
+  // POSTCONDITIONS:
+  // 1. new_slots are transferred from old_slots_ consistent with
+  //    GrowIntoSingleGroupShuffleControlBytes.
+  // 2. Empty new_slots are *not* poisoned.
+  void GrowIntoSingleGroupShuffleTransferableSlots(void* old_slots,
+                                                   void* new_slots,
+                                                   size_t slot_size) const;
+
+  // Poison empty slots that were transferred using the deterministic algorithm
+  // described above.
+  // PRECONDITIONs:
+  // 1. new_ctrl is fully initialized using
+  //    GrowIntoSingleGroupShuffleControlBytes.
+  // 2. new_slots is fully initialized consistent with
+  //    GrowIntoSingleGroupShuffleControlBytes.
+  void PoisonSingleGroupEmptySlots(CommonFields& c, size_t slot_size) const {
+    // poison non full items
+    for (size_t i = 0; i < c.capacity(); ++i) {
+      if (!IsFull(c.control()[i])) {
+        SanitizerPoisonMemoryRegion(SlotAddress(c.slot_array(), i, slot_size),
+                                    slot_size);
+      }
+    }
+  }
+
+  ctrl_t* old_ctrl_;
+  size_t old_capacity_;
+  bool had_infoz_;
+};
+
 // PolicyFunctions bundles together some information for a particular
 // raw_hash_set<T, ...> instantiation. This information is passed to
 // type-erased functions that want to do small amounts of type-specific
@@ -1627,6 +1854,11 @@
   using AllocTraits = absl::allocator_traits<allocator_type>;
   using SlotAlloc = typename absl::allocator_traits<
       allocator_type>::template rebind_alloc<slot_type>;
+  // People are often sloppy with the exact type of their allocator (sometimes
+  // it has an extra const or is missing the pair, but rebinds made it work
+  // anyway).
+  using CharAlloc =
+      typename absl::allocator_traits<Alloc>::template rebind_alloc<char>;
   using SlotAllocTraits = typename absl::allocator_traits<
       allocator_type>::template rebind_traits<slot_type>;
 
@@ -1819,8 +2051,7 @@
       const allocator_type& alloc = allocator_type())
       : settings_(CommonFields{}, hash, eq, alloc) {
     if (bucket_count) {
-      common().set_capacity(NormalizeCapacity(bucket_count));
-      initialize_slots();
+      resize(NormalizeCapacity(bucket_count));
     }
   }
 
@@ -2616,52 +2847,63 @@
     EraseMetaOnly(common(), it.control(), sizeof(slot_type));
   }
 
-  // Allocates a backing array for `self` and initializes its control bytes.
-  // This reads `capacity` and updates all other fields based on the result of
-  // the allocation.
+  // Resizes table to the new capacity and move all elements to the new
+  // positions accordingly.
   //
-  // This does not free the currently held array; `capacity` must be nonzero.
-  inline void initialize_slots() {
-    // People are often sloppy with the exact type of their allocator (sometimes
-    // it has an extra const or is missing the pair, but rebinds made it work
-    // anyway).
-    using CharAlloc =
-        typename absl::allocator_traits<Alloc>::template rebind_alloc<char>;
-    InitializeSlots<CharAlloc, sizeof(slot_type), alignof(slot_type)>(
-        common(), CharAlloc(alloc_ref()));
-  }
-
+  // Note that for better performance instead of
+  // find_first_non_full(common(), hash),
+  // HashSetResizeHelper::FindFirstNonFullAfterResize(
+  //    common(), old_capacity, hash)
+  // can be called right after `resize`.
   ABSL_ATTRIBUTE_NOINLINE void resize(size_t new_capacity) {
     assert(IsValidCapacity(new_capacity));
-    auto* old_ctrl = control();
+    HashSetResizeHelper resize_helper(common());
     auto* old_slots = slot_array();
-    const bool had_infoz = common().has_infoz();
-    const size_t old_capacity = common().capacity();
     common().set_capacity(new_capacity);
-    initialize_slots();
+    // Note that `InitializeSlots` does different number initialization steps
+    // depending on the values of `transfer_uses_memcpy` and capacities.
+    // Refer to the comment in `InitializeSlots` for more details.
+    const bool grow_single_group =
+        resize_helper.InitializeSlots<CharAlloc, sizeof(slot_type),
+                                      PolicyTraits::transfer_uses_memcpy(),
+                                      alignof(slot_type)>(
+            common(), const_cast<std::remove_const_t<slot_type>*>(old_slots),
+            CharAlloc(alloc_ref()));
 
-    auto* new_slots = slot_array();
-    size_t total_probe_length = 0;
-    for (size_t i = 0; i != old_capacity; ++i) {
-      if (IsFull(old_ctrl[i])) {
-        size_t hash = PolicyTraits::apply(HashElement{hash_ref()},
-                                          PolicyTraits::element(old_slots + i));
-        auto target = find_first_non_full(common(), hash);
-        size_t new_i = target.offset;
-        total_probe_length += target.probe_length;
-        SetCtrl(common(), new_i, H2(hash), sizeof(slot_type));
-        transfer(new_slots + new_i, old_slots + i);
+    if (resize_helper.old_capacity() == 0) {
+      // InitializeSlots did all the work including infoz().RecordRehash().
+      return;
+    }
+
+    if (grow_single_group) {
+      if (PolicyTraits::transfer_uses_memcpy()) {
+        // InitializeSlots did all the work.
+        return;
       }
+      // We want GrowSizeIntoSingleGroup to be called here in order to make
+      // InitializeSlots not depend on PolicyTraits.
+      resize_helper.GrowSizeIntoSingleGroup<PolicyTraits>(common(), alloc_ref(),
+                                                          old_slots);
+    } else {
+      // InitializeSlots prepares control bytes to correspond to empty table.
+      auto* new_slots = slot_array();
+      size_t total_probe_length = 0;
+      for (size_t i = 0; i != resize_helper.old_capacity(); ++i) {
+        if (IsFull(resize_helper.old_ctrl()[i])) {
+          size_t hash = PolicyTraits::apply(
+              HashElement{hash_ref()}, PolicyTraits::element(old_slots + i));
+          auto target = find_first_non_full(common(), hash);
+          size_t new_i = target.offset;
+          total_probe_length += target.probe_length;
+          SetCtrl(common(), new_i, H2(hash), sizeof(slot_type));
+          transfer(new_slots + new_i, old_slots + i);
+        }
+      }
+      infoz().RecordRehash(total_probe_length);
     }
-    if (old_capacity) {
-      SanitizerUnpoisonMemoryRegion(old_slots,
-                                    sizeof(slot_type) * old_capacity);
-      Deallocate<BackingArrayAlignment(alignof(slot_type))>(
-          &alloc_ref(), old_ctrl - ControlOffset(had_infoz),
-          AllocSize(old_capacity, sizeof(slot_type), alignof(slot_type),
-                    had_infoz));
-    }
-    infoz().RecordRehash(total_probe_length);
+    resize_helper.DeallocateOld<alignof(slot_type)>(
+        CharAlloc(alloc_ref()), sizeof(slot_type),
+        const_cast<std::remove_const_t<slot_type>*>(old_slots));
   }
 
   // Prunes control bytes to remove as many tombstones as possible.
@@ -2830,8 +3072,17 @@
     if (!rehash_for_bug_detection &&
         ABSL_PREDICT_FALSE(growth_left() == 0 &&
                            !IsDeleted(control()[target.offset]))) {
+      size_t old_capacity = capacity();
       rehash_and_grow_if_necessary();
-      target = find_first_non_full(common(), hash);
+      // NOTE: It is safe to use `FindFirstNonFullAfterResize`.
+      // `FindFirstNonFullAfterResize` must be called right after resize.
+      // `rehash_and_grow_if_necessary` may *not* call `resize`
+      // and perform `drop_deletes_without_resize` instead. But this
+      // could happen only on big tables.
+      // For big tables `FindFirstNonFullAfterResize` will always
+      // fallback to normal `find_first_non_full`, so it is safe to use it.
+      target = HashSetResizeHelper::FindFirstNonFullAfterResize(
+          common(), old_capacity, hash);
     }
     common().increment_size();
     set_growth_left(growth_left() - IsEmpty(control()[target.offset]));
diff --git a/absl/container/internal/raw_hash_set_test.cc b/absl/container/internal/raw_hash_set_test.cc
index 8577272..756b9df 100644
--- a/absl/container/internal/raw_hash_set_test.cc
+++ b/absl/container/internal/raw_hash_set_test.cc
@@ -30,6 +30,7 @@
 #include <ostream>
 #include <random>
 #include <string>
+#include <tuple>
 #include <type_traits>
 #include <unordered_map>
 #include <unordered_set>
@@ -299,7 +300,7 @@
   }
 }
 
-template <class T>
+template <class T, bool kTransferable = false>
 struct ValuePolicy {
   using slot_type = T;
   using key_type = T;
@@ -317,10 +318,11 @@
   }
 
   template <class Allocator>
-  static void transfer(Allocator* alloc, slot_type* new_slot,
-                       slot_type* old_slot) {
+  static std::integral_constant<bool, kTransferable> transfer(
+      Allocator* alloc, slot_type* new_slot, slot_type* old_slot) {
     construct(alloc, new_slot, std::move(*old_slot));
     destroy(alloc, old_slot);
+    return {};
   }
 
   static T& element(slot_type* slot) { return *slot; }
@@ -337,6 +339,8 @@
 using IntPolicy = ValuePolicy<int64_t>;
 using Uint8Policy = ValuePolicy<uint8_t>;
 
+using TranferableIntPolicy = ValuePolicy<int64_t, /*kTransferable=*/true>;
+
 class StringPolicy {
   template <class F, class K, class V,
             class = typename std::enable_if<
@@ -409,9 +413,10 @@
   using Base::Base;
 };
 
-template <typename T>
-struct ValueTable : raw_hash_set<ValuePolicy<T>, hash_default_hash<T>,
-                                 std::equal_to<T>, std::allocator<T>> {
+template <typename T, bool kTransferable = false>
+struct ValueTable
+    : raw_hash_set<ValuePolicy<T, kTransferable>, hash_default_hash<T>,
+                   std::equal_to<T>, std::allocator<T>> {
   using Base = typename ValueTable::raw_hash_set;
   using Base::Base;
 };
@@ -419,6 +424,8 @@
 using IntTable = ValueTable<int64_t>;
 using Uint8Table = ValueTable<uint8_t>;
 
+using TransferableIntTable = ValueTable<int64_t, /*kTransferable=*/true>;
+
 template <typename T>
 struct CustomAlloc : std::allocator<T> {
   CustomAlloc() = default;
@@ -653,6 +660,68 @@
   EXPECT_THAT(addr(0), original_addr_0);
 }
 
+template <class TableType>
+class SmallTableResizeTest : public testing::Test {};
+
+TYPED_TEST_SUITE_P(SmallTableResizeTest);
+
+TYPED_TEST_P(SmallTableResizeTest, InsertIntoSmallTable) {
+  TypeParam t;
+  for (int i = 0; i < 32; ++i) {
+    t.insert(i);
+    ASSERT_EQ(t.size(), i + 1);
+    for (int j = 0; j < i + 1; ++j) {
+      EXPECT_TRUE(t.find(j) != t.end());
+      EXPECT_EQ(*t.find(j), j);
+    }
+  }
+}
+
+TYPED_TEST_P(SmallTableResizeTest, ResizeGrowSmallTables) {
+  TypeParam t;
+  for (size_t source_size = 0; source_size < 32; ++source_size) {
+    for (size_t target_size = source_size; target_size < 32; ++target_size) {
+      for (bool rehash : {false, true}) {
+        for (size_t i = 0; i < source_size; ++i) {
+          t.insert(static_cast<int>(i));
+        }
+        if (rehash) {
+          t.rehash(target_size);
+        } else {
+          t.reserve(target_size);
+        }
+        for (size_t i = 0; i < source_size; ++i) {
+          EXPECT_TRUE(t.find(static_cast<int>(i)) != t.end());
+          EXPECT_EQ(*t.find(static_cast<int>(i)), static_cast<int>(i));
+        }
+      }
+    }
+  }
+}
+
+TYPED_TEST_P(SmallTableResizeTest, ResizeReduceSmallTables) {
+  TypeParam t;
+  for (size_t source_size = 0; source_size < 32; ++source_size) {
+    for (size_t target_size = 0; target_size <= source_size; ++target_size) {
+      size_t inserted_count = std::min<size_t>(source_size, 5);
+      for (size_t i = 0; i < inserted_count; ++i) {
+        t.insert(static_cast<int>(i));
+      }
+      t.rehash(target_size);
+      for (size_t i = 0; i < inserted_count; ++i) {
+        EXPECT_TRUE(t.find(static_cast<int>(i)) != t.end());
+        EXPECT_EQ(*t.find(static_cast<int>(i)), static_cast<int>(i));
+      }
+    }
+  }
+}
+
+REGISTER_TYPED_TEST_SUITE_P(SmallTableResizeTest, InsertIntoSmallTable,
+                            ResizeGrowSmallTables, ResizeReduceSmallTables);
+using SmallTableTypes = ::testing::Types<IntTable, TransferableIntTable>;
+INSTANTIATE_TYPED_TEST_SUITE_P(InstanceSmallTableResizeTest,
+                               SmallTableResizeTest, SmallTableTypes);
+
 TEST(Table, LazyEmplace) {
   StringTable t;
   bool called = false;
@@ -1071,7 +1140,7 @@
 TEST(Table, EraseMaintainsValidIterator) {
   IntTable t;
   const int kNumElements = 100;
-  for (int i = 0; i < kNumElements; i ++) {
+  for (int i = 0; i < kNumElements; i++) {
     EXPECT_TRUE(t.emplace(i).second);
   }
   EXPECT_EQ(t.size(), kNumElements);
@@ -2258,21 +2327,34 @@
 }
 
 #ifdef ABSL_HAVE_ADDRESS_SANITIZER
-TEST(Sanitizer, PoisoningUnused) {
-  IntTable t;
-  t.reserve(5);
-  // Insert something to force an allocation.
-  int64_t& v1 = *t.insert(0).first;
+template <class TableType>
+class SanitizerTest : public testing::Test {};
 
-  // Make sure there is something to test.
-  ASSERT_GT(t.capacity(), 1);
+TYPED_TEST_SUITE_P(SanitizerTest);
 
-  int64_t* slots = RawHashSetTestOnlyAccess::GetSlots(t);
-  for (size_t i = 0; i < t.capacity(); ++i) {
-    EXPECT_EQ(slots + i != &v1, __asan_address_is_poisoned(slots + i));
+TYPED_TEST_P(SanitizerTest, PoisoningUnused) {
+  TypeParam t;
+  for (size_t reserve_size = 2; reserve_size < 1024;
+       reserve_size = reserve_size * 3 / 2) {
+    t.reserve(reserve_size);
+    // Insert something to force an allocation.
+    int64_t& v = *t.insert(0).first;
+
+    // Make sure there is something to test.
+    ASSERT_GT(t.capacity(), 1);
+
+    int64_t* slots = RawHashSetTestOnlyAccess::GetSlots(t);
+    for (size_t i = 0; i < t.capacity(); ++i) {
+      EXPECT_EQ(slots + i != &v, __asan_address_is_poisoned(slots + i)) << i;
+    }
   }
 }
 
+REGISTER_TYPED_TEST_SUITE_P(SanitizerTest, PoisoningUnused);
+using SanitizerTableTypes = ::testing::Types<IntTable, TransferableIntTable>;
+INSTANTIATE_TYPED_TEST_SUITE_P(InstanceSanitizerTest, SanitizerTest,
+                               SanitizerTableTypes);
+
 TEST(Sanitizer, PoisoningOnErase) {
   IntTable t;
   int64_t& v = *t.insert(0).first;
diff --git a/absl/hash/hash_benchmark.cc b/absl/hash/hash_benchmark.cc
index 916fb62..d18ea69 100644
--- a/absl/hash/hash_benchmark.cc
+++ b/absl/hash/hash_benchmark.cc
@@ -85,12 +85,6 @@
   }
 };
 
-template <typename FuncType>
-inline FuncType* ODRUseFunction(FuncType* ptr) {
-  volatile FuncType* dummy = ptr;
-  return dummy;
-}
-
 absl::Cord FlatCord(size_t size) {
   absl::Cord result(std::string(size, 'a'));
   result.Flatten();
@@ -166,7 +160,7 @@
     return hash<decltype(__VA_ARGS__)>{}(arg);                   \
   }                                                              \
   bool absl_hash_test_odr_use##hash##name =                      \
-      ODRUseFunction(&Codegen##hash##name);
+      (benchmark::DoNotOptimize(&Codegen##hash##name), false);
 
 MAKE_BENCHMARK(AbslHash, Int32, int32_t{});
 MAKE_BENCHMARK(AbslHash, Int64, int64_t{});
diff --git a/absl/log/BUILD.bazel b/absl/log/BUILD.bazel
index 40c1bfb..af23bb8 100644
--- a/absl/log/BUILD.bazel
+++ b/absl/log/BUILD.bazel
@@ -98,6 +98,7 @@
         "//absl/flags:marshalling",
         "//absl/log/internal:config",
         "//absl/log/internal:flags",
+        "//absl/log/internal:vlog_config",
         "//absl/strings",
     ],
     # Binaries which do not access these flags from C++ still want this library linked in.
@@ -143,6 +144,7 @@
     copts = ABSL_DEFAULT_COPTS,
     linkopts = ABSL_DEFAULT_LINKOPTS,
     deps = [
+        ":vlog_is_on",
         "//absl/log/internal:log_impl",
     ],
 )
@@ -235,6 +237,57 @@
     ],
 )
 
+cc_library(
+    name = "absl_vlog_is_on",
+    hdrs = ["absl_vlog_is_on.h"],
+    copts = ABSL_DEFAULT_COPTS,
+    linkopts = ABSL_DEFAULT_LINKOPTS,
+    visibility = [
+        "//absl/log:__subpackages__",
+    ],
+    deps = [
+        "//absl/base:config",
+        "//absl/base:core_headers",
+        "//absl/log/internal:vlog_config",
+        "//absl/strings",
+    ],
+)
+
+cc_library(
+    name = "vlog_is_on",
+    hdrs = ["vlog_is_on.h"],
+    copts = ABSL_DEFAULT_COPTS,
+    linkopts = ABSL_DEFAULT_LINKOPTS,
+    visibility = [
+        "//absl/log:__subpackages__",
+    ],
+    deps = [
+        ":absl_vlog_is_on",
+    ],
+)
+
+# TODO(b/200695798): run this in TAP projects with -DABSL_MAX_VLOG_VERBOSITY={-100,100}
+cc_test(
+    name = "vlog_is_on_test",
+    size = "small",
+    srcs = [
+        "vlog_is_on_test.cc",
+    ],
+    copts = ABSL_TEST_COPTS,
+    linkopts = ABSL_DEFAULT_LINKOPTS,
+    deps = [
+        ":flags",
+        ":log",
+        ":scoped_mock_log",
+        ":vlog_is_on",
+        "//absl/base:log_severity",
+        "//absl/flags:flag",
+        "//absl/types:optional",
+        "@com_google_googletest//:gtest",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
+
 # Test targets
 
 cc_test(
@@ -571,6 +624,7 @@
         "//absl/base:strerror",
         "//absl/flags:program_name",
         "//absl/log/internal:test_helpers",
+        "//absl/status",
         "//absl/strings",
         "//absl/strings:str_format",
         "@com_google_googletest//:gtest",
diff --git a/absl/log/CMakeLists.txt b/absl/log/CMakeLists.txt
index aa00886..366d7be 100644
--- a/absl/log/CMakeLists.txt
+++ b/absl/log/CMakeLists.txt
@@ -157,6 +157,7 @@
     absl::log_internal_conditions
     absl::log_internal_message
     absl::log_internal_strip
+    absl::absl_vlog_is_on
 )
 
 absl_cc_library(
@@ -481,6 +482,7 @@
     absl::flags
     absl::flags_marshalling
     absl::strings
+    absl::vlog_config_internal
   PUBLIC
 )
 
@@ -536,6 +538,7 @@
     ${ABSL_DEFAULT_LINKOPTS}
   DEPS
     absl::log_internal_log_impl
+    absl::vlog_is_on
   PUBLIC
 )
 
@@ -673,19 +676,91 @@
 )
 
 absl_cc_library(
-    NAME
-      log_internal_fnmatch
-    SRCS
-      "internal/fnmatch.cc"
-    HDRS
-      "internal/fnmatch.h"
-    COPTS
-      ${ABSL_DEFAULT_COPTS}
-    LINKOPTS
-      ${ABSL_DEFAULT_LINKOPTS}
-    DEPS
-      absl::config
-      absl::strings
+  NAME
+    vlog_config_internal
+  SRCS
+    "internal/vlog_config.cc"
+  HDRS
+    "internal/vlog_config.h"
+  COPTS
+    ${ABSL_DEFAULT_COPTS}
+  LINKOPTS
+    ${ABSL_DEFAULT_LINKOPTS}
+  DEPS
+    absl::base
+    absl::config
+    absl::core_headers
+    absl::log_internal_fnmatch
+    absl::memory
+    absl::no_destructor
+    absl::strings
+    absl::synchronization
+    absl::optional
+)
+
+absl_cc_library(
+  NAME
+    absl_vlog_is_on
+  COPTS
+    ${ABSL_DEFAULT_COPTS}
+  LINKOPTS
+    ${ABSL_DEFAULT_LINKOPTS}
+  HDRS
+    "absl_vlog_is_on.h"
+  DEPS
+    absl::vlog_config_internal
+    absl::config
+    absl::core_headers
+    absl::strings
+)
+
+absl_cc_library(
+  NAME
+    vlog_is_on
+  COPTS
+    ${ABSL_DEFAULT_COPTS}
+  LINKOPTS
+    ${ABSL_DEFAULT_LINKOPTS}
+  HDRS
+    "vlog_is_on.h"
+  DEPS
+    absl::absl_vlog_is_on
+)
+
+absl_cc_test(
+  NAME
+    vlog_is_on_test
+  SRCS
+    "vlog_is_on_test.cc"
+  COPTS
+    ${ABSL_TEST_COPTS}
+  LINKOPTS
+    ${ABSL_DEFAULT_LINKOPTS}
+  DEPS
+    absl::log_flags
+    absl::log
+    absl::scoped_mock_log
+    absl::vlog_is_on
+    absl::log_severity
+    absl::flags
+    absl::optional
+    GTest::gmock_main
+)
+
+absl_cc_library(
+  NAME
+    log_internal_fnmatch
+  SRCS
+    "internal/fnmatch.cc"
+  HDRS
+    "internal/fnmatch.h"
+  COPTS
+    ${ABSL_DEFAULT_COPTS}
+  LINKOPTS
+    ${ABSL_DEFAULT_LINKOPTS}
+  DEPS
+    absl::config
+    absl::strings
 )
 
 # Test targets
@@ -1021,6 +1096,7 @@
     absl::log
     absl::log_internal_test_helpers
     absl::log_severity
+    absl::status
     absl::strerror
     absl::strings
     absl::str_format
diff --git a/absl/log/absl_log.h b/absl/log/absl_log.h
index 0517760..0fd9ae3 100644
--- a/absl/log/absl_log.h
+++ b/absl/log/absl_log.h
@@ -39,6 +39,9 @@
 #define ABSL_PLOG(severity) ABSL_LOG_INTERNAL_PLOG_IMPL(_##severity)
 #define ABSL_DLOG(severity) ABSL_LOG_INTERNAL_DLOG_IMPL(_##severity)
 
+#define ABSL_VLOG(verbose_level) ABSL_LOG_INTERNAL_VLOG_IMPL(verbose_level)
+#define ABSL_DVLOG(verbose_level) ABSL_LOG_INTERNAL_DVLOG_IMPL(verbose_level)
+
 #define ABSL_LOG_IF(severity, condition) \
   ABSL_LOG_INTERNAL_LOG_IF_IMPL(_##severity, condition)
 #define ABSL_PLOG_IF(severity, condition) \
@@ -73,6 +76,15 @@
 #define ABSL_DLOG_EVERY_N_SEC(severity, n_seconds) \
   ABSL_LOG_INTERNAL_DLOG_EVERY_N_SEC_IMPL(_##severity, n_seconds)
 
+#define ABSL_VLOG_EVERY_N(verbose_level, n) \
+  ABSL_LOG_INTERNAL_VLOG_EVERY_N_IMPL(verbose_level, n)
+#define ABSL_VLOG_FIRST_N(verbose_level, n) \
+  ABSL_LOG_INTERNAL_VLOG_FIRST_N_IMPL(verbose_level, n)
+#define ABSL_VLOG_EVERY_POW_2(verbose_level, n) \
+  ABSL_LOG_INTERNAL_VLOG_EVERY_POW_2_IMPL(verbose_level, n)
+#define ABSL_VLOG_EVERY_N_SEC(verbose_level, n) \
+  ABSL_LOG_INTERNAL_VLOG_EVERY_N_SEC_IMPL(verbose_level, n)
+
 #define ABSL_LOG_IF_EVERY_N(severity, condition, n) \
   ABSL_LOG_INTERNAL_LOG_IF_EVERY_N_IMPL(_##severity, condition, n)
 #define ABSL_LOG_IF_FIRST_N(severity, condition, n) \
diff --git a/absl/log/absl_vlog_is_on.h b/absl/log/absl_vlog_is_on.h
new file mode 100644
index 0000000..d1df2c0
--- /dev/null
+++ b/absl/log/absl_vlog_is_on.h
@@ -0,0 +1,115 @@
+// Copyright 2022 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.
+//
+// -----------------------------------------------------------------------------
+// File: log/absl_vlog_is_on.h
+// -----------------------------------------------------------------------------
+//
+// This header defines the `ABSL_VLOG_IS_ON()` macro that controls the
+// variable-verbosity conditional logging.
+//
+// It's used by `VLOG` in log.h, or it can also be used directly like this:
+//
+//   if (ABSL_VLOG_IS_ON(2)) {
+//     foo_server.RecomputeStatisticsExpensive();
+//     LOG(INFO) << foo_server.LastStatisticsAsString();
+//   }
+//
+// Each source file has an effective verbosity level that's a non-negative
+// integer computed from the `--vmodule` and `--v` flags.
+// `ABSL_VLOG_IS_ON(n)` is true, and `VLOG(n)` logs, if that effective verbosity
+// level is greater than or equal to `n`.
+//
+// `--vmodule` takes a comma-delimited list of key=value pairs.  Each key is a
+// pattern matched against filenames, and the values give the effective severity
+// level applied to matching files.  '?' and '*' characters in patterns are
+// interpreted as single-character and zero-or-more-character wildcards.
+// Patterns including a slash character are matched against full pathnames,
+// while those without are matched against basenames only.  One suffix (i.e. the
+// last . and everything after it) is stripped from each filename prior to
+// matching, as is the special suffix "-inl".
+//
+// Files are matched against globs in `--vmodule` in order, and the first match
+// determines the verbosity level.
+//
+// Files which do not match any pattern in `--vmodule` use the value of `--v` as
+// their effective verbosity level.  The default is 0.
+//
+// SetVLOGLevel helper function is provided to do limited dynamic control over
+// V-logging by appending to `--vmodule`. Because these go at the beginning of
+// the list, they take priority over any globs previously added.
+//
+// Resetting --vmodule will override all previous modifications to `--vmodule`,
+// including via SetVLOGLevel.
+
+#ifndef ABSL_LOG_ABSL_VLOG_IS_ON_H_
+#define ABSL_LOG_ABSL_VLOG_IS_ON_H_
+
+#include "absl/base/attributes.h"
+#include "absl/base/config.h"
+#include "absl/log/internal/vlog_config.h"  // IWYU pragma: export
+#include "absl/strings/string_view.h"
+
+// IWYU pragma: private, include "absl/log/log.h"
+
+// This is expanded at the callsite to allow the compiler to optimize
+// always-false cases out of the build.
+// An ABSL_MAX_VLOG_VERBOSITY of 2 means that VLOG(3) and above should never
+// log.
+#ifdef ABSL_MAX_VLOG_VERBOSITY
+#define ABSL_LOG_INTERNAL_MAX_LOG_VERBOSITY_CHECK(x) \
+  ((x) <= ABSL_MAX_VLOG_VERBOSITY)&&
+#else
+#define ABSL_LOG_INTERNAL_MAX_LOG_VERBOSITY_CHECK(x)
+#endif
+
+// Each ABSL_VLOG_IS_ON call site gets its own VLogSite that registers with the
+// global linked list of sites to asynchronously update its verbosity level on
+// changes to --v or --vmodule. The verbosity can also be set by manually
+// calling SetVLOGLevel.
+//
+// ABSL_VLOG_IS_ON is not async signal safe, but it is guaranteed not to
+// allocate new memory.
+#define ABSL_VLOG_IS_ON(verbose_level)                                     \
+  (ABSL_LOG_INTERNAL_MAX_LOG_VERBOSITY_CHECK(verbose_level)[]()            \
+       ->::absl::log_internal::VLogSite *                                  \
+   {                                                                       \
+     ABSL_CONST_INIT static ::absl::log_internal::VLogSite site(__FILE__); \
+     return &site;                                                         \
+   }()                                                                     \
+       ->IsEnabled(verbose_level))
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+
+// Sets the global `(ABSL_)VLOG(_IS_ON)` level to `log_level`.  This level is
+// applied to any sites whose filename doesn't match any `module_pattern`.
+// Returns the prior value.
+inline int SetGlobalVLogLevel(int log_level) {
+  return absl::log_internal::UpdateGlobalVLogLevel(log_level);
+}
+
+// Sets `(ABSL_)VLOG(_IS_ON)` level for `module_pattern` to `log_level`.
+// This lets us dynamically control what is normally set by the --vmodule flag.
+// Returns the level that previously applied to module_pattern.
+// Calling this with `log_level` of kUseFlag will have all sites for that
+// pattern use the value of --v.
+inline int SetVLogLevel(absl::string_view module_pattern, int log_level) {
+  return absl::log_internal::PrependVModule(module_pattern, log_level);
+}
+
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif  // ABSL_LOG_ABSL_VLOG_IS_ON_H_
diff --git a/absl/log/flags.cc b/absl/log/flags.cc
index 215b7bd..287b3e9 100644
--- a/absl/log/flags.cc
+++ b/absl/log/flags.cc
@@ -28,6 +28,7 @@
 #include "absl/flags/marshalling.h"
 #include "absl/log/globals.h"
 #include "absl/log/internal/config.h"
+#include "absl/log/internal/vlog_config.h"
 #include "absl/strings/numbers.h"
 #include "absl/strings/string_view.h"
 
@@ -118,3 +119,25 @@
     .OnUpdate([] {
       absl::log_internal::RawEnableLogPrefix(absl::GetFlag(FLAGS_log_prefix));
     });
+
+ABSL_FLAG(int, v, 0,
+          "Show all VLOG(m) messages for m <= this. Overridable by --vmodule.")
+    .OnUpdate([] {
+      absl::log_internal::UpdateGlobalVLogLevel(absl::GetFlag(FLAGS_v));
+    });
+
+ABSL_FLAG(
+    std::string, vmodule, "",
+    "per-module log verbosity level."
+    " Argument is a comma-separated list of <module name>=<log level>."
+    " <module name> is a glob pattern, matched against the filename base"
+    " (that is, name ignoring .cc/.h./-inl.h)."
+    " A pattern without slashes matches just the file name portion, otherwise"
+    " the whole file path below the workspace root"
+    " (still without .cc/.h./-inl.h) is matched."
+    " ? and * in the glob pattern match any single or sequence of characters"
+    " respectively including slashes."
+    " <log level> overrides any value given by --v.")
+    .OnUpdate([] {
+      absl::log_internal::UpdateVModule(absl::GetFlag(FLAGS_vmodule));
+    });
diff --git a/absl/log/internal/BUILD.bazel b/absl/log/internal/BUILD.bazel
index 2030a3d..1be1349 100644
--- a/absl/log/internal/BUILD.bazel
+++ b/absl/log/internal/BUILD.bazel
@@ -153,6 +153,7 @@
         ":conditions",
         ":log_message",
         ":strip",
+        "//absl/log:absl_vlog_is_on",
     ],
 )
 
@@ -377,6 +378,48 @@
     ],
 )
 
+cc_library(
+    name = "vlog_config",
+    srcs = ["vlog_config.cc"],
+    hdrs = ["vlog_config.h"],
+    copts = ABSL_DEFAULT_COPTS,
+    linkopts = ABSL_DEFAULT_LINKOPTS,
+    visibility = ["//absl/log:__subpackages__"],
+    deps = [
+        "//absl/base",
+        "//absl/base:config",
+        "//absl/base:core_headers",
+        "//absl/base:no_destructor",
+        "//absl/log/internal:fnmatch",
+        "//absl/memory",
+        "//absl/strings",
+        "//absl/synchronization",
+        "//absl/types:optional",
+    ],
+)
+
+cc_binary(
+    name = "vlog_config_benchmark",
+    testonly = 1,
+    srcs = ["vlog_config_benchmark.cc"],
+    copts = ABSL_TEST_COPTS,
+    linkopts = ABSL_DEFAULT_LINKOPTS,
+    tags = [
+        "benchmark",
+    ],
+    visibility = ["//visibility:private"],
+    deps = [
+        ":vlog_config",
+        "//absl/base:config",
+        "//absl/base:core_headers",
+        "//absl/container:layout",
+        "//absl/memory",
+        "//absl/random:distributions",
+        "//absl/strings",
+        "@com_github_google_benchmark//:benchmark_main",
+    ],
+)
+
 # Test targets
 cc_test(
     name = "stderr_log_sink_test",
diff --git a/absl/log/internal/check_op.h b/absl/log/internal/check_op.h
index 20b01b5..11f0f40 100644
--- a/absl/log/internal/check_op.h
+++ b/absl/log/internal/check_op.h
@@ -65,6 +65,7 @@
               ::absl::log_internal::GetReferenceableValue(val2),               \
               ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL(val1_text                 \
                                                      " " #op " " val2_text)))  \
+    ABSL_LOG_INTERNAL_CONDITION_FATAL(STATELESS, true)                         \
   ABSL_LOG_INTERNAL_CHECK(*absl_log_internal_check_op_result).InternalStream()
 #define ABSL_LOG_INTERNAL_QCHECK_OP(name, op, val1, val1_text, val2, \
                                     val2_text)                       \
@@ -74,6 +75,7 @@
                  ::absl::log_internal::GetReferenceableValue(val2),  \
                  ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL(             \
                      val1_text " " #op " " val2_text)))              \
+    ABSL_LOG_INTERNAL_CONDITION_QFATAL(STATELESS, true)              \
   ABSL_LOG_INTERNAL_QCHECK(*absl_log_internal_qcheck_op_result).InternalStream()
 #define ABSL_LOG_INTERNAL_CHECK_STROP(func, op, expected, s1, s1_text, s2,     \
                                       s2_text)                                 \
@@ -82,6 +84,7 @@
                  (s1), (s2),                                                   \
                  ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL(s1_text " " #op        \
                                                                 " " s2_text))) \
+    ABSL_LOG_INTERNAL_CONDITION_FATAL(STATELESS, true)                         \
   ABSL_LOG_INTERNAL_CHECK(*absl_log_internal_check_strop_result)               \
       .InternalStream()
 #define ABSL_LOG_INTERNAL_QCHECK_STROP(func, op, expected, s1, s1_text, s2,    \
@@ -91,6 +94,7 @@
                  (s1), (s2),                                                   \
                  ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL(s1_text " " #op        \
                                                                 " " s2_text))) \
+    ABSL_LOG_INTERNAL_CONDITION_QFATAL(STATELESS, true)                        \
   ABSL_LOG_INTERNAL_QCHECK(*absl_log_internal_qcheck_strop_result)             \
       .InternalStream()
 // This one is tricky:
@@ -113,6 +117,10 @@
 // * As usual, no braces so we can stream into the expansion with `operator<<`.
 // * Also as usual, it must expand to a single (partial) statement with no
 //   ambiguous-else problems.
+// * When stripped by `ABSL_MIN_LOG_LEVEL`, we must discard the `<expr> is OK`
+//   string literal and abort without doing any streaming.  We don't need to
+//   strip the call to stringify the non-ok `Status` as long as we don't log it;
+//   dropping the `Status`'s message text is out of scope.
 #define ABSL_LOG_INTERNAL_CHECK_OK(val, val_text)                        \
   for (::std::pair<const ::absl::Status*, ::std::string*>                \
            absl_log_internal_check_ok_goo;                               \
@@ -126,22 +134,24 @@
                      ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL(val_text     \
                                                             " is OK")),  \
        !ABSL_PREDICT_TRUE(absl_log_internal_check_ok_goo.first->ok());)  \
+    ABSL_LOG_INTERNAL_CONDITION_FATAL(STATELESS, true)                   \
   ABSL_LOG_INTERNAL_CHECK(*absl_log_internal_check_ok_goo.second)        \
       .InternalStream()
-#define ABSL_LOG_INTERNAL_QCHECK_OK(val, val_text)                       \
-  for (::std::pair<const ::absl::Status*, ::std::string*>                \
-           absl_log_internal_check_ok_goo;                               \
-       absl_log_internal_check_ok_goo.first =                            \
-           ::absl::log_internal::AsStatus(val),                          \
-       absl_log_internal_check_ok_goo.second =                           \
-           ABSL_PREDICT_TRUE(absl_log_internal_check_ok_goo.first->ok()) \
-               ? nullptr                                                 \
-               : ::absl::status_internal::MakeCheckFailString(           \
-                     absl_log_internal_check_ok_goo.first,               \
-                     ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL(val_text     \
-                                                            " is OK")),  \
-       !ABSL_PREDICT_TRUE(absl_log_internal_check_ok_goo.first->ok());)  \
-  ABSL_LOG_INTERNAL_QCHECK(*absl_log_internal_check_ok_goo.second)       \
+#define ABSL_LOG_INTERNAL_QCHECK_OK(val, val_text)                        \
+  for (::std::pair<const ::absl::Status*, ::std::string*>                 \
+           absl_log_internal_qcheck_ok_goo;                               \
+       absl_log_internal_qcheck_ok_goo.first =                            \
+           ::absl::log_internal::AsStatus(val),                           \
+       absl_log_internal_qcheck_ok_goo.second =                           \
+           ABSL_PREDICT_TRUE(absl_log_internal_qcheck_ok_goo.first->ok()) \
+               ? nullptr                                                  \
+               : ::absl::status_internal::MakeCheckFailString(            \
+                     absl_log_internal_qcheck_ok_goo.first,               \
+                     ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL(val_text      \
+                                                            " is OK")),   \
+       !ABSL_PREDICT_TRUE(absl_log_internal_qcheck_ok_goo.first->ok());)  \
+    ABSL_LOG_INTERNAL_CONDITION_QFATAL(STATELESS, true)                   \
+  ABSL_LOG_INTERNAL_QCHECK(*absl_log_internal_qcheck_ok_goo.second)       \
       .InternalStream()
 
 namespace absl {
@@ -152,8 +162,8 @@
 class StatusOr;
 
 namespace status_internal {
-std::string* MakeCheckFailString(const absl::Status* status,
-                                 const char* prefix);
+ABSL_ATTRIBUTE_PURE_FUNCTION std::string* MakeCheckFailString(
+    const absl::Status* status, const char* prefix);
 }  // namespace status_internal
 
 namespace log_internal {
@@ -314,6 +324,20 @@
 ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(const void*);
 #undef ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN
 
+// `ABSL_LOG_INTERNAL_CHECK_OP_IMPL_RESULT` skips formatting the Check_OP result
+// string iff `ABSL_MIN_LOG_LEVEL` exceeds `kFatal`, instead returning an empty
+// string.
+#ifdef ABSL_MIN_LOG_LEVEL
+#define ABSL_LOG_INTERNAL_CHECK_OP_IMPL_RESULT(U1, U2, v1, v2, exprtext) \
+  ((::absl::LogSeverity::kFatal >=                                       \
+    static_cast<::absl::LogSeverity>(ABSL_MIN_LOG_LEVEL))                \
+       ? MakeCheckOpString<U1, U2>(v1, v2, exprtext)                     \
+       : new std::string())
+#else
+#define ABSL_LOG_INTERNAL_CHECK_OP_IMPL_RESULT(U1, U2, v1, v2, exprtext) \
+  MakeCheckOpString<U1, U2>(v1, v2, exprtext)
+#endif
+
 // Helper functions for `ABSL_LOG_INTERNAL_CHECK_OP` macro family.  The
 // `(int, int)` override works around the issue that the compiler will not
 // instantiate the template version of the function on values of unnamed enum
@@ -326,7 +350,8 @@
     using U2 = CheckOpStreamType<T2>;                                    \
     return ABSL_PREDICT_TRUE(v1 op v2)                                   \
                ? nullptr                                                 \
-               : MakeCheckOpString<U1, U2>(v1, v2, exprtext);            \
+               : ABSL_LOG_INTERNAL_CHECK_OP_IMPL_RESULT(U1, U2, v1, v2,  \
+                                                        exprtext);       \
   }                                                                      \
   inline constexpr ::std::string* name##Impl(int v1, int v2,             \
                                              const char* exprtext) {     \
@@ -339,6 +364,7 @@
 ABSL_LOG_INTERNAL_CHECK_OP_IMPL(Check_LT, <)
 ABSL_LOG_INTERNAL_CHECK_OP_IMPL(Check_GE, >=)
 ABSL_LOG_INTERNAL_CHECK_OP_IMPL(Check_GT, >)
+#undef ABSL_LOG_INTERNAL_CHECK_OP_IMPL_RESULT
 #undef ABSL_LOG_INTERNAL_CHECK_OP_IMPL
 
 std::string* CheckstrcmptrueImpl(const char* s1, const char* s2,
diff --git a/absl/log/internal/conditions.h b/absl/log/internal/conditions.h
index 41f6721..45a17b0 100644
--- a/absl/log/internal/conditions.h
+++ b/absl/log/internal/conditions.h
@@ -68,7 +68,7 @@
     !(condition) ? (void)0 : ::absl::log_internal::Voidify()&&
 
 // `ABSL_LOG_INTERNAL_STATEFUL_CONDITION` applies a condition like
-// `ABSL_LOG_INTERNAL_CONDITION` but adds to that a series of variable
+// `ABSL_LOG_INTERNAL_STATELESS_CONDITION` but adds to that a series of variable
 // declarations, including a local static object which stores the state needed
 // to implement the stateful macros like `LOG_EVERY_N`.
 //
@@ -147,20 +147,20 @@
             (::absl::kLogDebugFatal == ::absl::LogSeverity::kFatal &&   \
              (::absl::log_internal::AbortQuietly(), false)))))
 
-#define ABSL_LOG_INTERNAL_CONDITION_LEVEL(severity)                    \
-  for (int log_internal_severity_loop = 1; log_internal_severity_loop; \
-       log_internal_severity_loop = 0)                                 \
-    for (const absl::LogSeverity log_internal_severity =               \
-             ::absl::NormalizeLogSeverity(severity);                   \
-         log_internal_severity_loop; log_internal_severity_loop = 0)   \
+#define ABSL_LOG_INTERNAL_CONDITION_LEVEL(severity)                            \
+  for (int absl_log_internal_severity_loop = 1;                                \
+       absl_log_internal_severity_loop; absl_log_internal_severity_loop = 0)   \
+    for (const absl::LogSeverity absl_log_internal_severity =                  \
+             ::absl::NormalizeLogSeverity(severity);                           \
+         absl_log_internal_severity_loop; absl_log_internal_severity_loop = 0) \
   ABSL_LOG_INTERNAL_CONDITION_LEVEL_IMPL
-#define ABSL_LOG_INTERNAL_CONDITION_LEVEL_IMPL(type, condition)    \
-  ABSL_LOG_INTERNAL_##type##_CONDITION(                            \
-      (condition) &&                                               \
-      (log_internal_severity >=                                    \
-           static_cast<::absl::LogSeverity>(ABSL_MIN_LOG_LEVEL) || \
-       (log_internal_severity == ::absl::LogSeverity::kFatal &&    \
-        (::absl::log_internal::AbortQuietly(), false))))
+#define ABSL_LOG_INTERNAL_CONDITION_LEVEL_IMPL(type, condition)          \
+  ABSL_LOG_INTERNAL_##type##_CONDITION((                                  \
+      (condition) &&                                                     \
+          (absl_log_internal_severity >=                                 \
+               static_cast<::absl::LogSeverity>(ABSL_MIN_LOG_LEVEL) ||   \
+           (absl_log_internal_severity == ::absl::LogSeverity::kFatal && \
+            (::absl::log_internal::AbortQuietly(), false)))))
 #else  // ndef ABSL_MIN_LOG_LEVEL
 #define ABSL_LOG_INTERNAL_CONDITION_INFO(type, condition) \
   ABSL_LOG_INTERNAL_##type##_CONDITION(condition)
@@ -174,12 +174,12 @@
   ABSL_LOG_INTERNAL_##type##_CONDITION(condition)
 #define ABSL_LOG_INTERNAL_CONDITION_DFATAL(type, condition) \
   ABSL_LOG_INTERNAL_##type##_CONDITION(condition)
-#define ABSL_LOG_INTERNAL_CONDITION_LEVEL(severity)                    \
-  for (int log_internal_severity_loop = 1; log_internal_severity_loop; \
-       log_internal_severity_loop = 0)                                 \
-    for (const absl::LogSeverity log_internal_severity =               \
-             ::absl::NormalizeLogSeverity(severity);                   \
-         log_internal_severity_loop; log_internal_severity_loop = 0)   \
+#define ABSL_LOG_INTERNAL_CONDITION_LEVEL(severity)                            \
+  for (int absl_log_internal_severity_loop = 1;                                \
+       absl_log_internal_severity_loop; absl_log_internal_severity_loop = 0)   \
+    for (const absl::LogSeverity absl_log_internal_severity =                  \
+             ::absl::NormalizeLogSeverity(severity);                           \
+         absl_log_internal_severity_loop; absl_log_internal_severity_loop = 0) \
   ABSL_LOG_INTERNAL_CONDITION_LEVEL_IMPL
 #define ABSL_LOG_INTERNAL_CONDITION_LEVEL_IMPL(type, condition) \
   ABSL_LOG_INTERNAL_##type##_CONDITION(condition)
diff --git a/absl/log/internal/flags.h b/absl/log/internal/flags.h
index b91b864..c453978 100644
--- a/absl/log/internal/flags.h
+++ b/absl/log/internal/flags.h
@@ -50,4 +50,10 @@
 // each message logged. Defaults to true.
 ABSL_DECLARE_FLAG(bool, log_prefix);
 
+// Global log verbosity level. Default is 0.
+ABSL_DECLARE_FLAG(int, v);
+
+// Per-module log verbosity level. By default is empty and is unused.
+ABSL_DECLARE_FLAG(std::string, vmodule);
+
 #endif  // ABSL_LOG_INTERNAL_FLAGS_H_
diff --git a/absl/log/internal/log_impl.h b/absl/log/internal/log_impl.h
index 9326780..99de6db 100644
--- a/absl/log/internal/log_impl.h
+++ b/absl/log/internal/log_impl.h
@@ -15,6 +15,7 @@
 #ifndef ABSL_LOG_INTERNAL_LOG_IMPL_H_
 #define ABSL_LOG_INTERNAL_LOG_IMPL_H_
 
+#include "absl/log/absl_vlog_is_on.h"
 #include "absl/log/internal/conditions.h"
 #include "absl/log/internal/log_message.h"
 #include "absl/log/internal/strip.h"
@@ -41,6 +42,35 @@
       ABSL_LOGGING_INTERNAL_LOG##severity.InternalStream()
 #endif
 
+// The `switch` ensures that this expansion is the begnning of a statement (as
+// opposed to an expression). The use of both `case 0` and `default` is to
+// suppress a compiler warning.
+#define ABSL_LOG_INTERNAL_VLOG_IMPL(verbose_level)                         \
+  switch (const int absl_logging_internal_verbose_level = (verbose_level)) \
+  case 0:                                                                  \
+  default:                                                                 \
+    ABSL_LOG_INTERNAL_LOG_IF_IMPL(                                         \
+        _INFO, ABSL_VLOG_IS_ON(absl_logging_internal_verbose_level))       \
+        .WithVerbosity(absl_logging_internal_verbose_level)
+
+#ifndef NDEBUG
+#define ABSL_LOG_INTERNAL_DVLOG_IMPL(verbose_level)                        \
+  switch (const int absl_logging_internal_verbose_level = (verbose_level)) \
+  case 0:                                                                  \
+  default:                                                                 \
+    ABSL_LOG_INTERNAL_LOG_IF_IMPL(                                         \
+        _INFO, ABSL_VLOG_IS_ON(absl_logging_internal_verbose_level))       \
+        .WithVerbosity(absl_logging_internal_verbose_level)
+#else
+#define ABSL_LOG_INTERNAL_DVLOG_IMPL(verbose_level)                           \
+  switch (const int absl_logging_internal_verbose_level = (verbose_level))    \
+  case 0:                                                                     \
+  default:                                                                    \
+    ABSL_LOG_INTERNAL_LOG_IF_IMPL(                                            \
+        _INFO, false && ABSL_VLOG_IS_ON(absl_logging_internal_verbose_level)) \
+        .WithVerbosity(absl_logging_internal_verbose_level)
+#endif
+
 #define ABSL_LOG_INTERNAL_LOG_IF_IMPL(severity, condition)    \
   ABSL_LOG_INTERNAL_CONDITION##severity(STATELESS, condition) \
       ABSL_LOGGING_INTERNAL_LOG##severity.InternalStream()
@@ -134,6 +164,42 @@
   (EveryNSec, n_seconds) ABSL_LOGGING_INTERNAL_LOG##severity.InternalStream()
 #endif  // def NDEBUG
 
+#define ABSL_LOG_INTERNAL_VLOG_EVERY_N_IMPL(verbose_level, n)                \
+  switch (const int absl_logging_internal_verbose_level = (verbose_level))   \
+  case 0:                                                                    \
+  default:                                                                   \
+    ABSL_LOG_INTERNAL_CONDITION_INFO(                                        \
+        STATEFUL, ABSL_VLOG_IS_ON(absl_logging_internal_verbose_level))      \
+  (EveryN, n) ABSL_LOGGING_INTERNAL_LOG_INFO.InternalStream().WithVerbosity( \
+      absl_logging_internal_verbose_level)
+
+#define ABSL_LOG_INTERNAL_VLOG_FIRST_N_IMPL(verbose_level, n)                \
+  switch (const int absl_logging_internal_verbose_level = (verbose_level))   \
+  case 0:                                                                    \
+  default:                                                                   \
+    ABSL_LOG_INTERNAL_CONDITION_INFO(                                        \
+        STATEFUL, ABSL_VLOG_IS_ON(absl_logging_internal_verbose_level))      \
+  (FirstN, n) ABSL_LOGGING_INTERNAL_LOG_INFO.InternalStream().WithVerbosity( \
+      absl_logging_internal_verbose_level)
+
+#define ABSL_LOG_INTERNAL_VLOG_EVERY_POW_2_IMPL(verbose_level)               \
+  switch (const int absl_logging_internal_verbose_level = (verbose_level))   \
+  case 0:                                                                    \
+  default:                                                                   \
+    ABSL_LOG_INTERNAL_CONDITION_INFO(                                        \
+        STATEFUL, ABSL_VLOG_IS_ON(absl_logging_internal_verbose_level))      \
+  (EveryPow2) ABSL_LOGGING_INTERNAL_LOG_INFO.InternalStream().WithVerbosity( \
+      absl_logging_internal_verbose_level)
+
+#define ABSL_LOG_INTERNAL_VLOG_EVERY_N_SEC_IMPL(verbose_level, n_seconds)  \
+  switch (const int absl_logging_internal_verbose_level = (verbose_level)) \
+  case 0:                                                                  \
+  default:                                                                 \
+    ABSL_LOG_INTERNAL_CONDITION_INFO(                                      \
+        STATEFUL, ABSL_VLOG_IS_ON(absl_logging_internal_verbose_level))    \
+  (EveryNSec, n_seconds) ABSL_LOGGING_INTERNAL_LOG_INFO.InternalStream()   \
+      .WithVerbosity(absl_logging_internal_verbose_level)
+
 #define ABSL_LOG_INTERNAL_LOG_IF_EVERY_N_IMPL(severity, condition, n)   \
   ABSL_LOG_INTERNAL_CONDITION##severity(STATEFUL, condition)(EveryN, n) \
       ABSL_LOGGING_INTERNAL_LOG##severity.InternalStream()
diff --git a/absl/log/internal/log_message.h b/absl/log/internal/log_message.h
index c94d053..4ecb8a1 100644
--- a/absl/log/internal/log_message.h
+++ b/absl/log/internal/log_message.h
@@ -170,13 +170,15 @@
 
   // Types that support `AbslStringify()` are serialized that way.
   template <typename T,
-            typename std::enable_if<HasAbslStringify<T>::value, int>::type = 0>
+            typename std::enable_if<absl::HasAbslStringify<T>::value,
+                                    int>::type = 0>
   LogMessage& operator<<(const T& v) ABSL_ATTRIBUTE_NOINLINE;
 
   // Types that don't support `AbslStringify()` but do support streaming into a
   // `std::ostream&` are serialized that way.
   template <typename T,
-            typename std::enable_if<!HasAbslStringify<T>::value, int>::type = 0>
+            typename std::enable_if<!absl::HasAbslStringify<T>::value,
+                                    int>::type = 0>
   LogMessage& operator<<(const T& v) ABSL_ATTRIBUTE_NOINLINE;
 
   // Note: We explicitly do not support `operator<<` for non-const references
@@ -281,7 +283,7 @@
 
 // Note: the following is declared `ABSL_ATTRIBUTE_NOINLINE`
 template <typename T,
-          typename std::enable_if<HasAbslStringify<T>::value, int>::type>
+          typename std::enable_if<absl::HasAbslStringify<T>::value, int>::type>
 LogMessage& LogMessage::operator<<(const T& v) {
   StringifySink sink(*this);
   // Replace with public API.
@@ -291,7 +293,7 @@
 
 // Note: the following is declared `ABSL_ATTRIBUTE_NOINLINE`
 template <typename T,
-          typename std::enable_if<!HasAbslStringify<T>::value, int>::type>
+          typename std::enable_if<!absl::HasAbslStringify<T>::value, int>::type>
 LogMessage& LogMessage::operator<<(const T& v) {
   OstreamView view(*data_);
   view.stream() << log_internal::NullGuard<T>().Guard(v);
diff --git a/absl/log/internal/strip.h b/absl/log/internal/strip.h
index adc86ff..f8d2786 100644
--- a/absl/log/internal/strip.h
+++ b/absl/log/internal/strip.h
@@ -37,7 +37,7 @@
 #define ABSL_LOGGING_INTERNAL_LOG_DFATAL \
   ::absl::log_internal::NullStreamMaybeFatal(::absl::kLogDebugFatal)
 #define ABSL_LOGGING_INTERNAL_LOG_LEVEL(severity) \
-  ::absl::log_internal::NullStreamMaybeFatal(log_internal_severity)
+  ::absl::log_internal::NullStreamMaybeFatal(absl_log_internal_severity)
 #define ABSL_LOG_INTERNAL_CHECK(failure_message) ABSL_LOGGING_INTERNAL_LOG_FATAL
 #define ABSL_LOG_INTERNAL_QCHECK(failure_message) \
   ABSL_LOGGING_INTERNAL_LOG_QFATAL
@@ -57,8 +57,9 @@
   ::absl::log_internal::LogMessageQuietlyFatal(__FILE__, __LINE__)
 #define ABSL_LOGGING_INTERNAL_LOG_DFATAL \
   ::absl::log_internal::LogMessage(__FILE__, __LINE__, ::absl::kLogDebugFatal)
-#define ABSL_LOGGING_INTERNAL_LOG_LEVEL(severity) \
-  ::absl::log_internal::LogMessage(__FILE__, __LINE__, log_internal_severity)
+#define ABSL_LOGGING_INTERNAL_LOG_LEVEL(severity)      \
+  ::absl::log_internal::LogMessage(__FILE__, __LINE__, \
+                                   absl_log_internal_severity)
 // These special cases dispatch to special-case constructors that allow us to
 // avoid an extra function call and shrink non-LTO binaries by a percent or so.
 #define ABSL_LOG_INTERNAL_CHECK(failure_message) \
diff --git a/absl/log/internal/vlog_config.cc b/absl/log/internal/vlog_config.cc
new file mode 100644
index 0000000..b578850
--- /dev/null
+++ b/absl/log/internal/vlog_config.cc
@@ -0,0 +1,340 @@
+// Copyright 2022 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/log/internal/vlog_config.h"
+
+#include <stddef.h>
+
+#include <algorithm>
+#include <atomic>
+#include <functional>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "absl/base/attributes.h"
+#include "absl/base/config.h"
+#include "absl/base/const_init.h"
+#include "absl/base/internal/spinlock.h"
+#include "absl/base/no_destructor.h"
+#include "absl/base/optimization.h"
+#include "absl/base/thread_annotations.h"
+#include "absl/log/internal/fnmatch.h"
+#include "absl/memory/memory.h"
+#include "absl/strings/numbers.h"
+#include "absl/strings/str_split.h"
+#include "absl/strings/string_view.h"
+#include "absl/strings/strip.h"
+#include "absl/synchronization/mutex.h"
+#include "absl/types/optional.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace log_internal {
+
+namespace {
+bool ModuleIsPath(absl::string_view module_pattern) {
+#ifdef _WIN32
+  return module_pattern.find_first_of("/\\") != module_pattern.npos;
+#else
+  return module_pattern.find('/') != module_pattern.npos;
+#endif
+}
+}  // namespace
+
+bool VLogSite::SlowIsEnabled(int stale_v, int level) {
+  if (ABSL_PREDICT_TRUE(stale_v != kUninitialized)) {
+    // Because of the prerequisites to this function, we know that stale_v is
+    // either uninitialized or >= level. If it's not uninitialized, that means
+    // it must be >= level, thus we should log.
+    return true;
+  }
+  stale_v = log_internal::RegisterAndInitialize(this);
+  return ABSL_PREDICT_FALSE(stale_v >= level);
+}
+
+bool VLogSite::SlowIsEnabled0(int stale_v) { return SlowIsEnabled(stale_v, 0); }
+bool VLogSite::SlowIsEnabled1(int stale_v) { return SlowIsEnabled(stale_v, 1); }
+bool VLogSite::SlowIsEnabled2(int stale_v) { return SlowIsEnabled(stale_v, 2); }
+bool VLogSite::SlowIsEnabled3(int stale_v) { return SlowIsEnabled(stale_v, 3); }
+bool VLogSite::SlowIsEnabled4(int stale_v) { return SlowIsEnabled(stale_v, 4); }
+bool VLogSite::SlowIsEnabled5(int stale_v) { return SlowIsEnabled(stale_v, 5); }
+
+namespace {
+struct VModuleInfo final {
+  std::string module_pattern;
+  bool module_is_path;  // i.e. it contains a path separator.
+  int vlog_level;
+
+  // Allocates memory.
+  VModuleInfo(absl::string_view module_pattern_arg, bool module_is_path_arg,
+              int vlog_level_arg)
+      : module_pattern(std::string(module_pattern_arg)),
+        module_is_path(module_is_path_arg),
+        vlog_level(vlog_level_arg) {}
+};
+
+// `mutex` guards all of the data structures that aren't lock-free.
+// To avoid problems with the heap checker which calls into `VLOG`, `mutex` must
+// be a `SpinLock` that prevents fiber scheduling instead of a `Mutex`.
+ABSL_CONST_INIT absl::base_internal::SpinLock mutex(
+    absl::kConstInit, absl::base_internal::SCHEDULE_KERNEL_ONLY);
+
+// `GetUpdateSitesMutex()` serializes updates to all of the sites (i.e. those in
+// `site_list_head`) themselves.
+absl::Mutex* GetUpdateSitesMutex() {
+  // Chromium requires no global destructors, so we can't use the
+  // absl::kConstInit idiom since absl::Mutex as a non-trivial destructor.
+  static absl::NoDestructor<absl::Mutex> update_sites_mutex ABSL_ACQUIRED_AFTER(
+      mutex);
+  return update_sites_mutex.get();
+}
+
+ABSL_CONST_INIT int global_v ABSL_GUARDED_BY(mutex) = 0;
+// `site_list_head` is the head of a singly-linked list.  Traversal, insertion,
+// and reads are atomic, so no locks are required, but updates to existing
+// elements are guarded by `GetUpdateSitesMutex()`.
+ABSL_CONST_INIT std::atomic<VLogSite*> site_list_head{nullptr};
+ABSL_CONST_INIT std::vector<VModuleInfo>* vmodule_info ABSL_GUARDED_BY(mutex)
+    ABSL_PT_GUARDED_BY(mutex){nullptr};
+
+// Only used for lisp.
+ABSL_CONST_INIT std::vector<std::function<void()>>* update_callbacks
+    ABSL_GUARDED_BY(GetUpdateSitesMutex())
+        ABSL_PT_GUARDED_BY(GetUpdateSitesMutex()){nullptr};
+
+// Allocates memory.
+std::vector<VModuleInfo>& get_vmodule_info()
+    ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex) {
+  if (!vmodule_info) vmodule_info = new std::vector<VModuleInfo>;
+  return *vmodule_info;
+}
+
+// Does not allocate or take locks.
+int VLogLevel(absl::string_view file, const std::vector<VModuleInfo>* infos,
+              int current_global_v) {
+  // `infos` is null during a call to `VLOG` prior to setting `vmodule` (e.g. by
+  // parsing flags).  We can't allocate in `VLOG`, so we treat null as empty
+  // here and press on.
+  if (!infos || infos->empty()) return current_global_v;
+  // Get basename for file
+  absl::string_view basename = file;
+  {
+    const size_t sep = basename.rfind('/');
+    if (sep != basename.npos) {
+      basename.remove_prefix(sep + 1);
+#ifdef _WIN32
+    } else {
+      const size_t sep = basename.rfind('\\');
+      if (sep != basename.npos) basename.remove_prefix(sep + 1);
+#endif
+    }
+  }
+
+  absl::string_view stem = file, stem_basename = basename;
+  {
+    const size_t sep = stem_basename.find('.');
+    if (sep != stem_basename.npos) {
+      stem.remove_suffix(stem_basename.size() - sep);
+      stem_basename.remove_suffix(stem_basename.size() - sep);
+    }
+    if (absl::ConsumeSuffix(&stem_basename, "-inl")) {
+      stem.remove_suffix(absl::string_view("-inl").size());
+    }
+  }
+  for (const auto& info : *infos) {
+    if (info.module_is_path) {
+      // If there are any slashes in the pattern, try to match the full
+      // name.
+      if (FNMatch(info.module_pattern, stem)) {
+        return info.vlog_level == kUseFlag ? current_global_v : info.vlog_level;
+      }
+    } else if (FNMatch(info.module_pattern, stem_basename)) {
+      return info.vlog_level == kUseFlag ? current_global_v : info.vlog_level;
+    }
+  }
+
+  return current_global_v;
+}
+
+// Allocates memory.
+int AppendVModuleLocked(absl::string_view module_pattern, int log_level)
+    ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex) {
+  for (const auto& info : get_vmodule_info()) {
+    if (FNMatch(info.module_pattern, module_pattern)) {
+      // This is a memory optimization to avoid storing patterns that will never
+      // match due to exit early semantics. Primarily optimized for our own unit
+      // tests.
+      return info.vlog_level;
+    }
+  }
+  bool module_is_path = ModuleIsPath(module_pattern);
+  get_vmodule_info().emplace_back(std::string(module_pattern), module_is_path,
+                                  log_level);
+  return global_v;
+}
+
+// Allocates memory.
+int PrependVModuleLocked(absl::string_view module_pattern, int log_level)
+    ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex) {
+  absl::optional<int> old_log_level;
+  for (const auto& info : get_vmodule_info()) {
+    if (FNMatch(info.module_pattern, module_pattern)) {
+      old_log_level = info.vlog_level;
+      break;
+    }
+  }
+  bool module_is_path = ModuleIsPath(module_pattern);
+  auto iter = get_vmodule_info().emplace(get_vmodule_info().cbegin(),
+                                         std::string(module_pattern),
+                                         module_is_path, log_level);
+
+  // This is a memory optimization to avoid storing patterns that will never
+  // match due to exit early semantics. Primarily optimized for our own unit
+  // tests.
+  get_vmodule_info().erase(
+      std::remove_if(++iter, get_vmodule_info().end(),
+                     [module_pattern](const VModuleInfo& info) {
+                       return FNMatch(info.module_pattern, module_pattern);
+                     }),
+      get_vmodule_info().cend());
+  return old_log_level.value_or(global_v);
+}
+}  // namespace
+
+int VLogLevel(absl::string_view file) ABSL_LOCKS_EXCLUDED(mutex) {
+  absl::base_internal::SpinLockHolder l(&mutex);
+  return VLogLevel(file, vmodule_info, global_v);
+}
+
+int RegisterAndInitialize(VLogSite* v) ABSL_LOCKS_EXCLUDED(mutex) {
+  // std::memory_order_seq_cst is overkill in this function, but given that this
+  // path is intended to be slow, it's not worth the brain power to relax that.
+  VLogSite* h = site_list_head.load(std::memory_order_seq_cst);
+
+  VLogSite* old = nullptr;
+  if (v->next_.compare_exchange_strong(old, h, std::memory_order_seq_cst,
+                                       std::memory_order_seq_cst)) {
+    // Multiple threads may attempt to register this site concurrently.
+    // By successfully setting `v->next` this thread commits to being *the*
+    // thread that installs `v` in the list.
+    while (!site_list_head.compare_exchange_weak(
+        h, v, std::memory_order_seq_cst, std::memory_order_seq_cst)) {
+      v->next_.store(h, std::memory_order_seq_cst);
+    }
+  }
+
+  int old_v = VLogSite::kUninitialized;
+  int new_v = VLogLevel(v->file_);
+  // No loop, if someone else set this, we should respect their evaluation of
+  // `VLogLevel`. This may mean we return a stale `v`, but `v` itself will
+  // always arrive at the freshest value.  Otherwise, we could be writing a
+  // stale value and clobbering the fresher one.
+  if (v->v_.compare_exchange_strong(old_v, new_v, std::memory_order_seq_cst,
+                                    std::memory_order_seq_cst)) {
+    return new_v;
+  }
+  return old_v;
+}
+
+void UpdateVLogSites() ABSL_UNLOCK_FUNCTION(mutex)
+    ABSL_LOCKS_EXCLUDED(GetUpdateSitesMutex()) {
+  std::vector<VModuleInfo> infos = get_vmodule_info();
+  int current_global_v = global_v;
+  // We need to grab `GetUpdateSitesMutex()` before we release `mutex` to ensure
+  // that updates are not interleaved (resulting in an inconsistent final state)
+  // and to ensure that the final state in the sites matches the final state of
+  // `vmodule_info`. We unlock `mutex` to ensure that uninitialized sites don't
+  // have to wait on all updates in order to acquire `mutex` and initialize
+  // themselves.
+  absl::MutexLock ul(GetUpdateSitesMutex());
+  mutex.Unlock();
+  VLogSite* n = site_list_head.load(std::memory_order_seq_cst);
+  // Because sites are added to the list in the order they are executed, there
+  // tend to be clusters of entries with the same file.
+  const char* last_file = nullptr;
+  int last_file_level = 0;
+  while (n != nullptr) {
+    if (n->file_ != last_file) {
+      last_file = n->file_;
+      last_file_level = VLogLevel(n->file_, &infos, current_global_v);
+    }
+    n->v_.store(last_file_level, std::memory_order_seq_cst);
+    n = n->next_.load(std::memory_order_seq_cst);
+  }
+  if (update_callbacks) {
+    for (auto& cb : *update_callbacks) {
+      cb();
+    }
+  }
+}
+
+void UpdateVModule(absl::string_view vmodule)
+    ABSL_LOCKS_EXCLUDED(mutex, GetUpdateSitesMutex()) {
+  std::vector<std::pair<absl::string_view, int>> glob_levels;
+  for (absl::string_view glob_level : absl::StrSplit(vmodule, ',')) {
+    const size_t eq = glob_level.rfind('=');
+    if (eq == glob_level.npos) continue;
+    const absl::string_view glob = glob_level.substr(0, eq);
+    int level;
+    if (!absl::SimpleAtoi(glob_level.substr(eq + 1), &level)) continue;
+    glob_levels.emplace_back(glob, level);
+  }
+  mutex.Lock();  // Unlocked by UpdateVLogSites().
+  get_vmodule_info().clear();
+  for (const auto& it : glob_levels) {
+    const absl::string_view glob = it.first;
+    const int level = it.second;
+    AppendVModuleLocked(glob, level);
+  }
+  UpdateVLogSites();
+}
+
+int UpdateGlobalVLogLevel(int v)
+    ABSL_LOCKS_EXCLUDED(mutex, GetUpdateSitesMutex()) {
+  mutex.Lock();  // Unlocked by UpdateVLogSites().
+  const int old_global_v = global_v;
+  if (v == global_v) {
+    mutex.Unlock();
+    return old_global_v;
+  }
+  global_v = v;
+  UpdateVLogSites();
+  return old_global_v;
+}
+
+int PrependVModule(absl::string_view module_pattern, int log_level)
+    ABSL_LOCKS_EXCLUDED(mutex, GetUpdateSitesMutex()) {
+  mutex.Lock();  // Unlocked by UpdateVLogSites().
+  int old_v = PrependVModuleLocked(module_pattern, log_level);
+  UpdateVLogSites();
+  return old_v;
+}
+
+void OnVLogVerbosityUpdate(std::function<void()> cb)
+    ABSL_LOCKS_EXCLUDED(GetUpdateSitesMutex()) {
+  absl::MutexLock ul(GetUpdateSitesMutex());
+  if (!update_callbacks)
+    update_callbacks = new std::vector<std::function<void()>>;
+  update_callbacks->push_back(std::move(cb));
+}
+
+VLogSite* SetVModuleListHeadForTestOnly(VLogSite* v) {
+  return site_list_head.exchange(v, std::memory_order_seq_cst);
+}
+
+}  // namespace log_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
diff --git a/absl/log/internal/vlog_config.h b/absl/log/internal/vlog_config.h
new file mode 100644
index 0000000..b6e322c
--- /dev/null
+++ b/absl/log/internal/vlog_config.h
@@ -0,0 +1,163 @@
+// Copyright 2022 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.
+//
+// -----------------------------------------------------------------------------
+// vlog_config.h
+// -----------------------------------------------------------------------------
+//
+// This header file defines `VLogSite`, a public primitive that represents
+// a callsite for the `VLOG` family of macros and related libraries.
+// It also declares and defines multiple internal utilities used to implement
+// `VLOG`, such as `VLogSiteManager`.
+
+#ifndef ABSL_LOG_INTERNAL_VLOG_CONFIG_H_
+#define ABSL_LOG_INTERNAL_VLOG_CONFIG_H_
+
+// IWYU pragma: private, include "absl/log/log.h"
+
+#include <atomic>
+#include <cstdint>
+#include <functional>
+#include <limits>
+#include <type_traits>
+
+#include "absl/base/attributes.h"
+#include "absl/base/config.h"
+#include "absl/base/optimization.h"
+#include "absl/base/thread_annotations.h"
+#include "absl/strings/string_view.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace log_internal {
+
+class SyntheticBinary;
+class VLogSite;
+
+int RegisterAndInitialize(VLogSite* v);
+void UpdateVLogSites();
+constexpr int kUseFlag = (std::numeric_limits<int16_t>::min)();
+
+// Represents a unique callsite for a `VLOG()` or `VLOG_IS_ON()` call.
+//
+// Libraries that provide `VLOG`-like functionality should use this to
+// efficiently handle --vmodule.
+//
+// VLogSite objects must not be destroyed until the program exits. Doing so will
+// probably yield nasty segfaults in VLogSiteManager::UpdateLogSites(). The
+// recommendation is to make all such objects function-local statics.
+class VLogSite final {
+ public:
+  // `f` must not be destroyed until the program exits.
+  explicit constexpr VLogSite(const char* f)
+      : file_(f), v_(kUninitialized), next_(nullptr) {}
+  VLogSite(const VLogSite&) = delete;
+  VLogSite& operator=(const VLogSite&) = delete;
+
+  // Inlining the function yields a ~3x performance improvement at the cost of a
+  // 1.5x code size increase at the call site.
+  // Takes locks but does not allocate memory.
+  ABSL_ATTRIBUTE_ALWAYS_INLINE
+  bool IsEnabled(int level) {
+    int stale_v = v_.load(std::memory_order_relaxed);
+    if (ABSL_PREDICT_TRUE(level > stale_v)) {
+      return false;
+    }
+
+    // We put everything other than the fast path, i.e. vlogging is initialized
+    // but not on, behind an out-of-line function to reduce code size.
+    // "level" is almost always a call-site constant, so we can save a bit
+    // of code space by special-casing for a few common levels.
+#if ABSL_HAVE_BUILTIN(__builtin_constant_p) || defined(__GNUC__)
+    if (__builtin_constant_p(level)) {
+      if (level == 0) return SlowIsEnabled0(stale_v);
+      if (level == 1) return SlowIsEnabled1(stale_v);
+      if (level == 2) return SlowIsEnabled2(stale_v);
+      if (level == 3) return SlowIsEnabled3(stale_v);
+      if (level == 4) return SlowIsEnabled4(stale_v);
+      if (level == 5) return SlowIsEnabled5(stale_v);
+    }
+#endif
+    return SlowIsEnabled(stale_v, level);
+  }
+
+ private:
+  friend int log_internal::RegisterAndInitialize(VLogSite* v);
+  friend void log_internal::UpdateVLogSites();
+  friend class log_internal::SyntheticBinary;
+  static constexpr int kUninitialized = (std::numeric_limits<int>::max)();
+
+  // SlowIsEnabled performs slower checks to determine whether a log site is
+  // enabled. Because it is expected to be called somewhat rarely
+  // (comparatively), it is not inlined to save on code size.
+  //
+  // Prerequisites to calling SlowIsEnabled:
+  //   1) stale_v is uninitialized OR
+  //   2) stale_v is initialized and >= level (meaning we must log).
+  // Takes locks but does not allocate memory.
+  ABSL_ATTRIBUTE_NOINLINE
+  bool SlowIsEnabled(int stale_v, int level);
+  ABSL_ATTRIBUTE_NOINLINE bool SlowIsEnabled0(int stale_v);
+  ABSL_ATTRIBUTE_NOINLINE bool SlowIsEnabled1(int stale_v);
+  ABSL_ATTRIBUTE_NOINLINE bool SlowIsEnabled2(int stale_v);
+  ABSL_ATTRIBUTE_NOINLINE bool SlowIsEnabled3(int stale_v);
+  ABSL_ATTRIBUTE_NOINLINE bool SlowIsEnabled4(int stale_v);
+  ABSL_ATTRIBUTE_NOINLINE bool SlowIsEnabled5(int stale_v);
+
+  // This object is too size-sensitive to use absl::string_view.
+  const char* const file_;
+  std::atomic<int> v_;
+  std::atomic<VLogSite*> next_;
+};
+static_assert(std::is_trivially_destructible<VLogSite>::value,
+              "VLogSite must be trivially destructible");
+
+// Returns the current verbose log level of `file`.
+// Does not allocate memory.
+int VLogLevel(absl::string_view file);
+
+// Registers a site `v` to get updated as `vmodule` and `v` change.  Also
+// initializes the site based on their current values, and returns that result.
+// Does not allocate memory.
+int RegisterAndInitialize(VLogSite* v);
+
+// Allocates memory.
+void UpdateVLogSites();
+
+// Completely overwrites the saved value of `vmodule`.
+// Allocates memory.
+void UpdateVModule(absl::string_view vmodule);
+
+// Updates the global verbosity level to `v` and returns the prior value.
+// Allocates memory.
+int UpdateGlobalVLogLevel(int v);
+
+// Atomically prepends `module_pattern=log_level` to the start of vmodule.
+// Returns the prior value for `module_pattern` if there was an exact match and
+// `global_v` otherwise.
+// Allocates memory.
+int PrependVModule(absl::string_view module_pattern, int log_level);
+
+// Registers `on_update` to be called whenever `v` or `vmodule` change.
+// Allocates memory.
+void OnVLogVerbosityUpdate(std::function<void()> cb);
+
+// Does not allocate memory.
+VLogSite* SetVModuleListHeadForTestOnly(VLogSite* v);
+
+}  // namespace log_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif  // ABSL_LOG_INTERNAL_VLOG_CONFIG_H_
diff --git a/absl/log/internal/vlog_config_benchmark.cc b/absl/log/internal/vlog_config_benchmark.cc
new file mode 100644
index 0000000..9004e2e
--- /dev/null
+++ b/absl/log/internal/vlog_config_benchmark.cc
@@ -0,0 +1,187 @@
+// Copyright 2022 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 <algorithm>
+#include <atomic>
+#include <cstddef>
+#include <cstring>
+#include <memory>
+#include <new>
+#include <random>
+#include <string>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+#include "absl/base/config.h"
+#include "absl/container/internal/layout.h"
+#include "absl/log/internal/vlog_config.h"
+#include "absl/memory/memory.h"
+#include "absl/random/distributions.h"
+#include "absl/strings/str_cat.h"
+#include "benchmark/benchmark.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace log_internal {
+// Performance of `UpdateLogSites` depends upon the number and organization of
+// `VLogSite`s in the program.  We can synthesize some on the heap to mimic
+// their layout and linkage in static data.
+class SyntheticBinary {
+ public:
+  explicit SyntheticBinary(const size_t num_tus,
+                           const size_t max_extra_static_data_bytes_per_tu,
+                           const size_t max_sites_per_tu,
+                           const int num_shuffles) {
+    per_tu_data_.reserve(num_tus);
+    auto sites = absl::make_unique<VLogSite *[]>(num_tus * max_sites_per_tu);
+    for (size_t i = 0; i < num_tus; i++) {
+      const std::string filename =
+          absl::StrCat("directory-", i / 100, "/subdirectory-", i % 100 / 10,
+                       "/file-", i % 10, ".cc");
+      container_internal::Layout<char, VLogSite, char> layout(
+          filename.size() + 1,
+          absl::LogUniform<size_t>(bitgen_, 1, max_sites_per_tu),
+          absl::LogUniform<size_t>(bitgen_, 0,
+                                   max_extra_static_data_bytes_per_tu));
+      auto buf = absl::make_unique<char[]>(layout.AllocSize());
+      layout.PoisonPadding(buf.get());
+      memcpy(layout.Pointer<0>(buf.get()), filename.c_str(),
+             filename.size() + 1);
+      for (VLogSite &site : layout.Slice<1>(buf.get())) {
+        sites[num_sites_++] =
+            new (&site) VLogSite(layout.Pointer<0>(buf.get()));
+        // The last one is a dangling pointer but will be fixed below.
+        site.next_.store(&site + 1, std::memory_order_seq_cst);
+      }
+      num_extra_static_data_bytes_ += layout.Size<2>();
+      per_tu_data_.push_back(PerTU{layout, std::move(buf)});
+    }
+    // Now link the files together back-to-front into a circular list.
+    for (size_t i = 0; i < num_tus; i++) {
+      auto &tu = per_tu_data_[i];
+      auto &next_tu = per_tu_data_[(i + 1) % num_tus];
+      tu.layout.Slice<1>(tu.buf.get())
+          .back()
+          .next_.store(next_tu.layout.Pointer<1>(next_tu.buf.get()),
+                       std::memory_order_seq_cst);
+    }
+    // Now do some shufflin'.
+    auto new_sites = absl::make_unique<VLogSite *[]>(num_sites_);
+    for (int shuffle_num = 0; shuffle_num < num_shuffles; shuffle_num++) {
+      // Each shuffle cuts the ring into three pieces and rearranges them.
+      const size_t cut_a = absl::Uniform(bitgen_, size_t{0}, num_sites_);
+      const size_t cut_b = absl::Uniform(bitgen_, size_t{0}, num_sites_);
+      const size_t cut_c = absl::Uniform(bitgen_, size_t{0}, num_sites_);
+      if (cut_a == cut_b || cut_b == cut_c || cut_a == cut_c) continue;
+      // The same cuts, sorted:
+      const size_t cut_1 = std::min({cut_a, cut_b, cut_c});
+      const size_t cut_3 = std::max({cut_a, cut_b, cut_c});
+      const size_t cut_2 = cut_a ^ cut_b ^ cut_c ^ cut_1 ^ cut_3;
+      VLogSite *const tmp = sites[cut_1]->next_.load(std::memory_order_seq_cst);
+      sites[cut_1]->next_.store(
+          sites[cut_2]->next_.load(std::memory_order_seq_cst),
+          std::memory_order_seq_cst);
+      sites[cut_2]->next_.store(
+          sites[cut_3]->next_.load(std::memory_order_seq_cst),
+          std::memory_order_seq_cst);
+      sites[cut_3]->next_.store(tmp, std::memory_order_seq_cst);
+      memcpy(&new_sites[0], &sites[0], sizeof(VLogSite *) * (cut_1 + 1));
+      memcpy(&new_sites[cut_1 + 1], &sites[cut_2 + 1],
+             sizeof(VLogSite *) * (cut_3 - cut_2));
+      memcpy(&new_sites[cut_1 + 1 + cut_3 - cut_2], &sites[cut_1 + 1],
+             sizeof(VLogSite *) * (cut_2 - cut_1));
+      memcpy(&new_sites[cut_3 + 1], &sites[cut_3 + 1],
+             sizeof(VLogSite *) * (num_sites_ - cut_3 - 1));
+      sites.swap(new_sites);
+    }
+    const char *last_file = nullptr;
+    for (size_t i = 0; i < num_sites_; i++) {
+      if (sites[i]->file_ == last_file) continue;
+      last_file = sites[i]->file_;
+      num_new_files_++;
+    }
+    absl::log_internal::SetVModuleListHeadForTestOnly(sites[0]);
+    sites[num_tus - 1]->next_.store(nullptr, std::memory_order_seq_cst);
+  }
+  ~SyntheticBinary() {
+    static_assert(std::is_trivially_destructible<VLogSite>::value, "");
+    absl::log_internal::SetVModuleListHeadForTestOnly(nullptr);
+  }
+
+  size_t num_sites() const { return num_sites_; }
+  size_t num_new_files() const { return num_new_files_; }
+  size_t num_extra_static_data_bytes() const {
+    return num_extra_static_data_bytes_;
+  }
+
+ private:
+  struct PerTU {
+    container_internal::Layout<char, VLogSite, char> layout;
+    std::unique_ptr<char[]> buf;
+  };
+
+  std::mt19937 bitgen_;
+  std::vector<PerTU> per_tu_data_;
+  size_t num_sites_ = 0;
+  size_t num_new_files_ = 0;
+  size_t num_extra_static_data_bytes_ = 0;
+};
+
+namespace {
+void BM_UpdateVModuleEmpty(benchmark::State& state) {
+  SyntheticBinary bin(static_cast<size_t>(state.range(0)), 10 * 1024 * 1024,
+                      256, state.range(1));
+  for (auto s : state) {
+    absl::log_internal::UpdateVModule("");
+  }
+  state.SetItemsProcessed(static_cast<int>(bin.num_new_files()));
+}
+BENCHMARK(BM_UpdateVModuleEmpty)
+    ->ArgPair(100, 200)
+    ->ArgPair(1000, 2000)
+    ->ArgPair(10000, 20000);
+
+void BM_UpdateVModuleShort(benchmark::State& state) {
+  SyntheticBinary bin(static_cast<size_t>(state.range(0)), 10 * 1024 * 1024,
+                      256, state.range(1));
+  for (auto s : state) {
+    absl::log_internal::UpdateVModule("directory2/*=4");
+  }
+  state.SetItemsProcessed(static_cast<int>(bin.num_new_files()));
+}
+BENCHMARK(BM_UpdateVModuleShort)
+    ->ArgPair(100, 200)
+    ->ArgPair(1000, 2000)
+    ->ArgPair(10000, 20000);
+
+void BM_UpdateVModuleLong(benchmark::State& state) {
+  SyntheticBinary bin(static_cast<size_t>(state.range(0)), 10 * 1024 * 1024,
+                      256, state.range(1));
+  for (auto s : state) {
+    absl::log_internal::UpdateVModule(
+        "d?r?c?o?y2/*=4,d?*r?*c?**o?*y1/*=2,d?*rc?**o?*y3/*=2,,"
+        "d?*r?*c?**o?*1/*=1,d?*r?**o?*y1/*=2,d?*r???***y1/*=7,"
+        "d?*r?**o?*y1/aaa=6");
+  }
+  state.SetItemsProcessed(static_cast<int>(bin.num_new_files()));
+}
+BENCHMARK(BM_UpdateVModuleLong)
+    ->ArgPair(100, 200)
+    ->ArgPair(1000, 2000)
+    ->ArgPair(10000, 20000);
+}  // namespace
+}  // namespace log_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
diff --git a/absl/log/log.h b/absl/log/log.h
index 99e78ea..b721b08 100644
--- a/absl/log/log.h
+++ b/absl/log/log.h
@@ -53,6 +53,14 @@
 //   * .NoPrefix()
 //     Omits the prefix from this line.  The prefix includes metadata about the
 //     logged data such as source code location and timestamp.
+//   * .WithVerbosity(int verbose_level)
+//     Sets the verbosity field of the logged message as if it was logged by
+//     `VLOG(verbose_level)`.  Unlike `VLOG`, this method does not affect
+//     evaluation of the statement when the specified `verbose_level` has been
+//     disabled.  The only effect is on `LogSink` implementations which make use
+//     of the `absl::LogSink::verbosity()` value.  The value
+//     `absl::LogEntry::kNoVerbosityLevel` can be specified to mark the message
+//     not verbose.
 //   * .WithTimestamp(absl::Time timestamp)
 //     Uses the specified timestamp instead of one collected at the time of
 //     execution.
@@ -190,6 +198,7 @@
 #define ABSL_LOG_LOG_H_
 
 #include "absl/log/internal/log_impl.h"
+#include "absl/log/vlog_is_on.h"  // IWYU pragma: export
 
 // LOG()
 //
@@ -213,11 +222,32 @@
 // terminate the program if `NDEBUG` is defined.
 #define DLOG(severity) ABSL_LOG_INTERNAL_DLOG_IMPL(_##severity)
 
+// `VLOG` uses numeric levels to provide verbose logging that can configured at
+// runtime, including at a per-module level.  `VLOG` statements are logged at
+// `INFO` severity if they are logged at all; the numeric levels are on a
+// different scale than the proper severity levels.  Positive levels are
+// disabled by default.  Negative levels should not be used.
+// Example:
+//
+//   VLOG(1) << "I print when you run the program with --v=1 or higher";
+//   VLOG(2) << "I print when you run the program with --v=2 or higher";
+//
+// See vlog_is_on.h for further documentation, including the usage of the
+// --vmodule flag to log at different levels in different source files.
+#define VLOG(severity) ABSL_LOG_INTERNAL_VLOG_IMPL(severity)
+
+// `DVLOG` behaves like `VLOG` in debug mode (i.e. `#ifndef NDEBUG`).
+// Otherwise, it compiles away and does nothing.
+#define DVLOG(severity) ABSL_LOG_INTERNAL_DVLOG_IMPL(severity)
+
 // `LOG_IF` and friends add a second argument which specifies a condition.  If
 // the condition is false, nothing is logged.
 // Example:
 //
 //   LOG_IF(INFO, num_cookies > 10) << "Got lots of cookies";
+//
+// There is no `VLOG_IF` because the order of evaluation of the arguments is
+// ambiguous and the alternate spelling with an `if`-statement is trivial.
 #define LOG_IF(severity, condition) \
   ABSL_LOG_INTERNAL_LOG_IF_IMPL(_##severity, condition)
 #define PLOG_IF(severity, condition) \
@@ -285,6 +315,15 @@
 #define DLOG_EVERY_N_SEC(severity, n_seconds) \
   ABSL_LOG_INTERNAL_DLOG_EVERY_N_SEC_IMPL(_##severity, n_seconds)
 
+#define VLOG_EVERY_N(severity, n) \
+  ABSL_LOG_INTERNAL_VLOG_EVERY_N_IMPL(severity, n)
+#define VLOG_FIRST_N(severity, n) \
+  ABSL_LOG_INTERNAL_VLOG_FIRST_N_IMPL(severity, n)
+#define VLOG_EVERY_POW_2(severity) \
+  ABSL_LOG_INTERNAL_VLOG_EVERY_POW_2_IMPL(severity)
+#define VLOG_EVERY_N_SEC(severity, n_seconds) \
+  ABSL_LOG_INTERNAL_VLOG_EVERY_N_SEC_IMPL(severity, n_seconds)
+
 // `LOG_IF_EVERY_N` and friends behave as the corresponding `LOG_EVERY_N`
 // but neither increment a counter nor log a message if condition is false (as
 // `LOG_IF`).
diff --git a/absl/log/log_entry.h b/absl/log/log_entry.h
index 9e4ae8e..7a55dfe 100644
--- a/absl/log/log_entry.h
+++ b/absl/log/log_entry.h
@@ -96,7 +96,8 @@
   // LogEntry::verbosity()
   //
   // Returns this entry's verbosity, or `kNoVerbosityLevel` for a non-verbose
-  // entry.  Verbosity control is not available outside of Google yet.
+  // entry. Taken from the argument to `VLOG` or from
+  // `LOG(...).WithVerbosity(...)`.
   int verbosity() const { return verbose_level_; }
 
   // LogEntry::timestamp()
diff --git a/absl/log/stripping_test.cc b/absl/log/stripping_test.cc
index 3535703..271fae1 100644
--- a/absl/log/stripping_test.cc
+++ b/absl/log/stripping_test.cc
@@ -54,6 +54,7 @@
 #include "absl/log/check.h"
 #include "absl/log/internal/test_helpers.h"
 #include "absl/log/log.h"
+#include "absl/status/status.h"
 #include "absl/strings/escaping.h"
 #include "absl/strings/str_format.h"
 #include "absl/strings/string_view.h"
@@ -414,4 +415,88 @@
   }
 }
 
+TEST_F(StrippingTest, CheckOp) {
+  // See `StrippingTest.Check` for some hairy implementation notes.
+  const std::string var_needle1 =
+      absl::Base64Escape("StrippingTestCheckOpVar1");
+  const std::string var_needle2 =
+      absl::Base64Escape("StrippingTestCheckOpVar2");
+  const std::string msg_needle = absl::Base64Escape("StrippingTest.CheckOp");
+  volatile int U3RyaXBwaW5nVGVzdENoZWNrT3BWYXIx = 0xFEED;
+  volatile int U3RyaXBwaW5nVGVzdENoZWNrT3BWYXIy = 0xCAFE;
+  if (kReallyDie) {
+    CHECK_EQ(U3RyaXBwaW5nVGVzdENoZWNrT3BWYXIx, U3RyaXBwaW5nVGVzdENoZWNrT3BWYXIy)
+        << "U3RyaXBwaW5nVGVzdC5DaGVja09w";
+  }
+
+  std::unique_ptr<FILE, std::function<void(FILE*)>> exe = OpenTestExecutable();
+  ASSERT_THAT(exe, NotNull());
+
+  if (absl::LogSeverity::kFatal >= kAbslMinLogLevel) {
+    EXPECT_THAT(exe.get(), FileHasSubstr(var_needle1));
+    EXPECT_THAT(exe.get(), FileHasSubstr(var_needle2));
+    EXPECT_THAT(exe.get(), FileHasSubstr(msg_needle));
+  } else {
+    EXPECT_THAT(exe.get(), Not(FileHasSubstr(var_needle1)));
+    EXPECT_THAT(exe.get(), Not(FileHasSubstr(var_needle2)));
+    EXPECT_THAT(exe.get(), Not(FileHasSubstr(msg_needle)));
+  }
+}
+
+TEST_F(StrippingTest, CheckStrOp) {
+  // See `StrippingTest.Check` for some hairy implementation notes.
+  const std::string var_needle1 =
+      absl::Base64Escape("StrippingTestCheckStrOpVar1");
+  const std::string var_needle2 =
+      absl::Base64Escape("StrippingTestCheckStrOpVar2");
+  const std::string msg_needle = absl::Base64Escape("StrippingTest.CheckStrOp");
+  const char *volatile U3RyaXBwaW5nVGVzdENoZWNrU3RyT3BWYXIx = "FEED";
+  const char *volatile U3RyaXBwaW5nVGVzdENoZWNrU3RyT3BWYXIy = "CAFE";
+  if (kReallyDie) {
+    CHECK_STREQ(U3RyaXBwaW5nVGVzdENoZWNrU3RyT3BWYXIx,
+                U3RyaXBwaW5nVGVzdENoZWNrU3RyT3BWYXIy)
+        << "U3RyaXBwaW5nVGVzdC5DaGVja1N0ck9w";
+  }
+
+  std::unique_ptr<FILE, std::function<void(FILE*)>> exe = OpenTestExecutable();
+  ASSERT_THAT(exe, NotNull());
+
+  if (absl::LogSeverity::kFatal >= kAbslMinLogLevel) {
+    EXPECT_THAT(exe.get(), FileHasSubstr(var_needle1));
+    EXPECT_THAT(exe.get(), FileHasSubstr(var_needle2));
+    EXPECT_THAT(exe.get(), FileHasSubstr(msg_needle));
+  } else {
+    EXPECT_THAT(exe.get(), Not(FileHasSubstr(var_needle1)));
+    EXPECT_THAT(exe.get(), Not(FileHasSubstr(var_needle2)));
+    EXPECT_THAT(exe.get(), Not(FileHasSubstr(msg_needle)));
+  }
+}
+
+TEST_F(StrippingTest, CheckOk) {
+  // See `StrippingTest.Check` for some hairy implementation notes.
+  const std::string var_needle = absl::Base64Escape("StrippingTestCheckOkVar1");
+  const std::string msg_needle = absl::Base64Escape("StrippingTest.CheckOk");
+  volatile bool x = false;
+  auto U3RyaXBwaW5nVGVzdENoZWNrT2tWYXIx = absl::OkStatus();
+  if (x) {
+    U3RyaXBwaW5nVGVzdENoZWNrT2tWYXIx =
+        absl::InvalidArgumentError("Stripping this is not my job!");
+  }
+  if (kReallyDie) {
+    CHECK_OK(U3RyaXBwaW5nVGVzdENoZWNrT2tWYXIx)
+        << "U3RyaXBwaW5nVGVzdC5DaGVja09r";
+  }
+
+  std::unique_ptr<FILE, std::function<void(FILE*)>> exe = OpenTestExecutable();
+  ASSERT_THAT(exe, NotNull());
+
+  if (absl::LogSeverity::kFatal >= kAbslMinLogLevel) {
+    EXPECT_THAT(exe.get(), FileHasSubstr(var_needle));
+    EXPECT_THAT(exe.get(), FileHasSubstr(msg_needle));
+  } else {
+    EXPECT_THAT(exe.get(), Not(FileHasSubstr(var_needle)));
+    EXPECT_THAT(exe.get(), Not(FileHasSubstr(msg_needle)));
+  }
+}
+
 }  // namespace
diff --git a/absl/log/vlog_is_on.h b/absl/log/vlog_is_on.h
new file mode 100644
index 0000000..f5c4247
--- /dev/null
+++ b/absl/log/vlog_is_on.h
@@ -0,0 +1,70 @@
+// Copyright 2022 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.
+//
+// -----------------------------------------------------------------------------
+// File: log/vlog_is_on.h
+// -----------------------------------------------------------------------------
+//
+// This header defines the `VLOG_IS_ON()` macro that controls the
+// variable-verbosity conditional logging.
+//
+// It's used by `VLOG` in log.h, or it can also be used directly like this:
+//
+//   if (VLOG_IS_ON(2)) {
+//     foo_server.RecomputeStatisticsExpensive();
+//     LOG(INFO) << foo_server.LastStatisticsAsString();
+//   }
+//
+// Each source file has an effective verbosity level that's a non-negative
+// integer computed from the `--vmodule` and `--v` flags.
+// `VLOG_IS_ON(n)` is true, and `VLOG(n)` logs, if that effective verbosity
+// level is greater than or equal to `n`.
+//
+// `--vmodule` takes a comma-delimited list of key=value pairs.  Each key is a
+// pattern matched against filenames, and the values give the effective severity
+// level applied to matching files.  '?' and '*' characters in patterns are
+// interpreted as single-character and zero-or-more-character wildcards.
+// Patterns including a slash character are matched against full pathnames,
+// while those without are matched against basenames only.  One suffix (i.e. the
+// last . and everything after it) is stripped from each filename prior to
+// matching, as is the special suffix "-inl".
+//
+// Files are matched against globs in `--vmodule` in order, and the first match
+// determines the verbosity level.
+//
+// Files which do not match any pattern in `--vmodule` use the value of `--v` as
+// their effective verbosity level.  The default is 0.
+//
+// SetVLOGLevel helper function is provided to do limited dynamic control over
+// V-logging by appending to `--vmodule`. Because these go at the beginning of
+// the list, they take priority over any globs previously added.
+//
+// Resetting --vmodule will override all previous modifications to `--vmodule`,
+// including via SetVLOGLevel.
+
+#ifndef ABSL_LOG_VLOG_IS_ON_H_
+#define ABSL_LOG_VLOG_IS_ON_H_
+
+#include "absl/log/absl_vlog_is_on.h"  // IWYU pragma: export
+
+// Each VLOG_IS_ON call site gets its own VLogSite that registers with the
+// global linked list of sites to asynchronously update its verbosity level on
+// changes to --v or --vmodule. The verbosity can also be set by manually
+// calling SetVLOGLevel.
+//
+// VLOG_IS_ON is not async signal safe, but it is guaranteed not to allocate
+// new memory.
+#define VLOG_IS_ON(verbose_level) ABSL_VLOG_IS_ON(verbose_level)
+
+#endif  // ABSL_LOG_VLOG_IS_ON_H_
diff --git a/absl/log/vlog_is_on_test.cc b/absl/log/vlog_is_on_test.cc
new file mode 100644
index 0000000..28a2eea
--- /dev/null
+++ b/absl/log/vlog_is_on_test.cc
@@ -0,0 +1,175 @@
+// Copyright 2023 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/log/vlog_is_on.h"
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "absl/base/log_severity.h"
+#include "absl/flags/flag.h"
+#include "absl/log/flags.h"
+#include "absl/log/log.h"
+#include "absl/log/scoped_mock_log.h"
+#include "absl/types/optional.h"
+
+namespace {
+
+using ::testing::_;
+
+absl::optional<int> MaxLogVerbosity() {
+#ifdef ABSL_MAX_VLOG_VERBOSITY
+  return ABSL_MAX_VLOG_VERBOSITY;
+#else
+  return absl::nullopt;
+#endif
+}
+
+absl::optional<int> MinLogLevel() {
+#ifdef ABSL_MIN_LOG_LEVEL
+  return static_cast<int>(ABSL_MIN_LOG_LEVEL);
+#else
+  return absl::nullopt;
+#endif
+}
+
+TEST(VLogIsOn, GlobalWorksWithoutMaxVerbosityAndMinLogLevel) {
+  if (MaxLogVerbosity().has_value() || MinLogLevel().has_value()) {
+    GTEST_SKIP();
+  }
+
+  absl::SetGlobalVLogLevel(3);
+  absl::ScopedMockLog log(absl::MockLogDefault::kDisallowUnexpected);
+
+  EXPECT_CALL(log, Log(absl::LogSeverity::kInfo, _, "important"));
+
+  log.StartCapturingLogs();
+  VLOG(3) << "important";
+  VLOG(4) << "spam";
+}
+
+TEST(VLogIsOn, FileWorksWithoutMaxVerbosityAndMinLogLevel) {
+  if (MaxLogVerbosity().has_value() || MinLogLevel().has_value()) {
+    GTEST_SKIP();
+  }
+
+  absl::SetVLogLevel("vlog_is_on_test", 3);
+  absl::ScopedMockLog log(absl::MockLogDefault::kDisallowUnexpected);
+
+  EXPECT_CALL(log, Log(absl::LogSeverity::kInfo, _, "important"));
+
+  log.StartCapturingLogs();
+  VLOG(3) << "important";
+  VLOG(4) << "spam";
+}
+
+TEST(VLogIsOn, PatternWorksWithoutMaxVerbosityAndMinLogLevel) {
+  if (MaxLogVerbosity().has_value() || MinLogLevel().has_value()) {
+    GTEST_SKIP();
+  }
+
+  absl::SetVLogLevel("vlog_is_on*", 3);
+  absl::ScopedMockLog log(absl::MockLogDefault::kDisallowUnexpected);
+
+  EXPECT_CALL(log, Log(absl::LogSeverity::kInfo, _, "important"));
+
+  log.StartCapturingLogs();
+  VLOG(3) << "important";
+  VLOG(4) << "spam";
+}
+
+TEST(VLogIsOn, GlobalDoesNotFilterBelowMaxVerbosity) {
+  if (!MaxLogVerbosity().has_value() || *MaxLogVerbosity() < 2) {
+    GTEST_SKIP();
+  }
+
+  // Set an arbitrary high value to avoid filtering VLOGs in tests by default.
+  absl::SetGlobalVLogLevel(1000);
+  absl::ScopedMockLog log(absl::MockLogDefault::kDisallowUnexpected);
+
+  EXPECT_CALL(log, Log(absl::LogSeverity::kInfo, _, "asdf"));
+
+  log.StartCapturingLogs();
+  VLOG(2) << "asdf";
+}
+
+TEST(VLogIsOn, FileDoesNotFilterBelowMaxVerbosity) {
+  if (!MaxLogVerbosity().has_value() || *MaxLogVerbosity() < 2) {
+    GTEST_SKIP();
+  }
+
+  // Set an arbitrary high value to avoid filtering VLOGs in tests by default.
+  absl::SetVLogLevel("vlog_is_on_test", 1000);
+  absl::ScopedMockLog log(absl::MockLogDefault::kDisallowUnexpected);
+
+  EXPECT_CALL(log, Log(absl::LogSeverity::kInfo, _, "asdf"));
+
+  log.StartCapturingLogs();
+  VLOG(2) << "asdf";
+}
+
+TEST(VLogIsOn, PatternDoesNotFilterBelowMaxVerbosity) {
+  if (!MaxLogVerbosity().has_value() || *MaxLogVerbosity() < 2) {
+    GTEST_SKIP();
+  }
+
+  // Set an arbitrary high value to avoid filtering VLOGs in tests by default.
+  absl::SetVLogLevel("vlog_is_on*", 1000);
+  absl::ScopedMockLog log(absl::MockLogDefault::kDisallowUnexpected);
+
+  EXPECT_CALL(log, Log(absl::LogSeverity::kInfo, _, "asdf"));
+
+  log.StartCapturingLogs();
+  VLOG(2) << "asdf";
+}
+
+TEST(VLogIsOn, GlobalFiltersAboveMaxVerbosity) {
+  if (!MaxLogVerbosity().has_value() || *MaxLogVerbosity() >= 4) {
+    GTEST_SKIP();
+  }
+
+  // Set an arbitrary high value to avoid filtering VLOGs in tests by default.
+  absl::SetGlobalVLogLevel(1000);
+  absl::ScopedMockLog log(absl::MockLogDefault::kDisallowUnexpected);
+
+  log.StartCapturingLogs();
+  VLOG(4) << "dfgh";
+}
+
+TEST(VLogIsOn, FileFiltersAboveMaxVerbosity) {
+  if (!MaxLogVerbosity().has_value() || *MaxLogVerbosity() >= 4) {
+    GTEST_SKIP();
+  }
+
+  // Set an arbitrary high value to avoid filtering VLOGs in tests by default.
+  absl::SetVLogLevel("vlog_is_on_test", 1000);
+  absl::ScopedMockLog log(absl::MockLogDefault::kDisallowUnexpected);
+
+  log.StartCapturingLogs();
+  VLOG(4) << "dfgh";
+}
+
+TEST(VLogIsOn, PatternFiltersAboveMaxVerbosity) {
+  if (!MaxLogVerbosity().has_value() || *MaxLogVerbosity() >= 4) {
+    GTEST_SKIP();
+  }
+
+  // Set an arbitrary high value to avoid filtering VLOGs in tests by default.
+  absl::SetVLogLevel("vlog_is_on*", 1000);
+  absl::ScopedMockLog log(absl::MockLogDefault::kDisallowUnexpected);
+
+  log.StartCapturingLogs();
+  VLOG(4) << "dfgh";
+}
+
+}  // namespace
diff --git a/absl/status/BUILD.bazel b/absl/status/BUILD.bazel
index 3cf0a5e..a5fad4d 100644
--- a/absl/status/BUILD.bazel
+++ b/absl/status/BUILD.bazel
@@ -54,6 +54,7 @@
         "//absl/base:config",
         "//absl/base:core_headers",
         "//absl/base:no_destructor",
+        "//absl/base:nullability",
         "//absl/base:raw_logging_internal",
         "//absl/base:strerror",
         "//absl/container:inlined_vector",
@@ -100,6 +101,7 @@
         "//absl/base",
         "//absl/base:config",
         "//absl/base:core_headers",
+        "//absl/base:nullability",
         "//absl/base:raw_logging_internal",
         "//absl/meta:type_traits",
         "//absl/strings",
diff --git a/absl/status/CMakeLists.txt b/absl/status/CMakeLists.txt
index da78990..d9d1683 100644
--- a/absl/status/CMakeLists.txt
+++ b/absl/status/CMakeLists.txt
@@ -37,6 +37,7 @@
     absl::inlined_vector
     absl::memory
     absl::no_destructor
+    absl::nullability
     absl::optional
     absl::raw_logging_internal
     absl::span
@@ -76,6 +77,7 @@
     absl::base
     absl::config
     absl::core_headers
+    absl::nullability
     absl::raw_logging_internal
     absl::status
     absl::strings
diff --git a/absl/status/internal/status_internal.cc b/absl/status/internal/status_internal.cc
index 2307579..a915675 100644
--- a/absl/status/internal/status_internal.cc
+++ b/absl/status/internal/status_internal.cc
@@ -27,6 +27,7 @@
 #include "absl/base/attributes.h"
 #include "absl/base/config.h"
 #include "absl/base/macros.h"
+#include "absl/base/nullability.h"
 #include "absl/debugging/stacktrace.h"
 #include "absl/debugging/symbolize.h"
 #include "absl/memory/memory.h"
@@ -187,7 +188,7 @@
   return true;
 }
 
-StatusRep* StatusRep::CloneAndUnref() const {
+absl::Nonnull<StatusRep*> StatusRep::CloneAndUnref() const {
   // Optimization: no need to create a clone if we already have a refcount of 1.
   if (ref_.load(std::memory_order_acquire) == 1) {
     // All StatusRep instances are heap allocated and mutable, therefore this
@@ -233,8 +234,9 @@
   }
 }
 
-std::string* MakeCheckFailString(const absl::Status* status,
-                                 const char* prefix) {
+absl::Nonnull<std::string*> MakeCheckFailString(
+    absl::Nonnull<const absl::Status*> status,
+    absl::Nonnull<const char*> prefix) {
   return new std::string(
       absl::StrCat(prefix, " (",
                    status->ToString(StatusToStringMode::kWithEverything), ")"));
diff --git a/absl/status/internal/status_internal.h b/absl/status/internal/status_internal.h
index 7655709..c9f4383 100644
--- a/absl/status/internal/status_internal.h
+++ b/absl/status/internal/status_internal.h
@@ -22,6 +22,7 @@
 
 #include "absl/base/attributes.h"
 #include "absl/base/config.h"
+#include "absl/base/nullability.h"
 #include "absl/container/inlined_vector.h"
 #include "absl/strings/cord.h"
 #include "absl/strings/string_view.h"
@@ -99,7 +100,7 @@
   // Returns an equivalent heap allocated StatusRep with refcount 1.
   //
   // `this` is not safe to be used after calling as it may have been deleted.
-  StatusRep* CloneAndUnref() const;
+  absl::Nonnull<StatusRep*> CloneAndUnref() const;
 
  private:
   mutable std::atomic<int32_t> ref_;
@@ -118,8 +119,10 @@
 // suitable for output as an error message in assertion/`CHECK()` failures.
 //
 // This is an internal implementation detail for Abseil logging.
-std::string* MakeCheckFailString(const absl::Status* status,
-                                 const char* prefix);
+ABSL_ATTRIBUTE_PURE_FUNCTION
+absl::Nonnull<std::string*> MakeCheckFailString(
+    absl::Nonnull<const absl::Status*> status,
+    absl::Nonnull<const char*> prefix);
 
 }  // namespace status_internal
 
diff --git a/absl/status/internal/statusor_internal.h b/absl/status/internal/statusor_internal.h
index 49cead7..25c1147 100644
--- a/absl/status/internal/statusor_internal.h
+++ b/absl/status/internal/statusor_internal.h
@@ -18,6 +18,7 @@
 #include <utility>
 
 #include "absl/base/attributes.h"
+#include "absl/base/nullability.h"
 #include "absl/meta/type_traits.h"
 #include "absl/status/status.h"
 #include "absl/utility/utility.h"
@@ -123,7 +124,7 @@
 class Helper {
  public:
   // Move type-agnostic error handling to the .cc.
-  static void HandleInvalidStatusCtorArg(Status*);
+  static void HandleInvalidStatusCtorArg(absl::Nonnull<Status*>);
   ABSL_ATTRIBUTE_NORETURN static void Crash(const absl::Status& status);
 };
 
@@ -131,7 +132,8 @@
 // the constructor.
 // This abstraction is here mostly for the gcc performance fix.
 template <typename T, typename... Args>
-ABSL_ATTRIBUTE_NONNULL(1) void PlacementNew(void* p, Args&&... args) {
+ABSL_ATTRIBUTE_NONNULL(1)
+void PlacementNew(absl::Nonnull<void*> p, Args&&... args) {
   new (p) T(std::forward<Args>(args)...);
 }
 
diff --git a/absl/status/status.cc b/absl/status/status.cc
index 7c778ac..4dd5ae0 100644
--- a/absl/status/status.cc
+++ b/absl/status/status.cc
@@ -29,6 +29,7 @@
 #include "absl/base/internal/strerror.h"
 #include "absl/base/macros.h"
 #include "absl/base/no_destructor.h"
+#include "absl/base/nullability.h"
 #include "absl/debugging/stacktrace.h"
 #include "absl/debugging/symbolize.h"
 #include "absl/status/internal/status_internal.h"
@@ -90,7 +91,7 @@
   return os << StatusCodeToString(code);
 }
 
-const std::string* Status::EmptyString() {
+absl::Nonnull<const std::string*> Status::EmptyString() {
   static const absl::NoDestructor<std::string> kEmpty;
   return kEmpty.get();
 }
@@ -99,7 +100,7 @@
 constexpr const char Status::kMovedFromString[];
 #endif
 
-const std::string* Status::MovedFromString() {
+absl::Nonnull<const std::string*> Status::MovedFromString() {
   static const absl::NoDestructor<std::string> kMovedFrom(kMovedFromString);
   return kMovedFrom.get();
 }
@@ -111,7 +112,8 @@
   }
 }
 
-status_internal::StatusRep* Status::PrepareToModify(uintptr_t rep) {
+absl::Nonnull<status_internal::StatusRep*> Status::PrepareToModify(
+    uintptr_t rep) {
   if (IsInlined(rep)) {
     return new status_internal::StatusRep(InlinedRepToCode(rep),
                                           absl::string_view(), nullptr);
@@ -412,7 +414,7 @@
                 MessageForErrnoToStatus(error_number, message));
 }
 
-const char* StatusMessageAsCStr(const Status& status) {
+absl::Nonnull<const char*> StatusMessageAsCStr(const Status& status) {
   // As an internal implementation detail, we guarantee that if status.message()
   // is non-empty, then the resulting string_view is null terminated.
   auto sv_message = status.message();
diff --git a/absl/status/status.h b/absl/status/status.h
index d4f7414..9ce16db 100644
--- a/absl/status/status.h
+++ b/absl/status/status.h
@@ -60,6 +60,7 @@
 #include "absl/base/attributes.h"
 #include "absl/base/config.h"
 #include "absl/base/macros.h"
+#include "absl/base/nullability.h"
 #include "absl/base/optimization.h"
 #include "absl/functional/function_ref.h"
 #include "absl/status/internal/status_internal.h"
@@ -622,14 +623,15 @@
 
   // REQUIRES: !ok()
   // Ensures rep is not inlined or shared with any other Status.
-  static status_internal::StatusRep* PrepareToModify(uintptr_t rep);
+  static absl::Nonnull<status_internal::StatusRep*> PrepareToModify(
+      uintptr_t rep);
 
   // MSVC 14.0 limitation requires the const.
   static constexpr const char kMovedFromString[] =
       "Status accessed after move.";
 
-  static const std::string* EmptyString();
-  static const std::string* MovedFromString();
+  static absl::Nonnull<const std::string*> EmptyString();
+  static absl::Nonnull<const std::string*> MovedFromString();
 
   // Returns whether rep contains an inlined representation.
   // See rep_ for details.
@@ -648,7 +650,8 @@
   // Converts between StatusRep* and the external uintptr_t representation used
   // by rep_. See rep_ for details.
   static uintptr_t PointerToRep(status_internal::StatusRep* r);
-  static const status_internal::StatusRep* RepToPointer(uintptr_t r);
+  static absl::Nonnull<const status_internal::StatusRep*> RepToPointer(
+      uintptr_t r);
 
   static std::string ToStringSlow(uintptr_t rep, StatusToStringMode mode);
 
@@ -899,12 +902,14 @@
   return CodeToInlinedRep(absl::StatusCode::kInternal) | 2;
 }
 
-inline const status_internal::StatusRep* Status::RepToPointer(uintptr_t rep) {
+inline absl::Nonnull<const status_internal::StatusRep*> Status::RepToPointer(
+    uintptr_t rep) {
   assert(!IsInlined(rep));
   return reinterpret_cast<const status_internal::StatusRep*>(rep);
 }
 
-inline uintptr_t Status::PointerToRep(status_internal::StatusRep* rep) {
+inline uintptr_t Status::PointerToRep(
+    absl::Nonnull<status_internal::StatusRep*> rep) {
   return reinterpret_cast<uintptr_t>(rep);
 }
 
@@ -929,7 +934,7 @@
 // If the status's message is empty, the empty string is returned.
 //
 // StatusMessageAsCStr exists for C support. Use `status.message()` in C++.
-const char* StatusMessageAsCStr(
+absl::Nonnull<const char*> StatusMessageAsCStr(
     const Status& status ABSL_ATTRIBUTE_LIFETIME_BOUND);
 
 ABSL_NAMESPACE_END
diff --git a/absl/status/status_payload_printer.h b/absl/status/status_payload_printer.h
index 5e0937f..f22255e 100644
--- a/absl/status/status_payload_printer.h
+++ b/absl/status/status_payload_printer.h
@@ -16,6 +16,7 @@
 
 #include <string>
 
+#include "absl/base/nullability.h"
 #include "absl/strings/cord.h"
 #include "absl/strings/string_view.h"
 #include "absl/types/optional.h"
@@ -34,8 +35,8 @@
 // NOTE: This is an internal API and the design is subject to change in the
 // future in a non-backward-compatible way. Since it's only meant for debugging
 // purpose, you should not rely on it in any critical logic.
-using StatusPayloadPrinter = absl::optional<std::string> (*)(absl::string_view,
-                                                             const absl::Cord&);
+using StatusPayloadPrinter = absl::Nullable<absl::optional<std::string> (*)(
+    absl::string_view, const absl::Cord&)>;
 
 // Sets the global payload printer. Only one printer should be set per process.
 // If multiple printers are set, it's undefined which one will be used.
diff --git a/absl/status/statusor.cc b/absl/status/statusor.cc
index bfad75e..7e6b334 100644
--- a/absl/status/statusor.cc
+++ b/absl/status/statusor.cc
@@ -19,6 +19,7 @@
 #include "absl/base/call_once.h"
 #include "absl/base/config.h"
 #include "absl/base/internal/raw_logging.h"
+#include "absl/base/nullability.h"
 #include "absl/status/internal/statusor_internal.h"
 #include "absl/status/status.h"
 #include "absl/strings/str_cat.h"
@@ -54,7 +55,7 @@
 BadStatusOrAccess::BadStatusOrAccess(BadStatusOrAccess&& other)
     : status_(std::move(other.status_)) {}
 
-const char* BadStatusOrAccess::what() const noexcept {
+absl::Nonnull<const char*> BadStatusOrAccess::what() const noexcept {
   InitWhat();
   return what_.c_str();
 }
@@ -69,7 +70,7 @@
 
 namespace internal_statusor {
 
-void Helper::HandleInvalidStatusCtorArg(absl::Status* status) {
+void Helper::HandleInvalidStatusCtorArg(absl::Nonnull<absl::Status*> status) {
   const char* kMessage =
       "An OK status is not a valid constructor argument to StatusOr<T>";
 #ifdef NDEBUG
diff --git a/absl/status/statusor.h b/absl/status/statusor.h
index 54c7ce0..33a2cf3 100644
--- a/absl/status/statusor.h
+++ b/absl/status/statusor.h
@@ -44,6 +44,7 @@
 #include <utility>
 
 #include "absl/base/attributes.h"
+#include "absl/base/nullability.h"
 #include "absl/base/call_once.h"
 #include "absl/meta/type_traits.h"
 #include "absl/status/internal/statusor_internal.h"
@@ -88,7 +89,7 @@
   //
   // The pointer of this string is guaranteed to be valid until any non-const
   // function is invoked on the exception object.
-  const char* what() const noexcept override;
+  absl::Nonnull<const char*> what() const noexcept override;
 
   // BadStatusOrAccess::status()
   //
@@ -750,13 +751,13 @@
 }
 
 template <typename T>
-const T* StatusOr<T>::operator->() const {
+absl::Nonnull<const T*> StatusOr<T>::operator->() const {
   this->EnsureOk();
   return &this->data_;
 }
 
 template <typename T>
-T* StatusOr<T>::operator->() {
+absl::Nonnull<T*> StatusOr<T>::operator->() {
   this->EnsureOk();
   return &this->data_;
 }
diff --git a/absl/strings/BUILD.bazel b/absl/strings/BUILD.bazel
index a3ef3ae..3a244b5 100644
--- a/absl/strings/BUILD.bazel
+++ b/absl/strings/BUILD.bazel
@@ -41,6 +41,7 @@
         "//absl/base",
         "//absl/base:config",
         "//absl/base:core_headers",
+        "//absl/base:nullability",
         "//absl/base:throw_delegate",
     ],
 )
@@ -103,6 +104,7 @@
         "//absl/base:config",
         "//absl/base:core_headers",
         "//absl/base:endian",
+        "//absl/base:nullability",
         "//absl/base:raw_logging_internal",
         "//absl/base:throw_delegate",
         "//absl/memory",
@@ -588,6 +590,7 @@
         "//absl/base:config",
         "//absl/base:core_headers",
         "//absl/base:endian",
+        "//absl/base:nullability",
         "//absl/base:raw_logging_internal",
         "//absl/container:inlined_vector",
         "//absl/crc:crc32c",
@@ -869,6 +872,7 @@
         ":strings",
         "//absl/base:config",
         "//absl/base:core_headers",
+        "//absl/base:nullability",
         "@com_google_googletest//:gtest",
     ],
 )
@@ -1247,6 +1251,11 @@
     linkopts = ABSL_DEFAULT_LINKOPTS,
     deps = [
         ":str_format_internal",
+        ":string_view",
+        "//absl/base:config",
+        "//absl/base:core_headers",
+        "//absl/base:nullability",
+        "//absl/types:span",
     ],
 )
 
@@ -1277,6 +1286,7 @@
         ":strings",
         "//absl/base:config",
         "//absl/base:core_headers",
+        "//absl/container:fixed_array",
         "//absl/container:inlined_vector",
         "//absl/functional:function_ref",
         "//absl/meta:type_traits",
@@ -1330,6 +1340,7 @@
     deps = [
         ":str_format",
         ":str_format_internal",
+        "//absl/base:config",
         "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
@@ -1366,12 +1377,16 @@
     copts = ABSL_TEST_COPTS,
     visibility = ["//visibility:private"],
     deps = [
+        ":str_format",
         ":str_format_internal",
         ":strings",
+        "//absl/base:config",
         "//absl/base:core_headers",
         "//absl/base:raw_logging_internal",
         "//absl/log",
+        "//absl/numeric:int128",
         "//absl/types:optional",
+        "//absl/types:span",
         "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
@@ -1397,6 +1412,8 @@
     visibility = ["//visibility:private"],
     deps = [
         ":str_format_internal",
+        ":string_view",
+        "//absl/base:config",
         "//absl/base:core_headers",
         "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
diff --git a/absl/strings/CMakeLists.txt b/absl/strings/CMakeLists.txt
index b129096..d4116a8 100644
--- a/absl/strings/CMakeLists.txt
+++ b/absl/strings/CMakeLists.txt
@@ -27,6 +27,7 @@
     absl::base
     absl::config
     absl::core_headers
+    absl::nullability
     absl::throw_delegate
   PUBLIC
 )
@@ -84,6 +85,7 @@
     absl::endian
     absl::int128
     absl::memory
+    absl::nullability
     absl::raw_logging_internal
     absl::throw_delegate
     absl::type_traits
@@ -470,7 +472,12 @@
   COPTS
     ${ABSL_DEFAULT_COPTS}
   DEPS
+    absl::config
+    absl::core_headers
+    absl::nullability
+    absl::span
     absl::str_format_internal
+    absl::string_view
   PUBLIC
 )
 
@@ -501,6 +508,7 @@
     absl::strings
     absl::config
     absl::core_headers
+    absl::fixed_array
     absl::inlined_vector
     absl::numeric_representation
     absl::type_traits
@@ -548,6 +556,7 @@
   COPTS
     ${ABSL_TEST_COPTS}
   DEPS
+    absl::config
     absl::str_format
     absl::str_format_internal
     GTest::gmock_main
@@ -585,12 +594,15 @@
   COPTS
     ${ABSL_TEST_COPTS}
   DEPS
-    absl::strings
-    absl::str_format_internal
+    absl::config
     absl::core_headers
+    absl::int128
     absl::log
     absl::raw_logging_internal
-    absl::int128
+    absl::span
+    absl::str_format
+    absl::str_format_internal
+    absl::strings
     GTest::gmock_main
 )
 
@@ -616,6 +628,8 @@
     ${ABSL_TEST_COPTS}
   DEPS
     absl::str_format_internal
+    absl::string_view
+    absl::config
     absl::core_headers
     GTest::gmock_main
 )
@@ -975,6 +989,7 @@
     absl::endian
     absl::function_ref
     absl::inlined_vector
+    absl::nullability
     absl::optional
     absl::raw_logging_internal
     absl::span
@@ -1032,6 +1047,7 @@
     absl::cordz_statistics
     absl::cordz_update_tracker
     absl::core_headers
+    absl::nullability
     absl::strings
   TESTONLY
 )
diff --git a/absl/strings/ascii.cc b/absl/strings/ascii.cc
index 1e6566e..df905e0 100644
--- a/absl/strings/ascii.cc
+++ b/absl/strings/ascii.cc
@@ -19,6 +19,7 @@
 #include <string>
 
 #include "absl/base/config.h"
+#include "absl/base/nullability.h"
 
 namespace absl {
 ABSL_NAMESPACE_BEGIN
@@ -174,7 +175,8 @@
 }
 
 template <bool ToUpper>
-constexpr void AsciiStrCaseFold(char* p, char* end) {
+constexpr void AsciiStrCaseFold(absl::Nonnull<char*> p,
+                                absl::Nonnull<char*> end) {
   // The upper- and lowercase versions of ASCII characters differ by only 1 bit.
   // When we need to flip the case, we can xor with this bit to achieve the
   // desired result. Note that the choice of 'a' and 'A' here is arbitrary. We
@@ -215,17 +217,17 @@
 
 }  // namespace ascii_internal
 
-void AsciiStrToLower(std::string* s) {
+void AsciiStrToLower(absl::Nonnull<std::string*> s) {
   char* p = &(*s)[0];  // Guaranteed to be valid for empty strings
   return ascii_internal::AsciiStrCaseFold<false>(p, p + s->size());
 }
 
-void AsciiStrToUpper(std::string* s) {
+void AsciiStrToUpper(absl::Nonnull<std::string*> s) {
   char* p = &(*s)[0];  // Guaranteed to be valid for empty strings
   return ascii_internal::AsciiStrCaseFold<true>(p, p + s->size());
 }
 
-void RemoveExtraAsciiWhitespace(std::string* str) {
+void RemoveExtraAsciiWhitespace(absl::Nonnull<std::string*> str) {
   auto stripped = StripAsciiWhitespace(*str);
 
   if (stripped.empty()) {
diff --git a/absl/strings/ascii.h b/absl/strings/ascii.h
index ba6679b..c238f4d 100644
--- a/absl/strings/ascii.h
+++ b/absl/strings/ascii.h
@@ -58,6 +58,7 @@
 
 #include "absl/base/attributes.h"
 #include "absl/base/config.h"
+#include "absl/base/nullability.h"
 #include "absl/strings/string_view.h"
 
 namespace absl {
@@ -166,7 +167,7 @@
 }
 
 // Converts the characters in `s` to lowercase, changing the contents of `s`.
-void AsciiStrToLower(std::string* s);
+void AsciiStrToLower(absl::Nonnull<std::string*> s);
 
 // Creates a lowercase string from a given absl::string_view.
 ABSL_MUST_USE_RESULT inline std::string AsciiStrToLower(absl::string_view s) {
@@ -184,7 +185,7 @@
 }
 
 // Converts the characters in `s` to uppercase, changing the contents of `s`.
-void AsciiStrToUpper(std::string* s);
+void AsciiStrToUpper(absl::Nonnull<std::string*> s);
 
 // Creates an uppercase string from a given absl::string_view.
 ABSL_MUST_USE_RESULT inline std::string AsciiStrToUpper(absl::string_view s) {
@@ -202,7 +203,7 @@
 }
 
 // Strips in place whitespace from the beginning of the given string.
-inline void StripLeadingAsciiWhitespace(std::string* str) {
+inline void StripLeadingAsciiWhitespace(absl::Nonnull<std::string*> str) {
   auto it = std::find_if_not(str->begin(), str->end(), absl::ascii_isspace);
   str->erase(str->begin(), it);
 }
@@ -216,7 +217,7 @@
 }
 
 // Strips in place whitespace from the end of the given string
-inline void StripTrailingAsciiWhitespace(std::string* str) {
+inline void StripTrailingAsciiWhitespace(absl::Nonnull<std::string*> str) {
   auto it = std::find_if_not(str->rbegin(), str->rend(), absl::ascii_isspace);
   str->erase(static_cast<size_t>(str->rend() - it));
 }
@@ -229,13 +230,13 @@
 }
 
 // Strips in place whitespace from both ends of the given string
-inline void StripAsciiWhitespace(std::string* str) {
+inline void StripAsciiWhitespace(absl::Nonnull<std::string*> str) {
   StripTrailingAsciiWhitespace(str);
   StripLeadingAsciiWhitespace(str);
 }
 
 // Removes leading, trailing, and consecutive internal whitespace.
-void RemoveExtraAsciiWhitespace(std::string*);
+void RemoveExtraAsciiWhitespace(absl::Nonnull<std::string*> str);
 
 ABSL_NAMESPACE_END
 }  // namespace absl
diff --git a/absl/strings/charconv.cc b/absl/strings/charconv.cc
index 60b3715..eda6d50 100644
--- a/absl/strings/charconv.cc
+++ b/absl/strings/charconv.cc
@@ -23,6 +23,7 @@
 
 #include "absl/base/casts.h"
 #include "absl/base/config.h"
+#include "absl/base/nullability.h"
 #include "absl/numeric/bits.h"
 #include "absl/numeric/int128.h"
 #include "absl/strings/internal/charconv_bigint.h"
@@ -119,7 +120,7 @@
   // Parsing a smaller N will produce something finite.
   static constexpr int kEiselLemireMaxExclusiveExp10 = 309;
 
-  static double MakeNan(const char* tagp) {
+  static double MakeNan(absl::Nullable<const char*> tagp) {
 #if ABSL_HAVE_BUILTIN(__builtin_nan)
     // Use __builtin_nan() if available since it has a fix for
     // https://bugs.llvm.org/show_bug.cgi?id=37778
@@ -192,7 +193,7 @@
   static constexpr int kEiselLemireMinInclusiveExp10 = -46 - 18;
   static constexpr int kEiselLemireMaxExclusiveExp10 = 39;
 
-  static float MakeNan(const char* tagp) {
+  static float MakeNan(absl::Nullable<const char*> tagp) {
 #if ABSL_HAVE_BUILTIN(__builtin_nanf)
     // Use __builtin_nanf() if available since it has a fix for
     // https://bugs.llvm.org/show_bug.cgi?id=37778
@@ -344,7 +345,7 @@
 // `value` must be wider than the requested bit width.
 //
 // Returns the number of bits shifted.
-int TruncateToBitWidth(int bit_width, uint128* value) {
+int TruncateToBitWidth(int bit_width, absl::Nonnull<uint128*> value) {
   const int current_bit_width = BitWidth(*value);
   const int shift = current_bit_width - bit_width;
   *value >>= shift;
@@ -356,7 +357,7 @@
 // the appropriate double, and returns true.
 template <typename FloatType>
 bool HandleEdgeCase(const strings_internal::ParsedFloat& input, bool negative,
-                    FloatType* value) {
+                    absl::Nonnull<FloatType*> value) {
   if (input.type == strings_internal::FloatType::kNan) {
     // A bug in both clang < 7 and gcc would cause the compiler to optimize
     // away the buffer we are building below.  Declaring the buffer volatile
@@ -405,7 +406,8 @@
 // number is stored in *value.
 template <typename FloatType>
 void EncodeResult(const CalculatedFloat& calculated, bool negative,
-                  absl::from_chars_result* result, FloatType* value) {
+                  absl::Nonnull<absl::from_chars_result*> result,
+                  absl::Nonnull<FloatType*> value) {
   if (calculated.exponent == kOverflow) {
     result->ec = std::errc::result_out_of_range;
     *value = negative ? -std::numeric_limits<FloatType>::max()
@@ -451,7 +453,7 @@
 // Zero and negative values of `shift` are accepted, in which case the word is
 // shifted left, as necessary.
 uint64_t ShiftRightAndRound(uint128 value, int shift, bool input_exact,
-                            bool* output_exact) {
+                            absl::Nonnull<bool*> output_exact) {
   if (shift <= 0) {
     *output_exact = input_exact;
     return static_cast<uint64_t>(value << -shift);
@@ -685,7 +687,8 @@
 // this function returns false) is both fast and correct.
 template <typename FloatType>
 bool EiselLemire(const strings_internal::ParsedFloat& input, bool negative,
-                 FloatType* value, std::errc* ec) {
+                 absl::Nonnull<FloatType*> value,
+                 absl::Nonnull<std::errc*> ec) {
   uint64_t man = input.mantissa;
   int exp10 = input.exponent;
   if (exp10 < FloatTraits<FloatType>::kEiselLemireMinInclusiveExp10) {
@@ -858,7 +861,8 @@
 }
 
 template <typename FloatType>
-from_chars_result FromCharsImpl(const char* first, const char* last,
+from_chars_result FromCharsImpl(absl::Nonnull<const char*> first,
+                                absl::Nonnull<const char*> last,
                                 FloatType& value, chars_format fmt_flags) {
   from_chars_result result;
   result.ptr = first;  // overwritten on successful parse
@@ -944,12 +948,14 @@
 }
 }  // namespace
 
-from_chars_result from_chars(const char* first, const char* last, double& value,
+from_chars_result from_chars(absl::Nonnull<const char*> first,
+                             absl::Nonnull<const char*> last, double& value,
                              chars_format fmt) {
   return FromCharsImpl(first, last, value, fmt);
 }
 
-from_chars_result from_chars(const char* first, const char* last, float& value,
+from_chars_result from_chars(absl::Nonnull<const char*> first,
+                             absl::Nonnull<const char*> last, float& value,
                              chars_format fmt) {
   return FromCharsImpl(first, last, value, fmt);
 }
diff --git a/absl/strings/charconv.h b/absl/strings/charconv.h
index 111c712..be25090 100644
--- a/absl/strings/charconv.h
+++ b/absl/strings/charconv.h
@@ -18,6 +18,7 @@
 #include <system_error>  // NOLINT(build/c++11)
 
 #include "absl/base/config.h"
+#include "absl/base/nullability.h"
 
 namespace absl {
 ABSL_NAMESPACE_BEGIN
@@ -44,7 +45,7 @@
 // characters that were successfully parsed.  If none was found, `ptr` is set
 // to the `first` argument to from_chars.
 struct from_chars_result {
-  const char* ptr;
+  absl::Nonnull<const char*> ptr;
   std::errc ec;
 };
 
@@ -76,11 +77,13 @@
 // format that strtod() accepts, except that a "0x" prefix is NOT matched.
 // (In particular, in `hex` mode, the input "0xff" results in the largest
 // matching pattern "0".)
-absl::from_chars_result from_chars(const char* first, const char* last,
+absl::from_chars_result from_chars(absl::Nonnull<const char*> first,
+                                   absl::Nonnull<const char*> last,
                                    double& value,  // NOLINT
                                    chars_format fmt = chars_format::general);
 
-absl::from_chars_result from_chars(const char* first, const char* last,
+absl::from_chars_result from_chars(absl::Nonnull<const char*> first,
+                                   absl::Nonnull<const char*> last,
                                    float& value,  // NOLINT
                                    chars_format fmt = chars_format::general);
 
diff --git a/absl/strings/cord.cc b/absl/strings/cord.cc
index 08a165e..f67326f 100644
--- a/absl/strings/cord.cc
+++ b/absl/strings/cord.cc
@@ -37,6 +37,7 @@
 #include "absl/base/internal/raw_logging.h"
 #include "absl/base/macros.h"
 #include "absl/base/optimization.h"
+#include "absl/base/nullability.h"
 #include "absl/container/inlined_vector.h"
 #include "absl/crc/crc32c.h"
 #include "absl/crc/internal/crc_cord_state.h"
@@ -74,18 +75,21 @@
 using ::absl::cord_internal::kInlinedVectorSize;
 using ::absl::cord_internal::kMaxBytesToCopy;
 
-static void DumpNode(CordRep* rep, bool include_data, std::ostream* os,
-                     int indent = 0);
-static bool VerifyNode(CordRep* root, CordRep* start_node);
+static void DumpNode(absl::Nonnull<CordRep*> rep, bool include_data,
+                     absl::Nonnull<std::ostream*> os, int indent = 0);
+static bool VerifyNode(absl::Nonnull<CordRep*> root,
+                       absl::Nonnull<CordRep*> start_node);
 
-static inline CordRep* VerifyTree(CordRep* node) {
+static inline absl::Nullable<CordRep*> VerifyTree(
+    absl::Nullable<CordRep*> node) {
   assert(node == nullptr || VerifyNode(node, node));
   static_cast<void>(&VerifyNode);
   return node;
 }
 
-static CordRepFlat* CreateFlat(const char* data, size_t length,
-                               size_t alloc_hint) {
+static absl::Nonnull<CordRepFlat*> CreateFlat(absl::Nonnull<const char*> data,
+                                              size_t length,
+                                              size_t alloc_hint) {
   CordRepFlat* flat = CordRepFlat::New(length + alloc_hint);
   flat->length = length;
   memcpy(flat->Data(), data, length);
@@ -94,7 +98,8 @@
 
 // Creates a new flat or Btree out of the specified array.
 // The returned node has a refcount of 1.
-static CordRep* NewBtree(const char* data, size_t length, size_t alloc_hint) {
+static absl::Nonnull<CordRep*> NewBtree(absl::Nonnull<const char*> data,
+                                        size_t length, size_t alloc_hint) {
   if (length <= kMaxFlatLength) {
     return CreateFlat(data, length, alloc_hint);
   }
@@ -107,14 +112,16 @@
 
 // Create a new tree out of the specified array.
 // The returned node has a refcount of 1.
-static CordRep* NewTree(const char* data, size_t length, size_t alloc_hint) {
+static absl::Nullable<CordRep*> NewTree(absl::Nullable<const char*> data,
+                                        size_t length, size_t alloc_hint) {
   if (length == 0) return nullptr;
   return NewBtree(data, length, alloc_hint);
 }
 
 namespace cord_internal {
 
-void InitializeCordRepExternal(absl::string_view data, CordRepExternal* rep) {
+void InitializeCordRepExternal(absl::string_view data,
+                               absl::Nonnull<CordRepExternal*> rep) {
   assert(!data.empty());
   rep->length = data.size();
   rep->tag = EXTERNAL;
@@ -128,7 +135,7 @@
 // and not wasteful, we move the string into an external cord rep, preserving
 // the already allocated string contents.
 // Requires the provided string length to be larger than `kMaxInline`.
-static CordRep* CordRepFromString(std::string&& src) {
+static absl::Nonnull<CordRep*> CordRepFromString(std::string&& src) {
   assert(src.length() > cord_internal::kMaxInline);
   if (
       // String is short: copy data to avoid external block overhead.
@@ -160,12 +167,13 @@
 constexpr unsigned char Cord::InlineRep::kMaxInline;
 #endif
 
-inline void Cord::InlineRep::set_data(const char* data, size_t n) {
+inline void Cord::InlineRep::set_data(absl::Nonnull<const char*> data,
+                                      size_t n) {
   static_assert(kMaxInline == 15, "set_data is hard-coded for a length of 15");
   data_.set_inline_data(data, n);
 }
 
-inline char* Cord::InlineRep::set_data(size_t n) {
+inline absl::Nonnull<char*> Cord::InlineRep::set_data(size_t n) {
   assert(n <= kMaxInline);
   ResetToEmpty();
   set_inline_size(n);
@@ -189,13 +197,13 @@
 
 // Returns `rep` converted into a CordRepBtree.
 // Directly returns `rep` if `rep` is already a CordRepBtree.
-static CordRepBtree* ForceBtree(CordRep* rep) {
+static absl::Nonnull<CordRepBtree*> ForceBtree(CordRep* rep) {
   return rep->IsBtree()
              ? rep->btree()
              : CordRepBtree::Create(cord_internal::RemoveCrcNode(rep));
 }
 
-void Cord::InlineRep::AppendTreeToInlined(CordRep* tree,
+void Cord::InlineRep::AppendTreeToInlined(absl::Nonnull<CordRep*> tree,
                                           MethodIdentifier method) {
   assert(!is_tree());
   if (!data_.is_empty()) {
@@ -205,14 +213,16 @@
   EmplaceTree(tree, method);
 }
 
-void Cord::InlineRep::AppendTreeToTree(CordRep* tree, MethodIdentifier method) {
+void Cord::InlineRep::AppendTreeToTree(absl::Nonnull<CordRep*> tree,
+                                       MethodIdentifier method) {
   assert(is_tree());
   const CordzUpdateScope scope(data_.cordz_info(), method);
   tree = CordRepBtree::Append(ForceBtree(data_.as_tree()), tree);
   SetTree(tree, scope);
 }
 
-void Cord::InlineRep::AppendTree(CordRep* tree, MethodIdentifier method) {
+void Cord::InlineRep::AppendTree(absl::Nonnull<CordRep*> tree,
+                                 MethodIdentifier method) {
   assert(tree != nullptr);
   assert(tree->length != 0);
   assert(!tree->IsCrc());
@@ -223,7 +233,7 @@
   }
 }
 
-void Cord::InlineRep::PrependTreeToInlined(CordRep* tree,
+void Cord::InlineRep::PrependTreeToInlined(absl::Nonnull<CordRep*> tree,
                                            MethodIdentifier method) {
   assert(!is_tree());
   if (!data_.is_empty()) {
@@ -233,7 +243,7 @@
   EmplaceTree(tree, method);
 }
 
-void Cord::InlineRep::PrependTreeToTree(CordRep* tree,
+void Cord::InlineRep::PrependTreeToTree(absl::Nonnull<CordRep*> tree,
                                         MethodIdentifier method) {
   assert(is_tree());
   const CordzUpdateScope scope(data_.cordz_info(), method);
@@ -241,7 +251,8 @@
   SetTree(tree, scope);
 }
 
-void Cord::InlineRep::PrependTree(CordRep* tree, MethodIdentifier method) {
+void Cord::InlineRep::PrependTree(absl::Nonnull<CordRep*> tree,
+                                  MethodIdentifier method) {
   assert(tree != nullptr);
   assert(tree->length != 0);
   assert(!tree->IsCrc());
@@ -256,8 +267,9 @@
 // suitable leaf is found, the function will update the length field for all
 // nodes to account for the size increase. The append region address will be
 // written to region and the actual size increase will be written to size.
-static inline bool PrepareAppendRegion(CordRep* root, char** region,
-                                       size_t* size, size_t max_length) {
+static inline bool PrepareAppendRegion(
+    absl::Nonnull<CordRep*> root, absl::Nonnull<absl::Nullable<char*>*> region,
+    absl::Nonnull<size_t*> size, size_t max_length) {
   if (root->IsBtree() && root->refcount.IsOne()) {
     Span<char> span = root->btree()->GetAppendBuffer(max_length);
     if (!span.empty()) {
@@ -460,11 +472,11 @@
   CommitTree(root, rep, scope, method);
 }
 
-inline CordRep* Cord::TakeRep() const& {
+inline absl::Nonnull<CordRep*> Cord::TakeRep() const& {
   return CordRep::Ref(contents_.tree());
 }
 
-inline CordRep* Cord::TakeRep() && {
+inline absl::Nonnull<CordRep*> Cord::TakeRep() && {
   CordRep* rep = contents_.tree();
   contents_.clear();
   return rep;
@@ -522,7 +534,7 @@
   contents_.AppendTree(rep, CordzUpdateTracker::kAppendCord);
 }
 
-static CordRep::ExtractResult ExtractAppendBuffer(CordRep* rep,
+static CordRep::ExtractResult ExtractAppendBuffer(absl::Nonnull<CordRep*> rep,
                                                   size_t min_capacity) {
   switch (rep->tag) {
     case cord_internal::BTREE:
@@ -769,8 +781,9 @@
   return static_cast<int>(memcmp_res > 0) - static_cast<int>(memcmp_res < 0);
 }
 
-int CompareChunks(absl::string_view* lhs, absl::string_view* rhs,
-                  size_t* size_to_compare) {
+int CompareChunks(absl::Nonnull<absl::string_view*> lhs,
+                  absl::Nonnull<absl::string_view*> rhs,
+                  absl::Nonnull<size_t*> size_to_compare) {
   size_t compared_size = std::min(lhs->size(), rhs->size());
   assert(*size_to_compare >= compared_size);
   *size_to_compare -= compared_size;
@@ -868,7 +881,8 @@
   SetCrcCordState(std::move(state));
 }
 
-const crc_internal::CrcCordState* Cord::MaybeGetCrcCordState() const {
+absl::Nullable<const crc_internal::CrcCordState*> Cord::MaybeGetCrcCordState()
+    const {
   if (!contents_.is_tree() || !contents_.tree()->IsCrc()) {
     return nullptr;
   }
@@ -885,7 +899,8 @@
 
 inline int Cord::CompareSlowPath(absl::string_view rhs, size_t compared_size,
                                  size_t size_to_compare) const {
-  auto advance = [](Cord::ChunkIterator* it, absl::string_view* chunk) {
+  auto advance = [](absl::Nonnull<Cord::ChunkIterator*> it,
+                    absl::Nonnull<absl::string_view*> chunk) {
     if (!chunk->empty()) return true;
     ++*it;
     if (it->bytes_remaining_ == 0) return false;
@@ -915,7 +930,8 @@
 
 inline int Cord::CompareSlowPath(const Cord& rhs, size_t compared_size,
                                  size_t size_to_compare) const {
-  auto advance = [](Cord::ChunkIterator* it, absl::string_view* chunk) {
+  auto advance = [](absl::Nonnull<Cord::ChunkIterator*> it,
+                    absl::Nonnull<absl::string_view*> chunk) {
     if (!chunk->empty()) return true;
     ++*it;
     if (it->bytes_remaining_ == 0) return false;
@@ -1037,7 +1053,7 @@
   return s;
 }
 
-void CopyCordToString(const Cord& src, std::string* dst) {
+void CopyCordToString(const Cord& src, absl::Nonnull<std::string*> dst) {
   if (!src.contents_.is_tree()) {
     src.contents_.CopyTo(dst);
   } else {
@@ -1046,7 +1062,7 @@
   }
 }
 
-void Cord::CopyToArraySlowPath(char* dst) const {
+void Cord::CopyToArraySlowPath(absl::Nonnull<char*> dst) const {
   assert(contents_.is_tree());
   absl::string_view fragment;
   if (GetFlatAux(contents_.tree(), &fragment)) {
@@ -1372,7 +1388,8 @@
   return absl::string_view(new_buffer, total_size);
 }
 
-/* static */ bool Cord::GetFlatAux(CordRep* rep, absl::string_view* fragment) {
+/* static */ bool Cord::GetFlatAux(absl::Nonnull<CordRep*> rep,
+                                   absl::Nonnull<absl::string_view*> fragment) {
   assert(rep != nullptr);
   if (rep->length == 0) {
     *fragment = absl::string_view();
@@ -1406,7 +1423,7 @@
 }
 
 /* static */ void Cord::ForEachChunkAux(
-    absl::cord_internal::CordRep* rep,
+    absl::Nonnull<absl::cord_internal::CordRep*> rep,
     absl::FunctionRef<void(absl::string_view)> callback) {
   assert(rep != nullptr);
   if (rep->length == 0) return;
@@ -1431,8 +1448,8 @@
   }
 }
 
-static void DumpNode(CordRep* rep, bool include_data, std::ostream* os,
-                     int indent) {
+static void DumpNode(absl::Nonnull<CordRep*> rep, bool include_data,
+                     absl::Nonnull<std::ostream*> os, int indent) {
   const int kIndentStep = 1;
   absl::InlinedVector<CordRep*, kInlinedVectorSize> stack;
   absl::InlinedVector<int, kInlinedVectorSize> indents;
@@ -1482,15 +1499,17 @@
   ABSL_INTERNAL_CHECK(indents.empty(), "");
 }
 
-static std::string ReportError(CordRep* root, CordRep* node) {
+static std::string ReportError(absl::Nonnull<CordRep*> root,
+                               absl::Nonnull<CordRep*> node) {
   std::ostringstream buf;
   buf << "Error at node " << node << " in:";
   DumpNode(root, true, &buf);
   return buf.str();
 }
 
-static bool VerifyNode(CordRep* root, CordRep* start_node) {
-  absl::InlinedVector<CordRep*, 2> worklist;
+static bool VerifyNode(absl::Nonnull<CordRep*> root,
+                       absl::Nonnull<CordRep*> start_node) {
+  absl::InlinedVector<absl::Nonnull<CordRep*>, 2> worklist;
   worklist.push_back(start_node);
   do {
     CordRep* node = worklist.back();
diff --git a/absl/strings/cord.h b/absl/strings/cord.h
index e8bb4f7..b3e556b 100644
--- a/absl/strings/cord.h
+++ b/absl/strings/cord.h
@@ -74,6 +74,7 @@
 #include "absl/base/internal/endian.h"
 #include "absl/base/internal/per_thread_tls.h"
 #include "absl/base/macros.h"
+#include "absl/base/nullability.h"
 #include "absl/base/port.h"
 #include "absl/container/inlined_vector.h"
 #include "absl/crc/internal/crc_cord_state.h"
@@ -102,7 +103,7 @@
 class CordTestPeer;
 template <typename Releaser>
 Cord MakeCordFromExternal(absl::string_view, Releaser&&);
-void CopyCordToString(const Cord& src, std::string* dst);
+void CopyCordToString(const Cord& src, absl::Nonnull<std::string*> dst);
 
 // Cord memory accounting modes
 enum class CordMemoryAccounting {
@@ -119,8 +120,8 @@
   //
   // For example:
   //   absl::Cord cord;
-  //   cord.append(some_other_cord);
-  //   cord.append(some_other_cord);
+  //   cord.Append(some_other_cord);
+  //   cord.Append(some_other_cord);
   //   // Counts `some_other_cord` twice:
   //   cord.EstimatedMemoryUsage(kTotal);
   //   // Counts `some_other_cord` once:
@@ -416,7 +417,8 @@
   // guarantee that pointers previously returned by `dst->data()` remain valid
   // even if `*dst` had enough capacity to hold `src`. If `*dst` is a new
   // object, prefer to simply use the conversion operator to `std::string`.
-  friend void CopyCordToString(const Cord& src, std::string* dst);
+  friend void CopyCordToString(const Cord& src,
+                               absl::Nonnull<std::string*> dst);
 
   class CharIterator;
 
@@ -453,7 +455,7 @@
     using iterator_category = std::input_iterator_tag;
     using value_type = absl::string_view;
     using difference_type = ptrdiff_t;
-    using pointer = const value_type*;
+    using pointer = absl::Nonnull<const value_type*>;
     using reference = value_type;
 
     ChunkIterator() = default;
@@ -473,14 +475,14 @@
     using CordRepBtree = absl::cord_internal::CordRepBtree;
     using CordRepBtreeReader = absl::cord_internal::CordRepBtreeReader;
 
-    // Constructs a `begin()` iterator from `tree`. `tree` must not be null.
-    explicit ChunkIterator(cord_internal::CordRep* tree);
+    // Constructs a `begin()` iterator from `tree`.
+    explicit ChunkIterator(absl::Nonnull<cord_internal::CordRep*> tree);
 
     // Constructs a `begin()` iterator from `cord`.
-    explicit ChunkIterator(const Cord* cord);
+    explicit ChunkIterator(absl::Nonnull<const Cord*> cord);
 
     // Initializes this instance from a tree. Invoked by constructors.
-    void InitTree(cord_internal::CordRep* tree);
+    void InitTree(absl::Nonnull<cord_internal::CordRep*> tree);
 
     // Removes `n` bytes from `current_chunk_`. Expects `n` to be smaller than
     // `current_chunk_.size()`.
@@ -498,7 +500,7 @@
     // The current leaf, or `nullptr` if the iterator points to short data.
     // If the current chunk is a substring node, current_leaf_ points to the
     // underlying flat or external node.
-    absl::cord_internal::CordRep* current_leaf_ = nullptr;
+    absl::Nullable<absl::cord_internal::CordRep*> current_leaf_ = nullptr;
     // The number of bytes left in the `Cord` over which we are iterating.
     size_t bytes_remaining_ = 0;
 
@@ -555,13 +557,13 @@
     using iterator = ChunkIterator;
     using const_iterator = ChunkIterator;
 
-    explicit ChunkRange(const Cord* cord) : cord_(cord) {}
+    explicit ChunkRange(absl::Nonnull<const Cord*> cord) : cord_(cord) {}
 
     ChunkIterator begin() const;
     ChunkIterator end() const;
 
    private:
-    const Cord* cord_;
+    absl::Nonnull<const Cord*> cord_;
   };
 
   // Cord::Chunks()
@@ -614,7 +616,7 @@
     using iterator_category = std::input_iterator_tag;
     using value_type = char;
     using difference_type = ptrdiff_t;
-    using pointer = const char*;
+    using pointer = absl::Nonnull<const char*>;
     using reference = const char&;
 
     CharIterator() = default;
@@ -629,7 +631,8 @@
     friend Cord;
 
    private:
-    explicit CharIterator(const Cord* cord) : chunk_iterator_(cord) {}
+    explicit CharIterator(absl::Nonnull<const Cord*> cord)
+        : chunk_iterator_(cord) {}
 
     ChunkIterator chunk_iterator_;
   };
@@ -640,14 +643,14 @@
   // advanced as a separate `Cord`. `n_bytes` must be less than or equal to the
   // number of bytes within the Cord; otherwise, behavior is undefined. It is
   // valid to pass `char_end()` and `0`.
-  static Cord AdvanceAndRead(CharIterator* it, size_t n_bytes);
+  static Cord AdvanceAndRead(absl::Nonnull<CharIterator*> it, size_t n_bytes);
 
   // Cord::Advance()
   //
   // Advances the `Cord::CharIterator` by `n_bytes`. `n_bytes` must be less than
   // or equal to the number of bytes remaining within the Cord; otherwise,
   // behavior is undefined. It is valid to pass `char_end()` and `0`.
-  static void Advance(CharIterator* it, size_t n_bytes);
+  static void Advance(absl::Nonnull<CharIterator*> it, size_t n_bytes);
 
   // Cord::ChunkRemaining()
   //
@@ -696,13 +699,13 @@
     using iterator = CharIterator;
     using const_iterator = CharIterator;
 
-    explicit CharRange(const Cord* cord) : cord_(cord) {}
+    explicit CharRange(absl::Nonnull<const Cord*> cord) : cord_(cord) {}
 
     CharIterator begin() const;
     CharIterator end() const;
 
    private:
-    const Cord* cord_;
+    absl::Nonnull<const Cord*> cord_;
   };
 
   // Cord::Chars()
@@ -761,7 +764,8 @@
   CharIterator Find(const absl::Cord& needle) const;
 
   // Supports absl::Cord as a sink object for absl::Format().
-  friend void AbslFormatFlush(absl::Cord* cord, absl::string_view part) {
+  friend void AbslFormatFlush(absl::Nonnull<absl::Cord*> cord,
+                              absl::string_view part) {
     cord->Append(part);
   }
 
@@ -831,7 +835,8 @@
   friend bool operator==(const Cord& lhs, const Cord& rhs);
   friend bool operator==(const Cord& lhs, absl::string_view rhs);
 
-  friend const CordzInfo* GetCordzInfoForTesting(const Cord& cord);
+  friend absl::Nullable<const CordzInfo*> GetCordzInfoForTesting(
+      const Cord& cord);
 
   // Calls the provided function once for each cord chunk, in order.  Unlike
   // Chunks(), this API will not allocate memory.
@@ -858,19 +863,22 @@
     InlineRep& operator=(const InlineRep& src);
     InlineRep& operator=(InlineRep&& src) noexcept;
 
-    explicit constexpr InlineRep(absl::string_view sv, CordRep* rep);
+    explicit constexpr InlineRep(absl::string_view sv,
+                                 absl::Nullable<CordRep*> rep);
 
-    void Swap(InlineRep* rhs);
+    void Swap(absl::Nonnull<InlineRep*> rhs);
     size_t size() const;
-    const char* data() const;  // Returns nullptr if holding pointer
-    void set_data(const char* data, size_t n);  // Discards pointer, if any
-    char* set_data(size_t n);                   // Write data to the result
+    // Returns nullptr if holding pointer
+    absl::Nullable<const char*> data() const;
+    // Discards pointer, if any
+    void set_data(absl::Nonnull<const char*> data, size_t n);
+    absl::Nonnull<char*> set_data(size_t n);  // Write data to the result
     // Returns nullptr if holding bytes
-    absl::cord_internal::CordRep* tree() const;
-    absl::cord_internal::CordRep* as_tree() const;
-    const char* as_chars() const;
+    absl::Nullable<absl::cord_internal::CordRep*> tree() const;
+    absl::Nonnull<absl::cord_internal::CordRep*> as_tree() const;
+    absl::Nonnull<const char*> as_chars() const;
     // Returns non-null iff was holding a pointer
-    absl::cord_internal::CordRep* clear();
+    absl::Nullable<absl::cord_internal::CordRep*> clear();
     // Converts to pointer if necessary.
     void reduce_size(size_t n);    // REQUIRES: holding data
     void remove_prefix(size_t n);  // REQUIRES: holding data
@@ -879,46 +887,52 @@
 
     // Creates a CordRepFlat instance from the current inlined data with `extra'
     // bytes of desired additional capacity.
-    CordRepFlat* MakeFlatWithExtraCapacity(size_t extra);
+    absl::Nonnull<CordRepFlat*> MakeFlatWithExtraCapacity(size_t extra);
 
     // Sets the tree value for this instance. `rep` must not be null.
     // Requires the current instance to hold a tree, and a lock to be held on
     // any CordzInfo referenced by this instance. The latter is enforced through
     // the CordzUpdateScope argument. If the current instance is sampled, then
     // the CordzInfo instance is updated to reference the new `rep` value.
-    void SetTree(CordRep* rep, const CordzUpdateScope& scope);
+    void SetTree(absl::Nonnull<CordRep*> rep, const CordzUpdateScope& scope);
 
     // Identical to SetTree(), except that `rep` is allowed to be null, in
     // which case the current instance is reset to an empty value.
-    void SetTreeOrEmpty(CordRep* rep, const CordzUpdateScope& scope);
+    void SetTreeOrEmpty(absl::Nullable<CordRep*> rep,
+                        const CordzUpdateScope& scope);
 
     // Sets the tree value for this instance, and randomly samples this cord.
     // This function disregards existing contents in `data_`, and should be
     // called when a Cord is 'promoted' from an 'uninitialized' or 'inlined'
     // value to a non-inlined (tree / ring) value.
-    void EmplaceTree(CordRep* rep, MethodIdentifier method);
+    void EmplaceTree(absl::Nonnull<CordRep*> rep, MethodIdentifier method);
 
     // Identical to EmplaceTree, except that it copies the parent stack from
     // the provided `parent` data if the parent is sampled.
-    void EmplaceTree(CordRep* rep, const InlineData& parent,
+    void EmplaceTree(absl::Nonnull<CordRep*> rep, const InlineData& parent,
                      MethodIdentifier method);
 
     // Commits the change of a newly created, or updated `rep` root value into
     // this cord. `old_rep` indicates the old (inlined or tree) value of the
     // cord, and determines if the commit invokes SetTree() or EmplaceTree().
-    void CommitTree(const CordRep* old_rep, CordRep* rep,
-                    const CordzUpdateScope& scope, MethodIdentifier method);
+    void CommitTree(absl::Nullable<const CordRep*> old_rep,
+                    absl::Nonnull<CordRep*> rep, const CordzUpdateScope& scope,
+                    MethodIdentifier method);
 
-    void AppendTreeToInlined(CordRep* tree, MethodIdentifier method);
-    void AppendTreeToTree(CordRep* tree, MethodIdentifier method);
-    void AppendTree(CordRep* tree, MethodIdentifier method);
-    void PrependTreeToInlined(CordRep* tree, MethodIdentifier method);
-    void PrependTreeToTree(CordRep* tree, MethodIdentifier method);
-    void PrependTree(CordRep* tree, MethodIdentifier method);
+    void AppendTreeToInlined(absl::Nonnull<CordRep*> tree,
+                             MethodIdentifier method);
+    void AppendTreeToTree(absl::Nonnull<CordRep*> tree,
+                          MethodIdentifier method);
+    void AppendTree(absl::Nonnull<CordRep*> tree, MethodIdentifier method);
+    void PrependTreeToInlined(absl::Nonnull<CordRep*> tree,
+                              MethodIdentifier method);
+    void PrependTreeToTree(absl::Nonnull<CordRep*> tree,
+                           MethodIdentifier method);
+    void PrependTree(absl::Nonnull<CordRep*> tree, MethodIdentifier method);
 
     bool IsSame(const InlineRep& other) const { return data_ == other.data_; }
 
-    void CopyTo(std::string* dst) const {
+    void CopyTo(absl::Nonnull<std::string*> dst) const {
       // memcpy is much faster when operating on a known size. On most supported
       // platforms, the small string optimization is large enough that resizing
       // to 15 bytes does not cause a memory allocation.
@@ -930,7 +944,7 @@
     }
 
     // Copies the inline contents into `dst`. Assumes the cord is not empty.
-    void CopyToArray(char* dst) const;
+    void CopyToArray(absl::Nonnull<char*> dst) const;
 
     bool is_tree() const { return data_.is_tree(); }
 
@@ -943,12 +957,12 @@
     }
 
     // Returns the profiled CordzInfo, or nullptr if not sampled.
-    absl::cord_internal::CordzInfo* cordz_info() const {
+    absl::Nullable<absl::cord_internal::CordzInfo*> cordz_info() const {
       return data_.cordz_info();
     }
 
-    // Sets the profiled CordzInfo. `cordz_info` must not be null.
-    void set_cordz_info(cord_internal::CordzInfo* cordz_info) {
+    // Sets the profiled CordzInfo.
+    void set_cordz_info(absl::Nonnull<cord_internal::CordzInfo*> cordz_info) {
       assert(cordz_info != nullptr);
       data_.set_cordz_info(cordz_info);
     }
@@ -980,19 +994,19 @@
   InlineRep contents_;
 
   // Helper for GetFlat() and TryFlat().
-  static bool GetFlatAux(absl::cord_internal::CordRep* rep,
-                         absl::string_view* fragment);
+  static bool GetFlatAux(absl::Nonnull<absl::cord_internal::CordRep*> rep,
+                         absl::Nonnull<absl::string_view*> fragment);
 
   // Helper for ForEachChunk().
   static void ForEachChunkAux(
-      absl::cord_internal::CordRep* rep,
+      absl::Nonnull<absl::cord_internal::CordRep*> rep,
       absl::FunctionRef<void(absl::string_view)> callback);
 
   // The destructor for non-empty Cords.
   void DestroyCordSlow();
 
   // Out-of-line implementation of slower parts of logic.
-  void CopyToArraySlowPath(char* dst) const;
+  void CopyToArraySlowPath(absl::Nonnull<char*> dst) const;
   int CompareSlowPath(absl::string_view rhs, size_t compared_size,
                       size_t size_to_compare) const;
   int CompareSlowPath(const Cord& rhs, size_t compared_size,
@@ -1009,8 +1023,8 @@
 
   // Returns a new reference to contents_.tree(), or steals an existing
   // reference if called on an rvalue.
-  absl::cord_internal::CordRep* TakeRep() const&;
-  absl::cord_internal::CordRep* TakeRep() &&;
+  absl::Nonnull<absl::cord_internal::CordRep*> TakeRep() const&;
+  absl::Nonnull<absl::cord_internal::CordRep*> TakeRep() &&;
 
   // Helper for Append().
   template <typename C>
@@ -1047,7 +1061,8 @@
 
   friend class CrcCord;
   void SetCrcCordState(crc_internal::CrcCordState state);
-  const crc_internal::CrcCordState* MaybeGetCrcCordState() const;
+  absl::Nullable<const crc_internal::CrcCordState*> MaybeGetCrcCordState()
+      const;
 
   CharIterator FindImpl(CharIterator it, absl::string_view needle) const;
 };
@@ -1068,13 +1083,15 @@
 
 // Does non-template-specific `CordRepExternal` initialization.
 // Requires `data` to be non-empty.
-void InitializeCordRepExternal(absl::string_view data, CordRepExternal* rep);
+void InitializeCordRepExternal(absl::string_view data,
+                               absl::Nonnull<CordRepExternal*> rep);
 
 // Creates a new `CordRep` that owns `data` and `releaser` and returns a pointer
 // to it. Requires `data` to be non-empty.
 template <typename Releaser>
 // NOLINTNEXTLINE - suppress clang-tidy raw pointer return.
-CordRep* NewExternalRep(absl::string_view data, Releaser&& releaser) {
+absl::Nonnull<CordRep*> NewExternalRep(absl::string_view data,
+                                       Releaser&& releaser) {
   assert(!data.empty());
   using ReleaserType = absl::decay_t<Releaser>;
   CordRepExternal* rep = new CordRepExternalImpl<ReleaserType>(
@@ -1086,7 +1103,7 @@
 // Overload for function reference types that dispatches using a function
 // pointer because there are no `alignof()` or `sizeof()` a function reference.
 // NOLINTNEXTLINE - suppress clang-tidy raw pointer return.
-inline CordRep* NewExternalRep(absl::string_view data,
+inline absl::Nonnull<CordRep*> NewExternalRep(absl::string_view data,
                                void (&releaser)(absl::string_view)) {
   return NewExternalRep(data, &releaser);
 }
@@ -1109,7 +1126,8 @@
   return cord;
 }
 
-constexpr Cord::InlineRep::InlineRep(absl::string_view sv, CordRep* rep)
+constexpr Cord::InlineRep::InlineRep(absl::string_view sv,
+                                     absl::Nullable<CordRep*> rep)
     : data_(sv, rep) {}
 
 inline Cord::InlineRep::InlineRep(const Cord::InlineRep& src)
@@ -1148,28 +1166,30 @@
   return *this;
 }
 
-inline void Cord::InlineRep::Swap(Cord::InlineRep* rhs) {
+inline void Cord::InlineRep::Swap(absl::Nonnull<Cord::InlineRep*> rhs) {
   if (rhs == this) {
     return;
   }
   std::swap(data_, rhs->data_);
 }
 
-inline const char* Cord::InlineRep::data() const {
+inline absl::Nullable<const char*> Cord::InlineRep::data() const {
   return is_tree() ? nullptr : data_.as_chars();
 }
 
-inline const char* Cord::InlineRep::as_chars() const {
+inline absl::Nonnull<const char*> Cord::InlineRep::as_chars() const {
   assert(!data_.is_tree());
   return data_.as_chars();
 }
 
-inline absl::cord_internal::CordRep* Cord::InlineRep::as_tree() const {
+inline absl::Nonnull<absl::cord_internal::CordRep*> Cord::InlineRep::as_tree()
+    const {
   assert(data_.is_tree());
   return data_.as_tree();
 }
 
-inline absl::cord_internal::CordRep* Cord::InlineRep::tree() const {
+inline absl::Nullable<absl::cord_internal::CordRep*> Cord::InlineRep::tree()
+    const {
   if (is_tree()) {
     return as_tree();
   } else {
@@ -1181,8 +1201,8 @@
   return is_tree() ? as_tree()->length : inline_size();
 }
 
-inline cord_internal::CordRepFlat* Cord::InlineRep::MakeFlatWithExtraCapacity(
-    size_t extra) {
+inline absl::Nonnull<cord_internal::CordRepFlat*>
+Cord::InlineRep::MakeFlatWithExtraCapacity(size_t extra) {
   static_assert(cord_internal::kMinFlatLength >= sizeof(data_), "");
   size_t len = data_.inline_size();
   auto* result = CordRepFlat::New(len + extra);
@@ -1191,20 +1211,21 @@
   return result;
 }
 
-inline void Cord::InlineRep::EmplaceTree(CordRep* rep,
+inline void Cord::InlineRep::EmplaceTree(absl::Nonnull<CordRep*> rep,
                                          MethodIdentifier method) {
   assert(rep);
   data_.make_tree(rep);
   CordzInfo::MaybeTrackCord(data_, method);
 }
 
-inline void Cord::InlineRep::EmplaceTree(CordRep* rep, const InlineData& parent,
+inline void Cord::InlineRep::EmplaceTree(absl::Nonnull<CordRep*> rep,
+                                         const InlineData& parent,
                                          MethodIdentifier method) {
   data_.make_tree(rep);
   CordzInfo::MaybeTrackCord(data_, parent, method);
 }
 
-inline void Cord::InlineRep::SetTree(CordRep* rep,
+inline void Cord::InlineRep::SetTree(absl::Nonnull<CordRep*> rep,
                                      const CordzUpdateScope& scope) {
   assert(rep);
   assert(data_.is_tree());
@@ -1212,7 +1233,7 @@
   scope.SetCordRep(rep);
 }
 
-inline void Cord::InlineRep::SetTreeOrEmpty(CordRep* rep,
+inline void Cord::InlineRep::SetTreeOrEmpty(absl::Nullable<CordRep*> rep,
                                             const CordzUpdateScope& scope) {
   assert(data_.is_tree());
   if (rep) {
@@ -1223,7 +1244,8 @@
   scope.SetCordRep(rep);
 }
 
-inline void Cord::InlineRep::CommitTree(const CordRep* old_rep, CordRep* rep,
+inline void Cord::InlineRep::CommitTree(absl::Nullable<const CordRep*> old_rep,
+                                        absl::Nonnull<CordRep*> rep,
                                         const CordzUpdateScope& scope,
                                         MethodIdentifier method) {
   if (old_rep) {
@@ -1233,7 +1255,7 @@
   }
 }
 
-inline absl::cord_internal::CordRep* Cord::InlineRep::clear() {
+inline absl::Nullable<absl::cord_internal::CordRep*> Cord::InlineRep::clear() {
   if (is_tree()) {
     CordzInfo::MaybeUntrackCord(cordz_info());
   }
@@ -1242,7 +1264,7 @@
   return result;
 }
 
-inline void Cord::InlineRep::CopyToArray(char* dst) const {
+inline void Cord::InlineRep::CopyToArray(absl::Nonnull<char*> dst) const {
   assert(!is_tree());
   size_t n = inline_size();
   assert(n != 0);
@@ -1423,7 +1445,8 @@
   return EqualsImpl(rhs, rhs_size);
 }
 
-inline void Cord::ChunkIterator::InitTree(cord_internal::CordRep* tree) {
+inline void Cord::ChunkIterator::InitTree(
+    absl::Nonnull<cord_internal::CordRep*> tree) {
   tree = cord_internal::SkipCrcNode(tree);
   if (tree->tag == cord_internal::BTREE) {
     current_chunk_ = btree_reader_.Init(tree->btree());
@@ -1433,12 +1456,13 @@
   }
 }
 
-inline Cord::ChunkIterator::ChunkIterator(cord_internal::CordRep* tree) {
+inline Cord::ChunkIterator::ChunkIterator(
+    absl::Nonnull<cord_internal::CordRep*> tree) {
   bytes_remaining_ = tree->length;
   InitTree(tree);
 }
 
-inline Cord::ChunkIterator::ChunkIterator(const Cord* cord) {
+inline Cord::ChunkIterator::ChunkIterator(absl::Nonnull<const Cord*> cord) {
   if (CordRep* tree = cord->contents_.tree()) {
     bytes_remaining_ = tree->length;
     if (ABSL_PREDICT_TRUE(bytes_remaining_ != 0)) {
@@ -1578,12 +1602,13 @@
   return chunk_iterator_->data();
 }
 
-inline Cord Cord::AdvanceAndRead(CharIterator* it, size_t n_bytes) {
+inline Cord Cord::AdvanceAndRead(absl::Nonnull<CharIterator*> it,
+                                 size_t n_bytes) {
   assert(it != nullptr);
   return it->chunk_iterator_.AdvanceAndReadBytes(n_bytes);
 }
 
-inline void Cord::Advance(CharIterator* it, size_t n_bytes) {
+inline void Cord::Advance(absl::Nonnull<CharIterator*> it, size_t n_bytes) {
   assert(it != nullptr);
   it->chunk_iterator_.AdvanceBytes(n_bytes);
 }
diff --git a/absl/strings/cord_analysis.cc b/absl/strings/cord_analysis.cc
index 5418a32..19b0fa4 100644
--- a/absl/strings/cord_analysis.cc
+++ b/absl/strings/cord_analysis.cc
@@ -20,6 +20,7 @@
 #include <unordered_set>
 
 #include "absl/base/config.h"
+#include "absl/base/nullability.h"
 #include "absl/strings/internal/cord_data_edge.h"
 #include "absl/strings/internal/cord_internal.h"
 #include "absl/strings/internal/cord_rep_btree.h"
@@ -38,13 +39,15 @@
 template <Mode mode>
 struct CordRepRef {
   // Instantiates a CordRepRef instance.
-  explicit CordRepRef(const CordRep* r) : rep(r) {}
+  explicit CordRepRef(absl::Nonnull<const CordRep*> r) : rep(r) {}
 
   // Creates a child reference holding the provided child.
   // Overloaded to add cumulative reference count for kFairShare.
-  CordRepRef Child(const CordRep* child) const { return CordRepRef(child); }
+  CordRepRef Child(absl::Nonnull<const CordRep*> child) const {
+    return CordRepRef(child);
+  }
 
-  const CordRep* rep;
+  absl::Nonnull<const CordRep*> rep;
 };
 
 // RawUsage holds the computed total number of bytes.
@@ -63,7 +66,7 @@
 struct RawUsage<Mode::kTotalMorePrecise> {
   size_t total = 0;
   // TODO(b/289250880): Replace this with a flat_hash_set.
-  std::unordered_set<const CordRep*> counted;
+  std::unordered_set<absl::Nonnull<const CordRep*>> counted;
 
   void Add(size_t size, CordRepRef<Mode::kTotalMorePrecise> repref) {
     if (counted.insert(repref.rep).second) {
@@ -87,15 +90,15 @@
 template <>
 struct CordRepRef<Mode::kFairShare> {
   // Creates a CordRepRef with the provided rep and top (parent) fraction.
-  explicit CordRepRef(const CordRep* r, double frac = 1.0)
+  explicit CordRepRef(absl::Nonnull<const CordRep*> r, double frac = 1.0)
       : rep(r), fraction(MaybeDiv(frac, r->refcount.Get())) {}
 
   // Returns a CordRepRef with a fraction of `this->fraction / child.refcount`
-  CordRepRef Child(const CordRep* child) const {
+  CordRepRef Child(absl::Nonnull<const CordRep*> child) const {
     return CordRepRef(child, fraction);
   }
 
-  const CordRep* rep;
+  absl::Nonnull<const CordRep*> rep;
   double fraction;
 };
 
@@ -147,7 +150,7 @@
 }
 
 template <Mode mode>
-size_t GetEstimatedUsage(const CordRep* rep) {
+size_t GetEstimatedUsage(absl::Nonnull<const CordRep*> rep) {
   // Zero initialized memory usage totals.
   RawUsage<mode> raw_usage;
 
@@ -176,15 +179,15 @@
 
 }  // namespace
 
-size_t GetEstimatedMemoryUsage(const CordRep* rep) {
+size_t GetEstimatedMemoryUsage(absl::Nonnull<const CordRep*> rep) {
   return GetEstimatedUsage<Mode::kTotal>(rep);
 }
 
-size_t GetEstimatedFairShareMemoryUsage(const CordRep* rep) {
+size_t GetEstimatedFairShareMemoryUsage(absl::Nonnull<const CordRep*> rep) {
   return GetEstimatedUsage<Mode::kFairShare>(rep);
 }
 
-size_t GetMorePreciseMemoryUsage(const CordRep* rep) {
+size_t GetMorePreciseMemoryUsage(absl::Nonnull<const CordRep*> rep) {
   return GetEstimatedUsage<Mode::kTotalMorePrecise>(rep);
 }
 
diff --git a/absl/strings/cord_analysis.h b/absl/strings/cord_analysis.h
index 9b9527a..f8ce348 100644
--- a/absl/strings/cord_analysis.h
+++ b/absl/strings/cord_analysis.h
@@ -19,6 +19,7 @@
 #include <cstdint>
 
 #include "absl/base/config.h"
+#include "absl/base/nullability.h"
 #include "absl/strings/internal/cord_internal.h"
 
 namespace absl {
@@ -28,7 +29,7 @@
 // Returns the *approximate* number of bytes held in full or in part by this
 // Cord (which may not remain the same between invocations). Cords that share
 // memory could each be "charged" independently for the same shared memory.
-size_t GetEstimatedMemoryUsage(const CordRep* rep);
+size_t GetEstimatedMemoryUsage(absl::Nonnull<const CordRep*> rep);
 
 // Returns the *approximate* number of bytes held in full or in part by this
 // Cord for the distinct memory held by this cord. This is similar to
@@ -46,13 +47,13 @@
 //
 // This is more expensive than `GetEstimatedMemoryUsage()` as it requires
 // deduplicating all memory references.
-size_t GetMorePreciseMemoryUsage(const CordRep* rep);
+size_t GetMorePreciseMemoryUsage(absl::Nonnull<const CordRep*> rep);
 
 // Returns the *approximate* number of bytes held in full or in part by this
 // CordRep weighted by the sharing ratio of that data. For example, if some data
 // edge is shared by 4 different Cords, then each cord is attribute 1/4th of
 // the total memory usage as a 'fair share' of the total memory usage.
-size_t GetEstimatedFairShareMemoryUsage(const CordRep* rep);
+size_t GetEstimatedFairShareMemoryUsage(absl::Nonnull<const CordRep*> rep);
 
 }  // namespace cord_internal
 ABSL_NAMESPACE_END
diff --git a/absl/strings/cordz_test_helpers.h b/absl/strings/cordz_test_helpers.h
index e410eec..619f13c 100644
--- a/absl/strings/cordz_test_helpers.h
+++ b/absl/strings/cordz_test_helpers.h
@@ -21,6 +21,7 @@
 #include "gtest/gtest.h"
 #include "absl/base/config.h"
 #include "absl/base/macros.h"
+#include "absl/base/nullability.h"
 #include "absl/strings/cord.h"
 #include "absl/strings/internal/cord_internal.h"
 #include "absl/strings/internal/cordz_info.h"
@@ -33,15 +34,16 @@
 ABSL_NAMESPACE_BEGIN
 
 // Returns the CordzInfo for the cord, or nullptr if the cord is not sampled.
-inline const cord_internal::CordzInfo* GetCordzInfoForTesting(
+inline absl::Nullable<const cord_internal::CordzInfo*> GetCordzInfoForTesting(
     const Cord& cord) {
   if (!cord.contents_.is_tree()) return nullptr;
   return cord.contents_.cordz_info();
 }
 
 // Returns true if the provided cordz_info is in the list of sampled cords.
-inline bool CordzInfoIsListed(const cord_internal::CordzInfo* cordz_info,
-                              cord_internal::CordzSampleToken token = {}) {
+inline bool CordzInfoIsListed(
+    absl::Nonnull<const cord_internal::CordzInfo*> cordz_info,
+    cord_internal::CordzSampleToken token = {}) {
   for (const cord_internal::CordzInfo& info : token) {
     if (cordz_info == &info) return true;
   }
@@ -119,7 +121,7 @@
 
 // Wrapper struct managing a small CordRep `rep`
 struct TestCordRep {
-  cord_internal::CordRepFlat* rep;
+  absl::Nonnull<cord_internal::CordRepFlat*> rep;
 
   TestCordRep() {
     rep = cord_internal::CordRepFlat::New(100);
diff --git a/absl/strings/internal/str_format/arg.cc b/absl/strings/internal/str_format/arg.cc
index c0a9a28..eeb2108 100644
--- a/absl/strings/internal/str_format/arg.cc
+++ b/absl/strings/internal/str_format/arg.cc
@@ -18,15 +18,28 @@
 //
 #include "absl/strings/internal/str_format/arg.h"
 
+#include <algorithm>
 #include <cassert>
-#include <cerrno>
+#include <cstddef>
+#include <cstdint>
 #include <cstdlib>
+#include <cstring>
+#include <cwchar>
 #include <string>
 #include <type_traits>
 
-#include "absl/base/port.h"
+#include "absl/base/config.h"
+#include "absl/base/optimization.h"
+#include "absl/container/fixed_array.h"
+#include "absl/numeric/int128.h"
+#include "absl/strings/internal/str_format/extension.h"
 #include "absl/strings/internal/str_format/float_conversion.h"
 #include "absl/strings/numbers.h"
+#include "absl/strings/string_view.h"
+
+#if defined(ABSL_HAVE_STD_STRING_VIEW)
+#include <string_view>
+#endif
 
 namespace absl {
 ABSL_NAMESPACE_BEGIN
@@ -298,6 +311,83 @@
                                conv.has_left_flag());
 }
 
+struct ShiftState {
+  bool saw_high_surrogate = false;
+  uint8_t bits = 0;
+};
+
+// Converts `v` from UTF-16 or UTF-32 to UTF-8 and writes to `buf`. `buf` is
+// assumed to have enough space for the output. `s` is used to carry state
+// between successive calls with a UTF-16 surrogate pair. Returns the number of
+// chars written, or `static_cast<size_t>(-1)` on failure.
+//
+// This is basically std::wcrtomb(), but always outputting UTF-8 instead of
+// respecting the current locale.
+inline size_t WideToUtf8(wchar_t wc, char *buf, ShiftState &s) {
+  const auto v = static_cast<uint32_t>(wc);
+  if (v < 0x80) {
+    *buf = static_cast<char>(v);
+    return 1;
+  } else if (v < 0x800) {
+    *buf++ = static_cast<char>(0xc0 | (v >> 6));
+    *buf = static_cast<char>(0x80 | (v & 0x3f));
+    return 2;
+  } else if (v < 0xd800 || (v - 0xe000) < 0x2000) {
+    *buf++ = static_cast<char>(0xe0 | (v >> 12));
+    *buf++ = static_cast<char>(0x80 | ((v >> 6) & 0x3f));
+    *buf = static_cast<char>(0x80 | (v & 0x3f));
+    return 3;
+  } else if ((v - 0x10000) < 0x100000) {
+    *buf++ = static_cast<char>(0xf0 | (v >> 18));
+    *buf++ = static_cast<char>(0x80 | ((v >> 12) & 0x3f));
+    *buf++ = static_cast<char>(0x80 | ((v >> 6) & 0x3f));
+    *buf = static_cast<char>(0x80 | (v & 0x3f));
+    return 4;
+  } else if (v < 0xdc00) {
+    s.saw_high_surrogate = true;
+    s.bits = static_cast<uint8_t>(v & 0x3);
+    const uint8_t high_bits = ((v >> 6) & 0xf) + 1;
+    *buf++ = static_cast<char>(0xf0 | (high_bits >> 2));
+    *buf =
+        static_cast<char>(0x80 | static_cast<uint8_t>((high_bits & 0x3) << 4) |
+                          static_cast<uint8_t>((v >> 2) & 0xf));
+    return 2;
+  } else if (v < 0xe000 && s.saw_high_surrogate) {
+    *buf++ = static_cast<char>(0x80 | static_cast<uint8_t>(s.bits << 4) |
+                               static_cast<uint8_t>((v >> 6) & 0xf));
+    *buf = static_cast<char>(0x80 | (v & 0x3f));
+    s.saw_high_surrogate = false;
+    s.bits = 0;
+    return 2;
+  } else {
+    return static_cast<size_t>(-1);
+  }
+}
+
+inline bool ConvertStringArg(const wchar_t *v,
+                             size_t len,
+                             const FormatConversionSpecImpl conv,
+                             FormatSinkImpl *sink) {
+  FixedArray<char> mb(len * 4);
+  ShiftState s;
+  size_t chars_written = 0;
+  for (size_t i = 0; i < len; ++i) {
+    const size_t chars = WideToUtf8(v[i], &mb[chars_written], s);
+    if (chars == static_cast<size_t>(-1)) { return false; }
+    chars_written += chars;
+  }
+  return ConvertStringArg(string_view(mb.data(), chars_written), conv, sink);
+}
+
+bool ConvertWCharTImpl(wchar_t v, const FormatConversionSpecImpl conv,
+                       FormatSinkImpl *sink) {
+  char mb[4];
+  ShiftState s;
+  const size_t chars_written = WideToUtf8(v, mb, s);
+  return chars_written != static_cast<size_t>(-1) && !s.saw_high_surrogate &&
+         ConvertStringArg(string_view(mb, chars_written), conv, sink);
+}
+
 }  // namespace
 
 bool ConvertBoolArg(bool v, FormatSinkImpl *sink) {
@@ -316,11 +406,14 @@
 
   // This odd casting is due to a bug in -Wswitch behavior in gcc49 which causes
   // it to complain about a switch/case type mismatch, even though both are
-  // FormatConverionChar.  Likely this is because at this point
+  // FormatConversionChar.  Likely this is because at this point
   // FormatConversionChar is declared, but not defined.
   switch (static_cast<uint8_t>(conv.conversion_char())) {
     case static_cast<uint8_t>(FormatConversionCharInternal::c):
-      return ConvertCharImpl(static_cast<char>(v), conv, sink);
+      return (std::is_same<T, wchar_t>::value ||
+              (conv.length_mod() == LengthMod::l))
+                 ? ConvertWCharTImpl(static_cast<wchar_t>(v), conv, sink)
+                 : ConvertCharImpl(static_cast<char>(v), conv, sink);
 
     case static_cast<uint8_t>(FormatConversionCharInternal::o):
       as_digits.PrintAsOct(static_cast<U>(v));
@@ -372,6 +465,8 @@
 template bool ConvertIntArg<unsigned char>(unsigned char v,
                                            FormatConversionSpecImpl conv,
                                            FormatSinkImpl *sink);
+template bool ConvertIntArg<wchar_t>(wchar_t v, FormatConversionSpecImpl conv,
+                                     FormatSinkImpl *sink);
 template bool ConvertIntArg<short>(short v,  // NOLINT
                                    FormatConversionSpecImpl conv,
                                    FormatSinkImpl *sink);
@@ -403,16 +498,29 @@
   return {ConvertStringArg(v, conv, sink)};
 }
 
+StringConvertResult FormatConvertImpl(const std::wstring &v,
+                                      const FormatConversionSpecImpl conv,
+                                      FormatSinkImpl *sink) {
+  return {ConvertStringArg(v.data(), v.size(), conv, sink)};
+}
+
 StringConvertResult FormatConvertImpl(string_view v,
                                       const FormatConversionSpecImpl conv,
                                       FormatSinkImpl *sink) {
   return {ConvertStringArg(v, conv, sink)};
 }
 
-ArgConvertResult<FormatConversionCharSetUnion(
-    FormatConversionCharSetInternal::s, FormatConversionCharSetInternal::p)>
-FormatConvertImpl(const char *v, const FormatConversionSpecImpl conv,
-                  FormatSinkImpl *sink) {
+#if defined(ABSL_HAVE_STD_STRING_VIEW)
+StringConvertResult FormatConvertImpl(std::wstring_view v,
+                                      const FormatConversionSpecImpl conv,
+                                      FormatSinkImpl* sink) {
+  return {ConvertStringArg(v.data(), v.size(), conv, sink)};
+}
+#endif
+
+StringPtrConvertResult FormatConvertImpl(const char* v,
+                                         const FormatConversionSpecImpl conv,
+                                         FormatSinkImpl* sink) {
   if (conv.conversion_char() == FormatConversionCharInternal::p)
     return {FormatConvertImpl(VoidPtr(v), conv, sink).value};
   size_t len;
@@ -427,6 +535,30 @@
   return {ConvertStringArg(string_view(v, len), conv, sink)};
 }
 
+StringPtrConvertResult FormatConvertImpl(const wchar_t* v,
+                                         const FormatConversionSpecImpl conv,
+                                         FormatSinkImpl* sink) {
+  if (conv.conversion_char() == FormatConversionCharInternal::p) {
+    return {FormatConvertImpl(VoidPtr(v), conv, sink).value};
+  }
+  size_t len;
+  if (v == nullptr) {
+    len = 0;
+  } else if (conv.precision() < 0) {
+    len = std::wcslen(v);
+  } else {
+    // If precision is set, we look for the NUL-terminator on the valid range.
+    len = static_cast<size_t>(std::find(v, v + conv.precision(), L'\0') - v);
+  }
+  return {ConvertStringArg(v, len, conv, sink)};
+}
+
+StringPtrConvertResult FormatConvertImpl(std::nullptr_t,
+                                         const FormatConversionSpecImpl conv,
+                                         FormatSinkImpl* sink) {
+  return FormatConvertImpl(static_cast<const char*>(nullptr), conv, sink);
+}
+
 // ==================== Raw pointers ====================
 ArgConvertResult<FormatConversionCharSetInternal::p> FormatConvertImpl(
     VoidPtr v, const FormatConversionSpecImpl conv, FormatSinkImpl *sink) {
@@ -461,6 +593,11 @@
                                     FormatSinkImpl *sink) {
   return {ConvertIntArg(v, conv, sink)};
 }
+CharConvertResult FormatConvertImpl(wchar_t v,
+                                    const FormatConversionSpecImpl conv,
+                                    FormatSinkImpl* sink) {
+  return {ConvertIntArg(v, conv, sink)};
+}
 
 // ==================== Ints ====================
 IntegralConvertResult FormatConvertImpl(signed char v,
diff --git a/absl/strings/internal/str_format/arg.h b/absl/strings/internal/str_format/arg.h
index 20483af..309161d 100644
--- a/absl/strings/internal/str_format/arg.h
+++ b/absl/strings/internal/str_format/arg.h
@@ -19,8 +19,9 @@
 #include <wchar.h>
 
 #include <algorithm>
+#include <cstddef>
+#include <cstdint>
 #include <cstdio>
-#include <iomanip>
 #include <limits>
 #include <memory>
 #include <sstream>
@@ -28,13 +29,18 @@
 #include <type_traits>
 #include <utility>
 
-#include "absl/base/port.h"
+#include "absl/base/config.h"
+#include "absl/base/optimization.h"
 #include "absl/meta/type_traits.h"
 #include "absl/numeric/int128.h"
 #include "absl/strings/has_absl_stringify.h"
 #include "absl/strings/internal/str_format/extension.h"
 #include "absl/strings/string_view.h"
 
+#if defined(ABSL_HAVE_STD_STRING_VIEW)
+#include <string_view>
+#endif
+
 namespace absl {
 ABSL_NAMESPACE_BEGIN
 
@@ -97,6 +103,9 @@
 extern template bool ConvertIntArg<unsigned char>(unsigned char v,
                                                   FormatConversionSpecImpl conv,
                                                   FormatSinkImpl* sink);
+extern template bool ConvertIntArg<wchar_t>(wchar_t v,
+                                            FormatConversionSpecImpl conv,
+                                            FormatSinkImpl* sink);
 extern template bool ConvertIntArg<short>(short v,  // NOLINT
                                           FormatConversionSpecImpl conv,
                                           FormatSinkImpl* sink);
@@ -203,30 +212,49 @@
   return C;
 }
 
-using StringConvertResult = ArgConvertResult<FormatConversionCharSetUnion(
-    FormatConversionCharSetInternal::s, FormatConversionCharSetInternal::v)>;
 ArgConvertResult<FormatConversionCharSetInternal::p> FormatConvertImpl(
     VoidPtr v, FormatConversionSpecImpl conv, FormatSinkImpl* sink);
 
 // Strings.
+using StringConvertResult = ArgConvertResult<FormatConversionCharSetUnion(
+    FormatConversionCharSetInternal::s,
+    FormatConversionCharSetInternal::v)>;
 StringConvertResult FormatConvertImpl(const std::string& v,
                                       FormatConversionSpecImpl conv,
                                       FormatSinkImpl* sink);
+StringConvertResult FormatConvertImpl(const std::wstring& v,
+                                      FormatConversionSpecImpl conv,
+                                      FormatSinkImpl* sink);
 StringConvertResult FormatConvertImpl(string_view v,
                                       FormatConversionSpecImpl conv,
                                       FormatSinkImpl* sink);
-#if defined(ABSL_HAVE_STD_STRING_VIEW) && !defined(ABSL_USES_STD_STRING_VIEW)
+#if defined(ABSL_HAVE_STD_STRING_VIEW)
+StringConvertResult FormatConvertImpl(std::wstring_view v,
+                                      FormatConversionSpecImpl conv,
+                                      FormatSinkImpl* sink);
+#if !defined(ABSL_USES_STD_STRING_VIEW)
 inline StringConvertResult FormatConvertImpl(std::string_view v,
                                              FormatConversionSpecImpl conv,
                                              FormatSinkImpl* sink) {
   return FormatConvertImpl(absl::string_view(v.data(), v.size()), conv, sink);
 }
-#endif  // ABSL_HAVE_STD_STRING_VIEW && !ABSL_USES_STD_STRING_VIEW
+#endif  // !ABSL_USES_STD_STRING_VIEW
+#endif  // ABSL_HAVE_STD_STRING_VIEW
 
-ArgConvertResult<FormatConversionCharSetUnion(
-    FormatConversionCharSetInternal::s, FormatConversionCharSetInternal::p)>
-FormatConvertImpl(const char* v, const FormatConversionSpecImpl conv,
-                  FormatSinkImpl* sink);
+using StringPtrConvertResult = ArgConvertResult<FormatConversionCharSetUnion(
+    FormatConversionCharSetInternal::s,
+    FormatConversionCharSetInternal::p)>;
+StringPtrConvertResult FormatConvertImpl(const char* v,
+                                         FormatConversionSpecImpl conv,
+                                         FormatSinkImpl* sink);
+StringPtrConvertResult FormatConvertImpl(const wchar_t* v,
+                                         FormatConversionSpecImpl conv,
+                                         FormatSinkImpl* sink);
+// This overload is needed to disambiguate, since `nullptr` could match either
+// of the other overloads equally well.
+StringPtrConvertResult FormatConvertImpl(std::nullptr_t,
+                                         FormatConversionSpecImpl conv,
+                                         FormatSinkImpl* sink);
 
 template <class AbslCord, typename std::enable_if<std::is_same<
                               AbslCord, absl::Cord>::value>::type* = nullptr>
@@ -280,6 +308,9 @@
 // Chars.
 CharConvertResult FormatConvertImpl(char v, FormatConversionSpecImpl conv,
                                     FormatSinkImpl* sink);
+CharConvertResult FormatConvertImpl(wchar_t v,
+                                    FormatConversionSpecImpl conv,
+                                    FormatSinkImpl* sink);
 
 // Ints.
 IntegralConvertResult FormatConvertImpl(signed char v,
@@ -441,6 +472,7 @@
   // Anything with a user-defined Convert will get its own vtable.
   // For everything else:
   //   - Decay char* and char arrays into `const char*`
+  //   - Decay wchar_t* and wchar_t arrays into `const wchar_t*`
   //   - Decay any other pointer to `const void*`
   //   - Decay all enums to the integral promotion of their underlying type.
   //   - Decay function pointers to void*.
@@ -452,9 +484,13 @@
     using type = typename std::conditional<
         !kHasUserDefined && std::is_convertible<T, const char*>::value,
         const char*,
-        typename std::conditional<!kHasUserDefined &&
-                                      std::is_convertible<T, VoidPtr>::value,
-                                  VoidPtr, const T&>::type>::type;
+        typename std::conditional<
+            !kHasUserDefined && std::is_convertible<T, const wchar_t*>::value,
+            const wchar_t*,
+            typename std::conditional<
+                !kHasUserDefined && std::is_convertible<T, VoidPtr>::value,
+                VoidPtr,
+                const T&>::type>::type>::type;
   };
   template <typename T>
   struct DecayType<
@@ -585,7 +621,7 @@
   E template bool FormatArgImpl::Dispatch<T>(Data, FormatConversionSpecImpl, \
                                              void*)
 
-#define ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_(...)                   \
+#define ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_NO_WSTRING_VIEW_(...)   \
   ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(str_format_internal::VoidPtr,     \
                                              __VA_ARGS__);                     \
   ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(bool, __VA_ARGS__);               \
@@ -611,7 +647,19 @@
   ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(long double, __VA_ARGS__);        \
   ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(const char*, __VA_ARGS__);        \
   ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(std::string, __VA_ARGS__);        \
-  ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(string_view, __VA_ARGS__)
+  ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(string_view, __VA_ARGS__);        \
+  ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(const wchar_t*, __VA_ARGS__);     \
+  ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(std::wstring, __VA_ARGS__)
+
+#if defined(ABSL_HAVE_STD_STRING_VIEW)
+#define ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_(...)       \
+  ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_NO_WSTRING_VIEW_( \
+      __VA_ARGS__);                                                \
+  ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(std::wstring_view, __VA_ARGS__)
+#else
+#define ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_(...) \
+  ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_NO_WSTRING_VIEW_(__VA_ARGS__)
+#endif
 
 ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_(extern);
 
diff --git a/absl/strings/internal/str_format/arg_test.cc b/absl/strings/internal/str_format/arg_test.cc
index 1261937..f663d7c 100644
--- a/absl/strings/internal/str_format/arg_test.cc
+++ b/absl/strings/internal/str_format/arg_test.cc
@@ -14,9 +14,10 @@
 
 #include "absl/strings/internal/str_format/arg.h"
 
-#include <ostream>
+#include <limits>
 #include <string>
 #include "gtest/gtest.h"
+#include "absl/base/config.h"
 #include "absl/strings/str_format.h"
 
 namespace absl {
@@ -93,6 +94,21 @@
             FormatArgImplFriend::GetVTablePtrForTest(FormatArgImpl(kMyArray)));
 }
 
+extern const wchar_t kMyWCharTArray[];
+
+TEST_F(FormatArgImplTest, WCharTArraysDecayToWCharTPtr) {
+  const wchar_t* a = L"";
+  EXPECT_EQ(FormatArgImplFriend::GetVTablePtrForTest(FormatArgImpl(a)),
+            FormatArgImplFriend::GetVTablePtrForTest(FormatArgImpl(L"")));
+  EXPECT_EQ(FormatArgImplFriend::GetVTablePtrForTest(FormatArgImpl(a)),
+            FormatArgImplFriend::GetVTablePtrForTest(FormatArgImpl(L"A")));
+  EXPECT_EQ(FormatArgImplFriend::GetVTablePtrForTest(FormatArgImpl(a)),
+            FormatArgImplFriend::GetVTablePtrForTest(FormatArgImpl(L"ABC")));
+  EXPECT_EQ(
+      FormatArgImplFriend::GetVTablePtrForTest(FormatArgImpl(a)),
+      FormatArgImplFriend::GetVTablePtrForTest(FormatArgImpl(kMyWCharTArray)));
+}
+
 TEST_F(FormatArgImplTest, OtherPtrDecayToVoidPtr) {
   auto expected = FormatArgImplFriend::GetVTablePtrForTest(
       FormatArgImpl(static_cast<void *>(nullptr)));
@@ -124,6 +140,22 @@
 }
 const char kMyArray[] = "ABCDE";
 
+TEST_F(FormatArgImplTest, WorksWithWCharTArraysOfUnknownSize) {
+  std::string s;
+  FormatSinkImpl sink(&s);
+  FormatConversionSpecImpl conv;
+  FormatConversionSpecImplFriend::SetConversionChar(
+      FormatConversionCharInternal::s, &conv);
+  FormatConversionSpecImplFriend::SetFlags(Flags(), &conv);
+  FormatConversionSpecImplFriend::SetWidth(-1, &conv);
+  FormatConversionSpecImplFriend::SetPrecision(-1, &conv);
+  EXPECT_TRUE(
+      FormatArgImplFriend::Convert(FormatArgImpl(kMyWCharTArray), conv, &sink));
+  sink.Flush();
+  EXPECT_EQ("ABCDE", s);
+}
+const wchar_t kMyWCharTArray[] = L"ABCDE";
+
 }  // namespace
 }  // namespace str_format_internal
 ABSL_NAMESPACE_END
diff --git a/absl/strings/internal/str_format/bind.cc b/absl/strings/internal/str_format/bind.cc
index 77a4222..87e23b5 100644
--- a/absl/strings/internal/str_format/bind.cc
+++ b/absl/strings/internal/str_format/bind.cc
@@ -14,10 +14,24 @@
 
 #include "absl/strings/internal/str_format/bind.h"
 
+#include <algorithm>
+#include <cassert>
 #include <cerrno>
+#include <cstddef>
+#include <cstdio>
+#include <ios>
 #include <limits>
+#include <ostream>
 #include <sstream>
 #include <string>
+#include "absl/base/config.h"
+#include "absl/base/optimization.h"
+#include "absl/strings/internal/str_format/arg.h"
+#include "absl/strings/internal/str_format/constexpr_parser.h"
+#include "absl/strings/internal/str_format/extension.h"
+#include "absl/strings/internal/str_format/output.h"
+#include "absl/strings/string_view.h"
+#include "absl/types/span.h"
 
 namespace absl {
 ABSL_NAMESPACE_BEGIN
@@ -90,6 +104,8 @@
     } else {
       FormatConversionSpecImplFriend::SetFlags(unbound->flags, bound);
     }
+
+    FormatConversionSpecImplFriend::SetLengthMod(unbound->length_mod, bound);
   } else {
     FormatConversionSpecImplFriend::SetFlags(unbound->flags, bound);
     FormatConversionSpecImplFriend::SetWidth(-1, bound);
@@ -215,7 +231,7 @@
   return *out;
 }
 
-std::string FormatPack(const UntypedFormatSpecImpl format,
+std::string FormatPack(UntypedFormatSpecImpl format,
                        absl::Span<const FormatArgImpl> args) {
   std::string out;
   if (ABSL_PREDICT_FALSE(!FormatUntyped(&out, format, args))) {
diff --git a/absl/strings/internal/str_format/bind.h b/absl/strings/internal/str_format/bind.h
index 5e2a43d..120bc35 100644
--- a/absl/strings/internal/str_format/bind.h
+++ b/absl/strings/internal/str_format/bind.h
@@ -15,16 +15,19 @@
 #ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_BIND_H_
 #define ABSL_STRINGS_INTERNAL_STR_FORMAT_BIND_H_
 
-#include <array>
+#include <cassert>
 #include <cstdio>
-#include <sstream>
+#include <ostream>
 #include <string>
 
-#include "absl/base/port.h"
+#include "absl/base/config.h"
 #include "absl/container/inlined_vector.h"
 #include "absl/strings/internal/str_format/arg.h"
 #include "absl/strings/internal/str_format/checker.h"
+#include "absl/strings/internal/str_format/constexpr_parser.h"
+#include "absl/strings/internal/str_format/extension.h"
 #include "absl/strings/internal/str_format/parser.h"
+#include "absl/strings/string_view.h"
 #include "absl/types/span.h"
 #include "absl/utility/utility.h"
 
@@ -203,7 +206,7 @@
 std::string& AppendPack(std::string* out, UntypedFormatSpecImpl format,
                         absl::Span<const FormatArgImpl> args);
 
-std::string FormatPack(const UntypedFormatSpecImpl format,
+std::string FormatPack(UntypedFormatSpecImpl format,
                        absl::Span<const FormatArgImpl> args);
 
 int FprintF(std::FILE* output, UntypedFormatSpecImpl format,
diff --git a/absl/strings/internal/str_format/constexpr_parser.h b/absl/strings/internal/str_format/constexpr_parser.h
index b70a16e..8f59387 100644
--- a/absl/strings/internal/str_format/constexpr_parser.h
+++ b/absl/strings/internal/str_format/constexpr_parser.h
@@ -17,17 +17,18 @@
 
 #include <cassert>
 #include <cstdint>
+#include <cstdio>
 #include <limits>
 
+#include "absl/base/config.h"
 #include "absl/base/const_init.h"
+#include "absl/base/optimization.h"
 #include "absl/strings/internal/str_format/extension.h"
 
 namespace absl {
 ABSL_NAMESPACE_BEGIN
 namespace str_format_internal {
 
-enum class LengthMod : std::uint8_t { h, hh, l, ll, L, j, z, t, q, none };
-
 // The analyzed properties of a single specified conversion.
 struct UnboundConversion {
   // This is a user defined default constructor on purpose to skip the
@@ -306,7 +307,6 @@
     if (ABSL_PREDICT_FALSE(!tag.is_length())) return nullptr;
 
     // It is a length modifier.
-    using str_format_internal::LengthMod;
     LengthMod length_mod = tag.as_length();
     ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR();
     if (c == 'h' && length_mod == LengthMod::h) {
@@ -322,6 +322,11 @@
 
     if (ABSL_PREDICT_FALSE(c == 'v')) return nullptr;
     if (ABSL_PREDICT_FALSE(!tag.is_conv())) return nullptr;
+
+    // `wchar_t` args are marked non-basic so `Bind()` will copy the length mod.
+    if (conv->length_mod == LengthMod::l && c == 'c') {
+      conv->flags = conv->flags | Flags::kNonBasic;
+    }
   }
 #undef ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR
 
diff --git a/absl/strings/internal/str_format/convert_test.cc b/absl/strings/internal/str_format/convert_test.cc
index d14ecb2..7f22277 100644
--- a/absl/strings/internal/str_format/convert_test.cc
+++ b/absl/strings/internal/str_format/convert_test.cc
@@ -12,25 +12,43 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <errno.h>
+#include <assert.h>
+#include <locale.h>
 #include <stdarg.h>
 #include <stdio.h>
 
 #include <algorithm>
-#include <cctype>
+#include <climits>
 #include <cmath>
+#include <cstdlib>
+#include <cstring>
+#include <cwctype>
 #include <limits>
+#include <set>
+#include <sstream>
 #include <string>
 #include <thread>  // NOLINT
+#include <type_traits>
+#include <vector>
 
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
 #include "absl/base/attributes.h"
 #include "absl/base/internal/raw_logging.h"
 #include "absl/log/log.h"
+#include "absl/numeric/int128.h"
+#include "absl/strings/ascii.h"
+#include "absl/strings/internal/str_format/arg.h"
 #include "absl/strings/internal/str_format/bind.h"
 #include "absl/strings/match.h"
+#include "absl/strings/str_format.h"
+#include "absl/strings/string_view.h"
 #include "absl/types/optional.h"
+#include "absl/types/span.h"
+
+#if defined(ABSL_HAVE_STD_STRING_VIEW)
+#include <string_view>
+#endif
 
 namespace absl {
 ABSL_NAMESPACE_BEGIN
@@ -49,36 +67,103 @@
   return N;
 }
 
-std::string LengthModFor(float) { return ""; }
-std::string LengthModFor(double) { return ""; }
-std::string LengthModFor(long double) { return "L"; }
-std::string LengthModFor(char) { return "hh"; }
-std::string LengthModFor(signed char) { return "hh"; }
-std::string LengthModFor(unsigned char) { return "hh"; }
-std::string LengthModFor(short) { return "h"; }           // NOLINT
-std::string LengthModFor(unsigned short) { return "h"; }  // NOLINT
-std::string LengthModFor(int) { return ""; }
-std::string LengthModFor(unsigned) { return ""; }
-std::string LengthModFor(long) { return "l"; }                 // NOLINT
-std::string LengthModFor(unsigned long) { return "l"; }        // NOLINT
-std::string LengthModFor(long long) { return "ll"; }           // NOLINT
-std::string LengthModFor(unsigned long long) { return "ll"; }  // NOLINT
+template <typename T>
+struct AlwaysFalse : std::false_type {};
+
+template <typename T>
+std::string LengthModFor() {
+  static_assert(AlwaysFalse<T>::value, "Unsupported type");
+  return "";
+}
+template <>
+std::string LengthModFor<char>() {
+  return "hh";
+}
+template <>
+std::string LengthModFor<signed char>() {
+  return "hh";
+}
+template <>
+std::string LengthModFor<unsigned char>() {
+  return "hh";
+}
+template <>
+std::string LengthModFor<short>() {  // NOLINT
+  return "h";
+}
+template <>
+std::string LengthModFor<unsigned short>() {  // NOLINT
+  return "h";
+}
+template <>
+std::string LengthModFor<int>() {
+  return "";
+}
+template <>
+std::string LengthModFor<unsigned>() {
+  return "";
+}
+template <>
+std::string LengthModFor<long>() {  // NOLINT
+  return "l";
+}
+template <>
+std::string LengthModFor<unsigned long>() {  // NOLINT
+  return "l";
+}
+template <>
+std::string LengthModFor<long long>() {  // NOLINT
+  return "ll";
+}
+template <>
+std::string LengthModFor<unsigned long long>() {  // NOLINT
+  return "ll";
+}
+
+// An integral type of the same rank and signedness as `wchar_t`, that isn't
+// `wchar_t`.
+using IntegralTypeForWCharT =
+    std::conditional_t<std::is_signed<wchar_t>::value,
+                       // Some STLs are broken and return `wchar_t` from
+                       // `std::make_[un]signed_t<wchar_t>` when the signedness
+                       // matches. Work around by round-tripping through the
+                       // opposite signedness.
+                       std::make_signed_t<std::make_unsigned_t<wchar_t>>,
+                       std::make_unsigned_t<std::make_signed_t<wchar_t>>>;
+
+// Given an integral type `T`, returns a type of the same rank and signedness
+// that is guaranteed to not be `wchar_t`.
+template <typename T>
+using MatchingIntegralType = std::conditional_t<std::is_same<T, wchar_t>::value,
+                                                IntegralTypeForWCharT, T>;
 
 std::string EscCharImpl(int v) {
-  if (std::isprint(static_cast<unsigned char>(v))) {
-    return std::string(1, static_cast<char>(v));
-  }
   char buf[64];
-  int n = snprintf(buf, sizeof(buf), "\\%#.2x",
-                   static_cast<unsigned>(v & 0xff));
-  assert(n > 0 && n < sizeof(buf));
-  return std::string(buf, n);
+  int n = absl::ascii_isprint(static_cast<unsigned char>(v))
+              ? snprintf(buf, sizeof(buf), "'%c'", v)
+              : snprintf(buf, sizeof(buf), "'\\x%.*x'", CHAR_BIT / 4,
+                         static_cast<unsigned>(
+                             static_cast<std::make_unsigned_t<char>>(v)));
+  assert(n > 0 && static_cast<size_t>(n) < sizeof(buf));
+  return std::string(buf, static_cast<size_t>(n));
 }
 
 std::string Esc(char v) { return EscCharImpl(v); }
 std::string Esc(signed char v) { return EscCharImpl(v); }
 std::string Esc(unsigned char v) { return EscCharImpl(v); }
 
+std::string Esc(wchar_t v) {
+  char buf[64];
+  int n = std::iswprint(static_cast<wint_t>(v))
+              ? snprintf(buf, sizeof(buf), "L'%lc'", static_cast<wint_t>(v))
+              : snprintf(buf, sizeof(buf), "L'\\x%.*llx'",
+                         static_cast<int>(sizeof(wchar_t) * CHAR_BIT / 4),
+                         static_cast<unsigned long long>(
+                             static_cast<std::make_unsigned_t<wchar_t>>(v)));
+  assert(n > 0 && static_cast<size_t>(n) < sizeof(buf));
+  return std::string(buf, static_cast<size_t>(n));
+}
+
 template <typename T>
 std::string Esc(const T &v) {
   std::ostringstream oss;
@@ -101,7 +186,7 @@
   if (result < kSpaceLength) {
     if (result >= 0) {
       // Normal case -- everything fit.
-      dst->append(space, result);
+      dst->append(space, static_cast<size_t>(result));
       return;
     }
     if (result < 0) {
@@ -112,7 +197,7 @@
 
   // Increase the buffer size to the size requested by vsnprintf,
   // plus one for the closing \0.
-  int length = result + 1;
+  size_t length = static_cast<size_t>(result) + 1;
   char *buf = new char[length];
 
   // Restore the va_list before we use it again
@@ -120,9 +205,9 @@
   result = vsnprintf(buf, length, format, backup_ap);
   va_end(backup_ap);
 
-  if (result >= 0 && result < length) {
+  if (result >= 0 && static_cast<size_t>(result) < length) {
     // It fit
-    dst->append(buf, result);
+    dst->append(buf, static_cast<size_t>(result));
   }
   delete[] buf;
 }
@@ -231,11 +316,15 @@
 
 TEST_F(FormatConvertTest, BasicString) {
   TestStringConvert("hello");  // As char array.
+  TestStringConvert(L"hello");
   TestStringConvert(static_cast<const char*>("hello"));
+  TestStringConvert(static_cast<const wchar_t*>(L"hello"));
   TestStringConvert(std::string("hello"));
+  TestStringConvert(std::wstring(L"hello"));
   TestStringConvert(string_view("hello"));
 #if defined(ABSL_HAVE_STD_STRING_VIEW)
   TestStringConvert(std::string_view("hello"));
+  TestStringConvert(std::wstring_view(L"hello"));
 #endif  // ABSL_HAVE_STD_STRING_VIEW
 }
 
@@ -243,6 +332,10 @@
   const char* p = nullptr;
   UntypedFormatSpecImpl format("%s");
   EXPECT_EQ("", FormatPack(format, {FormatArgImpl(p)}));
+
+  const wchar_t* wp = nullptr;
+  UntypedFormatSpecImpl wformat("%ls");
+  EXPECT_EQ("", FormatPack(wformat, {FormatArgImpl(wp)}));
 }
 
 TEST_F(FormatConvertTest, StringPrecision) {
@@ -252,10 +345,19 @@
   UntypedFormatSpecImpl format("%.1s");
   EXPECT_EQ("a", FormatPack(format, {FormatArgImpl(p)}));
 
+  wchar_t wc = L'a';
+  const wchar_t* wp = &wc;
+  UntypedFormatSpecImpl wformat("%.1ls");
+  EXPECT_EQ("a", FormatPack(wformat, {FormatArgImpl(wp)}));
+
   // We cap at the NUL-terminator.
   p = "ABC";
   UntypedFormatSpecImpl format2("%.10s");
   EXPECT_EQ("ABC", FormatPack(format2, {FormatArgImpl(p)}));
+
+  wp = L"ABC";
+  UntypedFormatSpecImpl wformat2("%.10ls");
+  EXPECT_EQ("ABC", FormatPack(wformat2, {FormatArgImpl(wp)}));
 }
 
 // Pointer formatting is implementation defined. This checks that the argument
@@ -278,16 +380,25 @@
   char *mcp = &c;
   const char *cp = "hi";
   const char *cnil = nullptr;
+  wchar_t wc = L'h';
+  wchar_t *mwcp = &wc;
+  const wchar_t *wcp = L"hi";
+  const wchar_t *wcnil = nullptr;
   const int *inil = nullptr;
   using VoidF = void (*)();
   VoidF fp = [] {}, fnil = nullptr;
   volatile char vc;
   volatile char *vcp = &vc;
   volatile char *vcnil = nullptr;
+  volatile wchar_t vwc;
+  volatile wchar_t *vwcp = &vwc;
+  volatile wchar_t *vwcnil = nullptr;
   const FormatArgImpl args_array[] = {
-      FormatArgImpl(xp),   FormatArgImpl(cp),  FormatArgImpl(inil),
-      FormatArgImpl(cnil), FormatArgImpl(mcp), FormatArgImpl(fp),
-      FormatArgImpl(fnil), FormatArgImpl(vcp), FormatArgImpl(vcnil),
+      FormatArgImpl(xp),    FormatArgImpl(cp),     FormatArgImpl(wcp),
+      FormatArgImpl(inil),  FormatArgImpl(cnil),   FormatArgImpl(wcnil),
+      FormatArgImpl(mcp),   FormatArgImpl(mwcp),   FormatArgImpl(fp),
+      FormatArgImpl(fnil),  FormatArgImpl(vcp),    FormatArgImpl(vwcp),
+      FormatArgImpl(vcnil), FormatArgImpl(vwcnil),
   };
   auto args = absl::MakeConstSpan(args_array);
 
@@ -313,30 +424,49 @@
   EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%-30.20p"), args),
               MatchesPointerString(&x));
 
+  // const int*
+  EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%1$p"), args),
+              MatchesPointerString(xp));
   // const char*
   EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%2$p"), args),
               MatchesPointerString(cp));
-  // null const int*
+  // const wchar_t*
   EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%3$p"), args),
-              MatchesPointerString(nullptr));
-  // null const char*
+              MatchesPointerString(wcp));
+  // null const int*
   EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%4$p"), args),
               MatchesPointerString(nullptr));
-  // nonconst char*
+  // null const char*
   EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%5$p"), args),
-              MatchesPointerString(mcp));
-
-  // function pointers
-  EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%6$p"), args),
-              MatchesPointerString(reinterpret_cast<const void*>(fp)));
-  EXPECT_THAT(
-      FormatPack(UntypedFormatSpecImpl("%8$p"), args),
-      MatchesPointerString(reinterpret_cast<volatile const void *>(vcp)));
-
-  // null function pointers
-  EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%7$p"), args),
               MatchesPointerString(nullptr));
+  // null const wchar_t*
+  EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%6$p"), args),
+              MatchesPointerString(nullptr));
+  // nonconst char*
+  EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%7$p"), args),
+              MatchesPointerString(mcp));
+  // nonconst wchar_t*
+  EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%8$p"), args),
+              MatchesPointerString(mwcp));
+  // function pointer
   EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%9$p"), args),
+              MatchesPointerString(reinterpret_cast<const void *>(fp)));
+  // null function pointer
+  EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%10$p"), args),
+              MatchesPointerString(nullptr));
+  // volatile char*
+  EXPECT_THAT(
+      FormatPack(UntypedFormatSpecImpl("%11$p"), args),
+      MatchesPointerString(reinterpret_cast<volatile const void *>(vcp)));
+  // volatile wchar_t*
+  EXPECT_THAT(
+      FormatPack(UntypedFormatSpecImpl("%12$p"), args),
+      MatchesPointerString(reinterpret_cast<volatile const void *>(vwcp)));
+  // null volatile char*
+  EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%13$p"), args),
+              MatchesPointerString(nullptr));
+  // null volatile wchar_t*
+  EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%14$p"), args),
               MatchesPointerString(nullptr));
 }
 
@@ -436,12 +566,15 @@
               // as printf can't do that conversion properly. For those
               // cases, we do expect agreement with printf with a "%u"
               // and the unsigned equivalent of 'val'.
-              UnsignedT uval = val;
-              old_fmt += LengthModFor(uval);
+              UnsignedT uval =
+                  static_cast<std::remove_volatile_t<UnsignedT>>(val);
+              old_fmt += LengthModFor<
+                  MatchingIntegralType<std::remove_cv_t<decltype(uval)>>>();
               old_fmt += "u";
               old_result = StrPrint(old_fmt.c_str(), uval);
             } else {
-              old_fmt += LengthModFor(val);
+              old_fmt += LengthModFor<
+                  MatchingIntegralType<std::remove_cv_t<decltype(val)>>>();
               old_fmt += conv_char;
               old_result = StrPrint(old_fmt.c_str(), val);
             }
@@ -459,6 +592,47 @@
   }
 }
 
+template <typename T>
+absl::optional<std::string> StrPrintChar(T c) {
+  return StrPrint("%c", static_cast<int>(c));
+}
+template <>
+absl::optional<std::string> StrPrintChar(wchar_t c) {
+  // musl libc has a bug where ("%lc", 0) writes no characters, and Android
+  // doesn't support forcing UTF-8 via setlocale(). Hardcode the expected
+  // answers for ASCII inputs to maximize test coverage on these platforms.
+  if (static_cast<std::make_unsigned_t<wchar_t>>(c) < 0x80) {
+    return std::string(1, static_cast<char>(c));
+  }
+
+  // Force a UTF-8 locale to match the expected `StrFormat()` behavior.
+  // It's important to copy the string returned by `old_locale` here, because
+  // its contents are not guaranteed to be valid after the next `setlocale()`
+  // call.
+  std::string old_locale = setlocale(LC_CTYPE, nullptr);
+  if (!setlocale(LC_CTYPE, "en_US.UTF-8")) {
+    return absl::nullopt;
+  }
+  const std::string output = StrPrint("%lc", static_cast<wint_t>(c));
+  setlocale(LC_CTYPE, old_locale.c_str());
+  return output;
+}
+
+template <typename T>
+typename std::remove_volatile<T>::type GetMaxForConversion() {
+  return static_cast<typename std::remove_volatile<T>::type>(
+      std::numeric_limits<int>::max());
+}
+
+template <>
+wchar_t GetMaxForConversion<wchar_t>() {
+  // Don't return values that aren't legal Unicode. For wchar_t conversions in a
+  // UTF-8 locale, conversion behavior for such values is unspecified, and we
+  // don't care about matching it.
+  return (sizeof(wchar_t) * CHAR_BIT <= 16) ? wchar_t{0xffff}
+                                            : static_cast<wchar_t>(0x10ffff);
+}
+
 TYPED_TEST_P(TypedFormatConvertTest, Char) {
   // Pass a bunch of values of type TypeParam to both FormatPack and libc's
   // vsnprintf("%c", ...) (wrapped in StrPrint) to make sure we get the same
@@ -475,28 +649,50 @@
   // std::numeric_limits::max(), too, but vsnprintf("%c", ...) can't handle
   // anything larger than an int. Add in the most extreme values we can without
   // exceeding that range.
+  // Special case: Formatting a wchar_t should behave like vsnprintf("%lc").
+  // Technically vsnprintf can accept a wint_t in this case, but since we must
+  // pass a wchar_t to FormatPack, the largest type we can use here is wchar_t.
+  using ArgType =
+      std::conditional_t<std::is_same<T, wchar_t>::value, wchar_t, int>;
   static const T kMin =
-      static_cast<remove_volatile_t>(std::numeric_limits<int>::min());
-  static const T kMax =
-      static_cast<remove_volatile_t>(std::numeric_limits<int>::max());
-  vals.insert(vals.end(), {kMin + 1, kMin, kMax - 1, kMax});
+      static_cast<remove_volatile_t>(std::numeric_limits<ArgType>::min());
+  static const T kMax = GetMaxForConversion<T>();
+  vals.insert(vals.end(), {static_cast<remove_volatile_t>(kMin + 1), kMin,
+                           static_cast<remove_volatile_t>(kMax - 1), kMax});
 
+  static const auto kMaxWCharT =
+      static_cast<remove_volatile_t>(GetMaxForConversion<wchar_t>());
   for (const T c : vals) {
+    SCOPED_TRACE(Esc(c));
     const FormatArgImpl args[] = {FormatArgImpl(c)};
     UntypedFormatSpecImpl format("%c");
-    EXPECT_EQ(StrPrint("%c", static_cast<int>(c)),
-              FormatPack(format, absl::MakeSpan(args)));
+    absl::optional<std::string> result = StrPrintChar(c);
+    if (result.has_value()) {
+      EXPECT_EQ(result.value(), FormatPack(format, absl::MakeSpan(args)));
+    }
+
+    // Also test that if the format specifier is "%lc", the argument is treated
+    // as if it's a `wchar_t`.
+    const T wc =
+        std::max(remove_volatile_t{0},
+                 std::min(static_cast<remove_volatile_t>(c), kMaxWCharT));
+    SCOPED_TRACE(Esc(wc));
+    const FormatArgImpl wide_args[] = {FormatArgImpl(wc)};
+    UntypedFormatSpecImpl wide_format("%lc");
+    result = StrPrintChar(static_cast<wchar_t>(wc));
+    if (result.has_value()) {
+      EXPECT_EQ(result.value(),
+                FormatPack(wide_format, absl::MakeSpan(wide_args)));
+    }
   }
 }
 
 REGISTER_TYPED_TEST_SUITE_P(TypedFormatConvertTest, AllIntsWithFlags, Char);
 
-typedef ::testing::Types<
-    int, unsigned, volatile int,
-    short, unsigned short,
-    long, unsigned long,
-    long long, unsigned long long,
-    signed char, unsigned char, char>
+typedef ::testing::Types<int, unsigned, volatile int, short,   // NOLINT
+                         unsigned short, long, unsigned long,  // NOLINT
+                         long long, unsigned long long,        // NOLINT
+                         signed char, unsigned char, char, wchar_t>
     AllIntTypes;
 INSTANTIATE_TYPED_TEST_SUITE_P(TypedFormatConvertTestWithAllIntTypes,
                                TypedFormatConvertTest, AllIntTypes);
@@ -511,6 +707,22 @@
                             FormatArgImpl(cv[0]), FormatArgImpl(cv[1])})));
 }
 
+TEST_F(FormatConvertTest, UnicodeWideString) {
+  // StrFormat() should be able to convert wide strings containing Unicode
+  // characters (to UTF-8).
+  const FormatArgImpl args[] = {FormatArgImpl(L"\u47e3 \U00011112")};
+  // `u8""` forces UTF-8 encoding; MSVC will default to e.g. CP1252 (and warn)
+  // without it. However, the resulting character type differs between pre-C++20
+  // (`char`) and C++20 (`char8_t`). So deduce the right character type for all
+  // C++ versions, init it with UTF-8, then `memcpy()` to get the result as a
+  // `char*`.
+  using ConstChar8T = std::remove_reference_t<decltype(*u8"a")>;
+  ConstChar8T kOutputUtf8[] = u8"\u47e3 \U00011112";
+  char output[sizeof kOutputUtf8];
+  std::memcpy(output, kOutputUtf8, sizeof kOutputUtf8);
+  EXPECT_EQ(output,
+            FormatPack(UntypedFormatSpecImpl("%ls"), absl::MakeSpan(args)));
+}
 
 TEST_F(FormatConvertTest, Int128) {
   absl::int128 positive = static_cast<absl::int128>(0x1234567890abcdef) * 1979;
@@ -1068,7 +1280,7 @@
 // We don't actually store the results. This is just to exercise the rest of the
 // machinery.
 struct NullSink {
-  friend void AbslFormatFlush(NullSink *sink, string_view str) {}
+  friend void AbslFormatFlush(NullSink *, string_view) {}
 };
 
 template <typename... T>
diff --git a/absl/strings/internal/str_format/extension.h b/absl/strings/internal/str_format/extension.h
index 8de42d2..173284c 100644
--- a/absl/strings/internal/str_format/extension.h
+++ b/absl/strings/internal/str_format/extension.h
@@ -16,16 +16,14 @@
 #ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_EXTENSION_H_
 #define ABSL_STRINGS_INTERNAL_STR_FORMAT_EXTENSION_H_
 
-#include <limits.h>
 
 #include <cstddef>
 #include <cstdint>
 #include <cstring>
 #include <ostream>
+#include <string>
 
 #include "absl/base/config.h"
-#include "absl/base/port.h"
-#include "absl/meta/type_traits.h"
 #include "absl/strings/internal/str_format/output.h"
 #include "absl/strings/string_view.h"
 
@@ -34,6 +32,7 @@
 
 enum class FormatConversionChar : uint8_t;
 enum class FormatConversionCharSet : uint64_t;
+enum class LengthMod : std::uint8_t { h, hh, l, ll, L, j, z, t, q, none };
 
 namespace str_format_internal {
 
@@ -139,7 +138,8 @@
   kAlt = 1 << 3,
   kZero = 1 << 4,
   // This is not a real flag. It just exists to turn off kBasic when no other
-  // flags are set. This is for when width/precision are specified.
+  // flags are set. This is for when width/precision are specified, or a length
+  // modifier affects the behavior ("%lc").
   kNonBasic = 1 << 5,
 };
 
@@ -285,6 +285,8 @@
   bool has_alt_flag() const { return FlagsContains(flags_, Flags::kAlt); }
   bool has_zero_flag() const { return FlagsContains(flags_, Flags::kZero); }
 
+  LengthMod length_mod() const { return length_mod_; }
+
   FormatConversionChar conversion_char() const {
     // Keep this field first in the struct . It generates better code when
     // accessing it when ConversionSpec is passed by value in registers.
@@ -310,6 +312,7 @@
   friend struct str_format_internal::FormatConversionSpecImplFriend;
   FormatConversionChar conv_ = FormatConversionCharInternal::kNone;
   Flags flags_;
+  LengthMod length_mod_ = LengthMod::none;
   int width_;
   int precision_;
 };
@@ -318,6 +321,9 @@
   static void SetFlags(Flags f, FormatConversionSpecImpl* conv) {
     conv->flags_ = f;
   }
+  static void SetLengthMod(LengthMod l, FormatConversionSpecImpl* conv) {
+    conv->length_mod_ = l;
+  }
   static void SetConversionChar(FormatConversionChar c,
                                 FormatConversionSpecImpl* conv) {
     conv->conv_ = c;
diff --git a/absl/strings/internal/str_format/parser.h b/absl/strings/internal/str_format/parser.h
index 35b6d49..b1d6d5f 100644
--- a/absl/strings/internal/str_format/parser.h
+++ b/absl/strings/internal/str_format/parser.h
@@ -15,22 +15,23 @@
 #ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_PARSER_H_
 #define ABSL_STRINGS_INTERNAL_STR_FORMAT_PARSER_H_
 
-#include <limits.h>
 #include <stddef.h>
 #include <stdlib.h>
 
 #include <cassert>
-#include <cstdint>
+#include <cstring>
 #include <initializer_list>
-#include <iosfwd>
-#include <iterator>
 #include <memory>
 #include <string>
+#include <utility>
 #include <vector>
 
+#include "absl/base/config.h"
+#include "absl/base/optimization.h"
 #include "absl/strings/internal/str_format/checker.h"
 #include "absl/strings/internal/str_format/constexpr_parser.h"
 #include "absl/strings/internal/str_format/extension.h"
+#include "absl/strings/string_view.h"
 
 namespace absl {
 ABSL_NAMESPACE_BEGIN
diff --git a/absl/strings/internal/str_format/parser_test.cc b/absl/strings/internal/str_format/parser_test.cc
index 021f6a8..e2225c6 100644
--- a/absl/strings/internal/str_format/parser_test.cc
+++ b/absl/strings/internal/str_format/parser_test.cc
@@ -15,10 +15,18 @@
 #include "absl/strings/internal/str_format/parser.h"
 
 #include <string.h>
+#include <algorithm>
+#include <initializer_list>
+#include <string>
+#include <utility>
 
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
+#include "absl/base/config.h"
 #include "absl/base/macros.h"
+#include "absl/strings/internal/str_format/constexpr_parser.h"
+#include "absl/strings/internal/str_format/extension.h"
+#include "absl/strings/string_view.h"
 
 namespace absl {
 ABSL_NAMESPACE_BEGIN
@@ -303,7 +311,7 @@
   }
 
   // Flag is off
-  for (const char* fmt : {"3d", ".llx", "-G", "1$#X"}) {
+  for (const char* fmt : {"3d", ".llx", "-G", "1$#X", "lc"}) {
     SCOPED_TRACE(fmt);
     EXPECT_TRUE(Run(fmt));
     EXPECT_NE(o.flags, Flags::kBasic);
diff --git a/absl/strings/numbers.cc b/absl/strings/numbers.cc
index c4f2103..b57d9e8 100644
--- a/absl/strings/numbers.cc
+++ b/absl/strings/numbers.cc
@@ -34,6 +34,7 @@
 #include "absl/base/config.h"
 #include "absl/base/internal/endian.h"
 #include "absl/base/internal/raw_logging.h"
+#include "absl/base/nullability.h"
 #include "absl/base/optimization.h"
 #include "absl/numeric/bits.h"
 #include "absl/numeric/int128.h"
@@ -45,7 +46,7 @@
 namespace absl {
 ABSL_NAMESPACE_BEGIN
 
-bool SimpleAtof(absl::string_view str, float* out) {
+bool SimpleAtof(absl::string_view str, absl::Nonnull<float*> out) {
   *out = 0.0;
   str = StripAsciiWhitespace(str);
   // std::from_chars doesn't accept an initial +, but SimpleAtof does, so if one
@@ -76,7 +77,7 @@
   return true;
 }
 
-bool SimpleAtod(absl::string_view str, double* out) {
+bool SimpleAtod(absl::string_view str, absl::Nonnull<double*> out) {
   *out = 0.0;
   str = StripAsciiWhitespace(str);
   // std::from_chars doesn't accept an initial +, but SimpleAtod does, so if one
@@ -107,7 +108,7 @@
   return true;
 }
 
-bool SimpleAtob(absl::string_view str, bool* out) {
+bool SimpleAtob(absl::string_view str, absl::Nonnull<bool*> out) {
   ABSL_RAW_CHECK(out != nullptr, "Output pointer must not be nullptr.");
   if (EqualsIgnoreCase(str, "true") || EqualsIgnoreCase(str, "t") ||
       EqualsIgnoreCase(str, "yes") || EqualsIgnoreCase(str, "y") ||
@@ -166,7 +167,7 @@
 constexpr uint64_t kDivisionBy100Div = 1 << 20;
 
 // Encode functions write the ASCII output of input `n` to `out_str`.
-inline char* EncodeHundred(uint32_t n, char* out_str) {
+inline char* EncodeHundred(uint32_t n, absl::Nonnull<char*> out_str) {
   int num_digits = static_cast<int>(n - 10) >> 8;
   uint32_t div10 = (n * kDivisionBy10Mul) / kDivisionBy10Div;
   uint32_t mod10 = n - 10u * div10;
@@ -176,7 +177,7 @@
   return out_str + 2 + num_digits;
 }
 
-inline char* EncodeTenThousand(uint32_t n, char* out_str) {
+inline char* EncodeTenThousand(uint32_t n, absl::Nonnull<char*> out_str) {
   // We split lower 2 digits and upper 2 digits of n into 2 byte consecutive
   // blocks. 123 ->  [\0\1][\0\23]. We divide by 10 both blocks
   // (it's 1 division + zeroing upper bits), and compute modulo 10 as well "in
@@ -232,8 +233,8 @@
   return tens;
 }
 
-inline ABSL_ATTRIBUTE_ALWAYS_INLINE char* EncodeFullU32(uint32_t n,
-                                                        char* out_str) {
+inline ABSL_ATTRIBUTE_ALWAYS_INLINE absl::Nonnull<char*> EncodeFullU32(
+    uint32_t n, absl::Nonnull<char*> out_str) {
   if (n < 10) {
     *out_str = static_cast<char>('0' + n);
     return out_str + 1;
@@ -282,7 +283,7 @@
 
 }  // namespace
 
-void numbers_internal::PutTwoDigits(uint32_t i, char* buf) {
+void numbers_internal::PutTwoDigits(uint32_t i, absl::Nonnull<char*> buf) {
   assert(i < 100);
   uint32_t base = kTwoZeroBytes;
   uint32_t div10 = (i * kDivisionBy10Mul) / kDivisionBy10Div;
@@ -291,13 +292,15 @@
   little_endian::Store16(buf, static_cast<uint16_t>(base));
 }
 
-char* numbers_internal::FastIntToBuffer(uint32_t n, char* out_str) {
+absl::Nonnull<char*> numbers_internal::FastIntToBuffer(
+    uint32_t n, absl::Nonnull<char*> out_str) {
   out_str = EncodeFullU32(n, out_str);
   *out_str = '\0';
   return out_str;
 }
 
-char* numbers_internal::FastIntToBuffer(int32_t i, char* buffer) {
+absl::Nonnull<char*> numbers_internal::FastIntToBuffer(
+    int32_t i, absl::Nonnull<char*> buffer) {
   uint32_t u = static_cast<uint32_t>(i);
   if (i < 0) {
     *buffer++ = '-';
@@ -311,13 +314,15 @@
   return buffer;
 }
 
-char* numbers_internal::FastIntToBuffer(uint64_t i, char* buffer) {
+absl::Nonnull<char*> numbers_internal::FastIntToBuffer(
+    uint64_t i, absl::Nonnull<char*> buffer) {
   buffer = EncodeFullU64(i, buffer);
   *buffer = '\0';
   return buffer;
 }
 
-char* numbers_internal::FastIntToBuffer(int64_t i, char* buffer) {
+absl::Nonnull<char*> numbers_internal::FastIntToBuffer(
+    int64_t i, absl::Nonnull<char*> buffer) {
   uint64_t u = static_cast<uint64_t>(i);
   if (i < 0) {
     *buffer++ = '-';
@@ -538,7 +543,8 @@
 
 // Helper function for fast formatting of floating-point.
 // The result is the same as "%g", a.k.a. "%.6g".
-size_t numbers_internal::SixDigitsToBuffer(double d, char* const buffer) {
+size_t numbers_internal::SixDigitsToBuffer(double d,
+                                           absl::Nonnull<char*> const buffer) {
   static_assert(std::numeric_limits<float>::is_iec559,
                 "IEEE-754/IEC-559 support only");
 
@@ -685,9 +691,10 @@
     36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36};
 
 // Parse the sign and optional hex or oct prefix in text.
-inline bool safe_parse_sign_and_base(absl::string_view* text /*inout*/,
-                                     int* base_ptr /*inout*/,
-                                     bool* negative_ptr /*output*/) {
+inline bool safe_parse_sign_and_base(
+    absl::Nonnull<absl::string_view*> text /*inout*/,
+    absl::Nonnull<int*> base_ptr /*inout*/,
+    absl::Nonnull<bool*> negative_ptr /*output*/) {
   if (text->data() == nullptr) {
     return false;
   }
@@ -972,7 +979,7 @@
 
 template <typename IntType>
 inline bool safe_parse_positive_int(absl::string_view text, int base,
-                                    IntType* value_p) {
+                                    absl::Nonnull<IntType*> value_p) {
   IntType value = 0;
   const IntType vmax = std::numeric_limits<IntType>::max();
   assert(vmax > 0);
@@ -1009,7 +1016,7 @@
 
 template <typename IntType>
 inline bool safe_parse_negative_int(absl::string_view text, int base,
-                                    IntType* value_p) {
+                                    absl::Nonnull<IntType*> value_p) {
   IntType value = 0;
   const IntType vmin = std::numeric_limits<IntType>::min();
   assert(vmin < 0);
@@ -1053,8 +1060,8 @@
 // Input format based on POSIX.1-2008 strtol
 // http://pubs.opengroup.org/onlinepubs/9699919799/functions/strtol.html
 template <typename IntType>
-inline bool safe_int_internal(absl::string_view text, IntType* value_p,
-                              int base) {
+inline bool safe_int_internal(absl::string_view text,
+                              absl::Nonnull<IntType*> value_p, int base) {
   *value_p = 0;
   bool negative;
   if (!safe_parse_sign_and_base(&text, &base, &negative)) {
@@ -1068,8 +1075,8 @@
 }
 
 template <typename IntType>
-inline bool safe_uint_internal(absl::string_view text, IntType* value_p,
-                               int base) {
+inline bool safe_uint_internal(absl::string_view text,
+                               absl::Nonnull<IntType*> value_p, int base) {
   *value_p = 0;
   bool negative;
   if (!safe_parse_sign_and_base(&text, &base, &negative) || negative) {
@@ -1103,27 +1110,33 @@
     "e0e1e2e3e4e5e6e7e8e9eaebecedeeef"
     "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff";
 
-bool safe_strto32_base(absl::string_view text, int32_t* value, int base) {
+bool safe_strto32_base(absl::string_view text, absl::Nonnull<int32_t*> value,
+                       int base) {
   return safe_int_internal<int32_t>(text, value, base);
 }
 
-bool safe_strto64_base(absl::string_view text, int64_t* value, int base) {
+bool safe_strto64_base(absl::string_view text, absl::Nonnull<int64_t*> value,
+                       int base) {
   return safe_int_internal<int64_t>(text, value, base);
 }
 
-bool safe_strto128_base(absl::string_view text, int128* value, int base) {
+bool safe_strto128_base(absl::string_view text, absl::Nonnull<int128*> value,
+                        int base) {
   return safe_int_internal<absl::int128>(text, value, base);
 }
 
-bool safe_strtou32_base(absl::string_view text, uint32_t* value, int base) {
+bool safe_strtou32_base(absl::string_view text, absl::Nonnull<uint32_t*> value,
+                        int base) {
   return safe_uint_internal<uint32_t>(text, value, base);
 }
 
-bool safe_strtou64_base(absl::string_view text, uint64_t* value, int base) {
+bool safe_strtou64_base(absl::string_view text, absl::Nonnull<uint64_t*> value,
+                        int base) {
   return safe_uint_internal<uint64_t>(text, value, base);
 }
 
-bool safe_strtou128_base(absl::string_view text, uint128* value, int base) {
+bool safe_strtou128_base(absl::string_view text, absl::Nonnull<uint128*> value,
+                         int base) {
   return safe_uint_internal<absl::uint128>(text, value, base);
 }
 
diff --git a/absl/strings/numbers.h b/absl/strings/numbers.h
index d7630ce..1d929de 100644
--- a/absl/strings/numbers.h
+++ b/absl/strings/numbers.h
@@ -42,6 +42,7 @@
 #include "absl/base/config.h"
 #include "absl/base/internal/endian.h"
 #include "absl/base/macros.h"
+#include "absl/base/nullability.h"
 #include "absl/base/port.h"
 #include "absl/numeric/bits.h"
 #include "absl/numeric/int128.h"
@@ -59,7 +60,8 @@
 // encountered, this function returns `false`, leaving `out` in an unspecified
 // state.
 template <typename int_type>
-ABSL_MUST_USE_RESULT bool SimpleAtoi(absl::string_view str, int_type* out);
+ABSL_MUST_USE_RESULT bool SimpleAtoi(absl::string_view str,
+                                     absl::Nonnull<int_type*> out);
 
 // SimpleAtof()
 //
@@ -70,7 +72,8 @@
 // allowed formats for `str`, except SimpleAtof() is locale-independent and will
 // always use the "C" locale. If any errors are encountered, this function
 // returns `false`, leaving `out` in an unspecified state.
-ABSL_MUST_USE_RESULT bool SimpleAtof(absl::string_view str, float* out);
+ABSL_MUST_USE_RESULT bool SimpleAtof(absl::string_view str,
+                                     absl::Nonnull<float*> out);
 
 // SimpleAtod()
 //
@@ -81,7 +84,8 @@
 // allowed formats for `str`, except SimpleAtod is locale-independent and will
 // always use the "C" locale. If any errors are encountered, this function
 // returns `false`, leaving `out` in an unspecified state.
-ABSL_MUST_USE_RESULT bool SimpleAtod(absl::string_view str, double* out);
+ABSL_MUST_USE_RESULT bool SimpleAtod(absl::string_view str,
+                                     absl::Nonnull<double*> out);
 
 // SimpleAtob()
 //
@@ -91,7 +95,8 @@
 // are interpreted as boolean `false`: "false", "f", "no", "n", "0". If any
 // errors are encountered, this function returns `false`, leaving `out` in an
 // unspecified state.
-ABSL_MUST_USE_RESULT bool SimpleAtob(absl::string_view str, bool* out);
+ABSL_MUST_USE_RESULT bool SimpleAtob(absl::string_view str,
+                                     absl::Nonnull<bool*> out);
 
 // SimpleHexAtoi()
 //
@@ -104,13 +109,14 @@
 // by this function. If any errors are encountered, this function returns
 // `false`, leaving `out` in an unspecified state.
 template <typename int_type>
-ABSL_MUST_USE_RESULT bool SimpleHexAtoi(absl::string_view str, int_type* out);
+ABSL_MUST_USE_RESULT bool SimpleHexAtoi(absl::string_view str,
+                                        absl::Nonnull<int_type*> out);
 
 // Overloads of SimpleHexAtoi() for 128 bit integers.
-ABSL_MUST_USE_RESULT inline bool SimpleHexAtoi(absl::string_view str,
-                                               absl::int128* out);
-ABSL_MUST_USE_RESULT inline bool SimpleHexAtoi(absl::string_view str,
-                                               absl::uint128* out);
+ABSL_MUST_USE_RESULT inline bool SimpleHexAtoi(
+    absl::string_view str, absl::Nonnull<absl::int128*> out);
+ABSL_MUST_USE_RESULT inline bool SimpleHexAtoi(
+    absl::string_view str, absl::Nonnull<absl::uint128*> out);
 
 ABSL_NAMESPACE_END
 }  // namespace absl
@@ -132,18 +138,22 @@
 //   PutTwoDigits(42, buf);
 //   // buf[0] == '4'
 //   // buf[1] == '2'
-void PutTwoDigits(uint32_t i, char* buf);
+void PutTwoDigits(uint32_t i, absl::Nonnull<char*> buf);
 
 // safe_strto?() functions for implementing SimpleAtoi()
 
-bool safe_strto32_base(absl::string_view text, int32_t* value, int base);
-bool safe_strto64_base(absl::string_view text, int64_t* value, int base);
-bool safe_strto128_base(absl::string_view text, absl::int128* value,
-                         int base);
-bool safe_strtou32_base(absl::string_view text, uint32_t* value, int base);
-bool safe_strtou64_base(absl::string_view text, uint64_t* value, int base);
-bool safe_strtou128_base(absl::string_view text, absl::uint128* value,
-                         int base);
+bool safe_strto32_base(absl::string_view text, absl::Nonnull<int32_t*> value,
+                       int base);
+bool safe_strto64_base(absl::string_view text, absl::Nonnull<int64_t*> value,
+                       int base);
+bool safe_strto128_base(absl::string_view text,
+                        absl::Nonnull<absl::int128*> value, int base);
+bool safe_strtou32_base(absl::string_view text, absl::Nonnull<uint32_t*> value,
+                        int base);
+bool safe_strtou64_base(absl::string_view text, absl::Nonnull<uint64_t*> value,
+                        int base);
+bool safe_strtou128_base(absl::string_view text,
+                         absl::Nonnull<absl::uint128*> value, int base);
 
 static const int kFastToBufferSize = 32;
 static const int kSixDigitsToBufferSize = 16;
@@ -154,20 +164,20 @@
 // outside the range 0.0001-999999 are output using scientific notation
 // (1.23456e+06). This routine is heavily optimized.
 // Required buffer size is `kSixDigitsToBufferSize`.
-size_t SixDigitsToBuffer(double d, char* buffer);
+size_t SixDigitsToBuffer(double d, absl::Nonnull<char*> buffer);
 
 // These functions are intended for speed. All functions take an output buffer
 // as an argument and return a pointer to the last byte they wrote, which is the
 // terminating '\0'. At most `kFastToBufferSize` bytes are written.
-char* FastIntToBuffer(int32_t, char*);
-char* FastIntToBuffer(uint32_t, char*);
-char* FastIntToBuffer(int64_t, char*);
-char* FastIntToBuffer(uint64_t, char*);
+absl::Nonnull<char*> FastIntToBuffer(int32_t, absl::Nonnull<char*>);
+absl::Nonnull<char*> FastIntToBuffer(uint32_t, absl::Nonnull<char*>);
+absl::Nonnull<char*> FastIntToBuffer(int64_t, absl::Nonnull<char*>);
+absl::Nonnull<char*> FastIntToBuffer(uint64_t, absl::Nonnull<char*>);
 
 // For enums and integer types that are not an exact match for the types above,
 // use templates to call the appropriate one of the four overloads above.
 template <typename int_type>
-char* FastIntToBuffer(int_type i, char* buffer) {
+absl::Nonnull<char*> FastIntToBuffer(int_type i, absl::Nonnull<char*> buffer) {
   static_assert(sizeof(i) <= 64 / 8,
                 "FastIntToBuffer works only with 64-bit-or-less integers.");
   // TODO(jorg): This signed-ness check is used because it works correctly
@@ -194,7 +204,8 @@
 // Implementation of SimpleAtoi, generalized to support arbitrary base (used
 // with base different from 10 elsewhere in Abseil implementation).
 template <typename int_type>
-ABSL_MUST_USE_RESULT bool safe_strtoi_base(absl::string_view s, int_type* out,
+ABSL_MUST_USE_RESULT bool safe_strtoi_base(absl::string_view s,
+                                           absl::Nonnull<int_type*> out,
                                            int base) {
   static_assert(sizeof(*out) == 4 || sizeof(*out) == 8,
                 "SimpleAtoi works only with 32-bit or 64-bit integers.");
@@ -237,7 +248,7 @@
 // without the terminating null character. Thus `out` must be of length >= 16.
 // Returns the number of non-pad digits of the output (it can never be zero
 // since 0 has one digit).
-inline size_t FastHexToBufferZeroPad16(uint64_t val, char* out) {
+inline size_t FastHexToBufferZeroPad16(uint64_t val, absl::Nonnull<char*> out) {
 #ifdef ABSL_INTERNAL_HAVE_SSSE3
   uint64_t be = absl::big_endian::FromHost64(val);
   const auto kNibbleMask = _mm_set1_epi8(0xf);
@@ -263,32 +274,34 @@
 }  // namespace numbers_internal
 
 template <typename int_type>
-ABSL_MUST_USE_RESULT bool SimpleAtoi(absl::string_view str, int_type* out) {
+ABSL_MUST_USE_RESULT bool SimpleAtoi(absl::string_view str,
+                                     absl::Nonnull<int_type*> out) {
   return numbers_internal::safe_strtoi_base(str, out, 10);
 }
 
 ABSL_MUST_USE_RESULT inline bool SimpleAtoi(absl::string_view str,
-                                            absl::int128* out) {
+                                            absl::Nonnull<absl::int128*> out) {
   return numbers_internal::safe_strto128_base(str, out, 10);
 }
 
 ABSL_MUST_USE_RESULT inline bool SimpleAtoi(absl::string_view str,
-                                            absl::uint128* out) {
+                                            absl::Nonnull<absl::uint128*> out) {
   return numbers_internal::safe_strtou128_base(str, out, 10);
 }
 
 template <typename int_type>
-ABSL_MUST_USE_RESULT bool SimpleHexAtoi(absl::string_view str, int_type* out) {
+ABSL_MUST_USE_RESULT bool SimpleHexAtoi(absl::string_view str,
+                                        absl::Nonnull<int_type*> out) {
   return numbers_internal::safe_strtoi_base(str, out, 16);
 }
 
-ABSL_MUST_USE_RESULT inline bool SimpleHexAtoi(absl::string_view str,
-                                               absl::int128* out) {
+ABSL_MUST_USE_RESULT inline bool SimpleHexAtoi(
+    absl::string_view str, absl::Nonnull<absl::int128*> out) {
   return numbers_internal::safe_strto128_base(str, out, 16);
 }
 
-ABSL_MUST_USE_RESULT inline bool SimpleHexAtoi(absl::string_view str,
-                                               absl::uint128* out) {
+ABSL_MUST_USE_RESULT inline bool SimpleHexAtoi(
+    absl::string_view str, absl::Nonnull<absl::uint128*> out) {
   return numbers_internal::safe_strtou128_base(str, out, 16);
 }
 
diff --git a/absl/strings/str_cat.cc b/absl/strings/str_cat.cc
index 900dc69..e7f7052 100644
--- a/absl/strings/str_cat.cc
+++ b/absl/strings/str_cat.cc
@@ -23,6 +23,7 @@
 #include <string>
 
 #include "absl/base/config.h"
+#include "absl/base/nullability.h"
 #include "absl/strings/internal/resize_uninitialized.h"
 #include "absl/strings/string_view.h"
 
@@ -40,7 +41,8 @@
 namespace {
 // Append is merely a version of memcpy that returns the address of the byte
 // after the area just overwritten.
-inline char* Append(char* out, const AlphaNum& x) {
+inline absl::Nonnull<char*> Append(absl::Nonnull<char*> out,
+                                   const AlphaNum& x) {
   // memcpy is allowed to overwrite arbitrary memory, so doing this after the
   // call would force an extra fetch of x.size().
   char* after = out + x.size();
@@ -128,7 +130,7 @@
   assert(((src).size() == 0) ||      \
          (uintptr_t((src).data() - (dest).data()) > uintptr_t((dest).size())))
 
-void AppendPieces(std::string* dest,
+void AppendPieces(absl::Nonnull<std::string*> dest,
                   std::initializer_list<absl::string_view> pieces) {
   size_t old_size = dest->size();
   size_t to_append = 0;
@@ -152,7 +154,7 @@
 
 }  // namespace strings_internal
 
-void StrAppend(std::string* dest, const AlphaNum& a) {
+void StrAppend(absl::Nonnull<std::string*> dest, const AlphaNum& a) {
   ASSERT_NO_OVERLAP(*dest, a);
   std::string::size_type old_size = dest->size();
   STLStringAppendUninitializedAmortized(dest, a.size());
@@ -162,7 +164,8 @@
   assert(out == begin + dest->size());
 }
 
-void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b) {
+void StrAppend(absl::Nonnull<std::string*> dest, const AlphaNum& a,
+               const AlphaNum& b) {
   ASSERT_NO_OVERLAP(*dest, a);
   ASSERT_NO_OVERLAP(*dest, b);
   std::string::size_type old_size = dest->size();
@@ -174,8 +177,8 @@
   assert(out == begin + dest->size());
 }
 
-void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b,
-               const AlphaNum& c) {
+void StrAppend(absl::Nonnull<std::string*> dest, const AlphaNum& a,
+               const AlphaNum& b, const AlphaNum& c) {
   ASSERT_NO_OVERLAP(*dest, a);
   ASSERT_NO_OVERLAP(*dest, b);
   ASSERT_NO_OVERLAP(*dest, c);
@@ -189,8 +192,8 @@
   assert(out == begin + dest->size());
 }
 
-void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b,
-               const AlphaNum& c, const AlphaNum& d) {
+void StrAppend(absl::Nonnull<std::string*> dest, const AlphaNum& a,
+               const AlphaNum& b, const AlphaNum& c, const AlphaNum& d) {
   ASSERT_NO_OVERLAP(*dest, a);
   ASSERT_NO_OVERLAP(*dest, b);
   ASSERT_NO_OVERLAP(*dest, c);
diff --git a/absl/strings/str_cat.h b/absl/strings/str_cat.h
index e782368..68637ce 100644
--- a/absl/strings/str_cat.h
+++ b/absl/strings/str_cat.h
@@ -100,6 +100,7 @@
 #include <vector>
 
 #include "absl/base/attributes.h"
+#include "absl/base/nullability.h"
 #include "absl/base/port.h"
 #include "absl/meta/type_traits.h"
 #include "absl/strings/has_absl_stringify.h"
@@ -206,7 +207,7 @@
                               !std::is_pointer<Int>::value>::type* = nullptr)
       : Hex(spec, static_cast<uint64_t>(v)) {}
   template <typename Pointee>
-  explicit Hex(Pointee* v, PadSpec spec = absl::kNoPad)
+  explicit Hex(absl::Nullable<Pointee*> v, PadSpec spec = absl::kNoPad)
       : Hex(spec, reinterpret_cast<uintptr_t>(v)) {}
 
   template <typename S>
@@ -349,7 +350,7 @@
           ABSL_ATTRIBUTE_LIFETIME_BOUND)
       : piece_(&buf.data[0], buf.size) {}
 
-  AlphaNum(const char* c_str  // NOLINT(runtime/explicit)
+  AlphaNum(absl::Nullable<const char*> c_str  // NOLINT(runtime/explicit)
                ABSL_ATTRIBUTE_LIFETIME_BOUND)
       : piece_(NullSafeStringView(c_str)) {}
   AlphaNum(absl::string_view pc  // NOLINT(runtime/explicit)
@@ -376,7 +377,7 @@
   AlphaNum& operator=(const AlphaNum&) = delete;
 
   absl::string_view::size_type size() const { return piece_.size(); }
-  const char* data() const { return piece_.data(); }
+  absl::Nullable<const char*> data() const { return piece_.data(); }
   absl::string_view Piece() const { return piece_; }
 
   // Match unscoped enums.  Use integral promotion so that a `char`-backed
@@ -446,7 +447,7 @@
 
 // Do not call directly - this is not part of the public API.
 std::string CatPieces(std::initializer_list<absl::string_view> pieces);
-void AppendPieces(std::string* dest,
+void AppendPieces(absl::Nonnull<std::string*> dest,
                   std::initializer_list<absl::string_view> pieces);
 
 template <typename Integer>
@@ -576,19 +577,20 @@
 //   absl::string_view p = s;
 //   StrAppend(&s, p);
 
-inline void StrAppend(std::string*) {}
-void StrAppend(std::string* dest, const AlphaNum& a);
-void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b);
-void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b,
-               const AlphaNum& c);
-void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b,
-               const AlphaNum& c, const AlphaNum& d);
+inline void StrAppend(absl::Nonnull<std::string*>) {}
+void StrAppend(absl::Nonnull<std::string*> dest, const AlphaNum& a);
+void StrAppend(absl::Nonnull<std::string*> dest, const AlphaNum& a,
+               const AlphaNum& b);
+void StrAppend(absl::Nonnull<std::string*> dest, const AlphaNum& a,
+               const AlphaNum& b, const AlphaNum& c);
+void StrAppend(absl::Nonnull<std::string*> dest, const AlphaNum& a,
+               const AlphaNum& b, const AlphaNum& c, const AlphaNum& d);
 
 // Support 5 or more arguments
 template <typename... AV>
-inline void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b,
-                      const AlphaNum& c, const AlphaNum& d, const AlphaNum& e,
-                      const AV&... args) {
+inline void StrAppend(absl::Nonnull<std::string*> dest, const AlphaNum& a,
+                      const AlphaNum& b, const AlphaNum& c, const AlphaNum& d,
+                      const AlphaNum& e, const AV&... args) {
   strings_internal::AppendPieces(
       dest, {a.Piece(), b.Piece(), c.Piece(), d.Piece(), e.Piece(),
              static_cast<const AlphaNum&>(args).Piece()...});
diff --git a/absl/strings/str_format.h b/absl/strings/str_format.h
index 21ee179..66b6af5 100644
--- a/absl/strings/str_format.h
+++ b/absl/strings/str_format.h
@@ -72,14 +72,21 @@
 #ifndef ABSL_STRINGS_STR_FORMAT_H_
 #define ABSL_STRINGS_STR_FORMAT_H_
 
+#include <cstdint>
 #include <cstdio>
 #include <string>
+#include <type_traits>
 
+#include "absl/base/attributes.h"
+#include "absl/base/config.h"
+#include "absl/base/nullability.h"
 #include "absl/strings/internal/str_format/arg.h"  // IWYU pragma: export
 #include "absl/strings/internal/str_format/bind.h"  // IWYU pragma: export
 #include "absl/strings/internal/str_format/checker.h"  // IWYU pragma: export
 #include "absl/strings/internal/str_format/extension.h"  // IWYU pragma: export
 #include "absl/strings/internal/str_format/parser.h"  // IWYU pragma: export
+#include "absl/strings/string_view.h"
+#include "absl/types/span.h"
 
 namespace absl {
 ABSL_NAMESPACE_BEGIN
@@ -104,7 +111,8 @@
   explicit UntypedFormatSpec(string_view s) : spec_(s) {}
 
  protected:
-  explicit UntypedFormatSpec(const str_format_internal::ParsedFormatBase* pc)
+  explicit UntypedFormatSpec(
+      absl::Nonnull<const str_format_internal::ParsedFormatBase*> pc)
       : spec_(pc) {}
 
  private:
@@ -144,7 +152,7 @@
 //   EXPECT_EQ(8, n);
 class FormatCountCapture {
  public:
-  explicit FormatCountCapture(int* p) : p_(p) {}
+  explicit FormatCountCapture(absl::Nonnull<int*> p) : p_(p) {}
 
  private:
   // FormatCountCaptureHelper is used to define FormatConvertImpl() for this
@@ -153,8 +161,8 @@
   // Unused() is here because of the false positive from -Wunused-private-field
   // p_ is used in the templated function of the friend FormatCountCaptureHelper
   // class.
-  int* Unused() { return p_; }
-  int* p_;
+  absl::Nonnull<int*> Unused() { return p_; }
+  absl::Nonnull<int*> p_;
 };
 
 // FormatSpec
@@ -256,7 +264,7 @@
 //
 // The `FormatSpec` intrinsically supports all of these fundamental C++ types:
 //
-// *   Characters: `char`, `signed char`, `unsigned char`
+// *   Characters: `char`, `signed char`, `unsigned char`, `wchar_t`
 // *   Integers: `int`, `short`, `unsigned short`, `unsigned`, `long`,
 //         `unsigned long`, `long long`, `unsigned long long`
 // *   Enums: printed as their underlying integral value
@@ -264,9 +272,9 @@
 //
 // However, in the `str_format` library, a format conversion specifies a broader
 // C++ conceptual category instead of an exact type. For example, `%s` binds to
-// any string-like argument, so `std::string`, `absl::string_view`, and
-// `const char*` are all accepted. Likewise, `%d` accepts any integer-like
-// argument, etc.
+// any string-like argument, so `std::string`, `std::wstring`,
+// `absl::string_view`, `const char*`, and `const wchar_t*` are all accepted.
+// Likewise, `%d` accepts any integer-like argument, etc.
 
 template <typename... Args>
 using FormatSpec = str_format_internal::FormatSpecTemplate<
@@ -369,7 +377,7 @@
 //   std::string orig("For example PI is approximately ");
 //   std::cout << StrAppendFormat(&orig, "%12.6f", 3.14);
 template <typename... Args>
-std::string& StrAppendFormat(std::string* dst,
+std::string& StrAppendFormat(absl::Nonnull<std::string*> dst,
                              const FormatSpec<Args...>& format,
                              const Args&... args) {
   return str_format_internal::AppendPack(
@@ -429,7 +437,7 @@
 //   Outputs: "The capital of Mongolia is Ulaanbaatar"
 //
 template <typename... Args>
-int FPrintF(std::FILE* output, const FormatSpec<Args...>& format,
+int FPrintF(absl::Nonnull<std::FILE*> output, const FormatSpec<Args...>& format,
             const Args&... args) {
   return str_format_internal::FprintF(
       output, str_format_internal::UntypedFormatSpecImpl::Extract(format),
@@ -458,8 +466,8 @@
 //   Post-condition: output == "The capital of Mongolia is Ulaanbaatar"
 //
 template <typename... Args>
-int SNPrintF(char* output, std::size_t size, const FormatSpec<Args...>& format,
-             const Args&... args) {
+int SNPrintF(absl::Nonnull<char*> output, std::size_t size,
+             const FormatSpec<Args...>& format, const Args&... args) {
   return str_format_internal::SnprintF(
       output, size, str_format_internal::UntypedFormatSpecImpl::Extract(format),
       {str_format_internal::FormatArgImpl(args)...});
@@ -492,7 +500,7 @@
   template <typename T,
             typename = typename std::enable_if<std::is_constructible<
                 str_format_internal::FormatRawSinkImpl, T*>::value>::type>
-  FormatRawSink(T* raw)  // NOLINT
+  FormatRawSink(absl::Nonnull<T*> raw)  // NOLINT
       : sink_(raw) {}
 
  private:
@@ -849,14 +857,16 @@
   }
 
   // Support `absl::Format(&sink, format, args...)`.
-  friend void AbslFormatFlush(FormatSink* sink, absl::string_view v) {
+  friend void AbslFormatFlush(absl::Nonnull<FormatSink*> sink,
+                              absl::string_view v) {
     sink->Append(v);
   }
 
  private:
   friend str_format_internal::FormatSinkImpl;
-  explicit FormatSink(str_format_internal::FormatSinkImpl* s) : sink_(s) {}
-  str_format_internal::FormatSinkImpl* sink_;
+  explicit FormatSink(absl::Nonnull<str_format_internal::FormatSinkImpl*> s)
+      : sink_(s) {}
+  absl::Nonnull<str_format_internal::FormatSinkImpl*> sink_;
 };
 
 // FormatConvertResult
diff --git a/absl/strings/str_format_test.cc b/absl/strings/str_format_test.cc
index 195ef3f..3c52be1 100644
--- a/absl/strings/str_format_test.cc
+++ b/absl/strings/str_format_test.cc
@@ -634,6 +634,10 @@
   const int& something = *reinterpret_cast<const int*>(ptr_value);
   EXPECT_EQ(StrFormat("%p", &something), StrFormat("0x%x", ptr_value));
 
+  // The output of formatting a null pointer is not documented as being a
+  // specific thing, but the attempt should at least compile.
+  (void)StrFormat("%p", nullptr);
+
   // Output widths are supported, with optional flags.
   EXPECT_EQ(StrFormat("%3d", 1), "  1");
   EXPECT_EQ(StrFormat("%3d", 123456), "123456");
diff --git a/absl/strings/str_replace.cc b/absl/strings/str_replace.cc
index 9ce49e5..a7ab52f 100644
--- a/absl/strings/str_replace.cc
+++ b/absl/strings/str_replace.cc
@@ -21,6 +21,7 @@
 #include <vector>
 
 #include "absl/base/config.h"
+#include "absl/base/nullability.h"
 #include "absl/strings/str_cat.h"
 #include "absl/strings/string_view.h"
 
@@ -36,8 +37,8 @@
 // occurred.
 int ApplySubstitutions(
     absl::string_view s,
-    std::vector<strings_internal::ViableSubstitution>* subs_ptr,
-    std::string* result_ptr) {
+    absl::Nonnull<std::vector<strings_internal::ViableSubstitution>*> subs_ptr,
+    absl::Nonnull<std::string*> result_ptr) {
   auto& subs = *subs_ptr;
   int substitutions = 0;
   size_t pos = 0;
@@ -82,7 +83,7 @@
 }
 
 int StrReplaceAll(strings_internal::FixedMapping replacements,
-                  std::string* target) {
+                  absl::Nonnull<std::string*> target) {
   return StrReplaceAll<strings_internal::FixedMapping>(replacements, target);
 }
 
diff --git a/absl/strings/str_replace.h b/absl/strings/str_replace.h
index 273c707..e77ced3 100644
--- a/absl/strings/str_replace.h
+++ b/absl/strings/str_replace.h
@@ -43,6 +43,7 @@
 #include <vector>
 
 #include "absl/base/attributes.h"
+#include "absl/base/nullability.h"
 #include "absl/strings/string_view.h"
 
 namespace absl {
@@ -113,7 +114,7 @@
 int StrReplaceAll(
     std::initializer_list<std::pair<absl::string_view, absl::string_view>>
         replacements,
-    std::string* target);
+    absl::Nonnull<std::string*> target);
 
 // Overload of `StrReplaceAll()` to replace patterns within a given output
 // string *in place* with replacements provided within a container of key/value
@@ -128,7 +129,8 @@
 //  EXPECT_EQ(count, 2);
 //  EXPECT_EQ("if (ptr &lt; &amp;foo)", s);
 template <typename StrToStrMapping>
-int StrReplaceAll(const StrToStrMapping& replacements, std::string* target);
+int StrReplaceAll(const StrToStrMapping& replacements,
+                  absl::Nonnull<std::string*> target);
 
 // Implementation details only, past this point.
 namespace strings_internal {
@@ -185,8 +187,8 @@
 }
 
 int ApplySubstitutions(absl::string_view s,
-                       std::vector<ViableSubstitution>* subs_ptr,
-                       std::string* result_ptr);
+                       absl::Nonnull<std::vector<ViableSubstitution>*> subs_ptr,
+                       absl::Nonnull<std::string*> result_ptr);
 
 }  // namespace strings_internal
 
@@ -201,7 +203,8 @@
 }
 
 template <typename StrToStrMapping>
-int StrReplaceAll(const StrToStrMapping& replacements, std::string* target) {
+int StrReplaceAll(const StrToStrMapping& replacements,
+                  absl::Nonnull<std::string*> target) {
   auto subs = strings_internal::FindSubstitutions(*target, replacements);
   if (subs.empty()) return 0;
 
diff --git a/absl/strings/string_view.cc b/absl/strings/string_view.cc
index 1ee468c..0500ef5 100644
--- a/absl/strings/string_view.cc
+++ b/absl/strings/string_view.cc
@@ -21,6 +21,8 @@
 #include <cstring>
 #include <ostream>
 
+#include "absl/base/nullability.h"
+
 namespace absl {
 ABSL_NAMESPACE_BEGIN
 
@@ -28,8 +30,10 @@
 
 // This is significantly faster for case-sensitive matches with very
 // few possible matches.
-const char* memmatch(const char* phaystack, size_t haylen, const char* pneedle,
-                     size_t neelen) {
+absl::Nullable<const char*> memmatch(absl::Nullable<const char*> phaystack,
+                                     size_t haylen,
+                                     absl::Nullable<const char*> pneedle,
+                                     size_t neelen) {
   if (0 == neelen) {
     return phaystack;  // even if haylen is 0
   }
diff --git a/absl/strings/string_view.h b/absl/strings/string_view.h
index f88b05b..04ca0a3 100644
--- a/absl/strings/string_view.h
+++ b/absl/strings/string_view.h
@@ -37,6 +37,7 @@
 #include <string>
 
 #include "absl/base/attributes.h"
+#include "absl/base/nullability.h"
 #include "absl/base/config.h"
 #include "absl/base/internal/throw_delegate.h"
 #include "absl/base/macros.h"
@@ -162,11 +163,11 @@
  public:
   using traits_type = std::char_traits<char>;
   using value_type = char;
-  using pointer = char*;
-  using const_pointer = const char*;
+  using pointer = absl::Nullable<char*>;
+  using const_pointer = absl::Nullable<const char*>;
   using reference = char&;
   using const_reference = const char&;
-  using const_iterator = const char*;
+  using const_iterator = absl::Nullable<const char*>;
   using iterator = const_iterator;
   using const_reverse_iterator = std::reverse_iterator<const_iterator>;
   using reverse_iterator = const_reverse_iterator;
@@ -194,11 +195,12 @@
   // accepting possibly null strings, use `absl::NullSafeStringView(str)`
   // instead (see below).
   // The length check is skipped since it is unnecessary and causes code bloat.
-  constexpr string_view(const char* str)  // NOLINT(runtime/explicit)
+  constexpr string_view(  // NOLINT(runtime/explicit)
+      absl::Nonnull<const char*> str)
       : ptr_(str), length_(str ? StrlenInternal(str) : 0) {}
 
   // Implicit constructor of a `string_view` from a `const char*` and length.
-  constexpr string_view(const char* data, size_type len)
+  constexpr string_view(absl::Nullable<const char*> data, size_type len)
       : ptr_(data), length_(CheckLengthInternal(len)) {}
 
   // NOTE: Harmlessly omitted to work around gdb bug.
@@ -427,18 +429,21 @@
 
   // Overload of `string_view::compare()` for comparing a `string_view` and a
   // a different C-style string `s`.
-  constexpr int compare(const char* s) const { return compare(string_view(s)); }
+  constexpr int compare(absl::Nonnull<const char*> s) const {
+    return compare(string_view(s));
+  }
 
   // Overload of `string_view::compare()` for comparing a substring of the
   // `string_view` and a different string C-style string `s`.
-  constexpr int compare(size_type pos1, size_type count1, const char* s) const {
+  constexpr int compare(size_type pos1, size_type count1,
+                        absl::Nonnull<const char*> s) const {
     return substr(pos1, count1).compare(string_view(s));
   }
 
   // Overload of `string_view::compare()` for comparing a substring of the
   // `string_view` and a substring of a different C-style string `s`.
-  constexpr int compare(size_type pos1, size_type count1, const char* s,
-                        size_type count2) const {
+  constexpr int compare(size_type pos1, size_type count1,
+                        absl::Nonnull<const char*> s, size_type count2) const {
     return substr(pos1, count1).compare(string_view(s, count2));
   }
 
@@ -457,13 +462,14 @@
 
   // Overload of `string_view::find()` for finding a substring of a different
   // C-style string `s` within the `string_view`.
-  size_type find(const char* s, size_type pos, size_type count) const {
+  size_type find(absl::Nonnull<const char*> s, size_type pos,
+                 size_type count) const {
     return find(string_view(s, count), pos);
   }
 
   // Overload of `string_view::find()` for finding a different C-style string
   // `s` within the `string_view`.
-  size_type find(const char* s, size_type pos = 0) const {
+  size_type find(absl::Nonnull<const char *> s, size_type pos = 0) const {
     return find(string_view(s), pos);
   }
 
@@ -480,13 +486,14 @@
 
   // Overload of `string_view::rfind()` for finding a substring of a different
   // C-style string `s` within the `string_view`.
-  size_type rfind(const char* s, size_type pos, size_type count) const {
+  size_type rfind(absl::Nonnull<const char*> s, size_type pos,
+                  size_type count) const {
     return rfind(string_view(s, count), pos);
   }
 
   // Overload of `string_view::rfind()` for finding a different C-style string
   // `s` within the `string_view`.
-  size_type rfind(const char* s, size_type pos = npos) const {
+  size_type rfind(absl::Nonnull<const char*> s, size_type pos = npos) const {
     return rfind(string_view(s), pos);
   }
 
@@ -505,14 +512,15 @@
 
   // Overload of `string_view::find_first_of()` for finding a substring of a
   // different C-style string `s` within the `string_view`.
-  size_type find_first_of(const char* s, size_type pos,
+  size_type find_first_of(absl::Nonnull<const char*> s, size_type pos,
                           size_type count) const {
     return find_first_of(string_view(s, count), pos);
   }
 
   // Overload of `string_view::find_first_of()` for finding a different C-style
   // string `s` within the `string_view`.
-  size_type find_first_of(const char* s, size_type pos = 0) const {
+  size_type find_first_of(absl::Nonnull<const char*> s,
+                          size_type pos = 0) const {
     return find_first_of(string_view(s), pos);
   }
 
@@ -531,13 +539,15 @@
 
   // Overload of `string_view::find_last_of()` for finding a substring of a
   // different C-style string `s` within the `string_view`.
-  size_type find_last_of(const char* s, size_type pos, size_type count) const {
+  size_type find_last_of(absl::Nonnull<const char*> s, size_type pos,
+                         size_type count) const {
     return find_last_of(string_view(s, count), pos);
   }
 
   // Overload of `string_view::find_last_of()` for finding a different C-style
   // string `s` within the `string_view`.
-  size_type find_last_of(const char* s, size_type pos = npos) const {
+  size_type find_last_of(absl::Nonnull<const char*> s,
+                         size_type pos = npos) const {
     return find_last_of(string_view(s), pos);
   }
 
@@ -554,14 +564,15 @@
 
   // Overload of `string_view::find_first_not_of()` for finding a substring of a
   // different C-style string `s` within the `string_view`.
-  size_type find_first_not_of(const char* s, size_type pos,
+  size_type find_first_not_of(absl::Nonnull<const char*> s, size_type pos,
                               size_type count) const {
     return find_first_not_of(string_view(s, count), pos);
   }
 
   // Overload of `string_view::find_first_not_of()` for finding a different
   // C-style string `s` within the `string_view`.
-  size_type find_first_not_of(const char* s, size_type pos = 0) const {
+  size_type find_first_not_of(absl::Nonnull<const char*> s,
+                              size_type pos = 0) const {
     return find_first_not_of(string_view(s), pos);
   }
 
@@ -579,14 +590,15 @@
 
   // Overload of `string_view::find_last_not_of()` for finding a substring of a
   // different C-style string `s` within the `string_view`.
-  size_type find_last_not_of(const char* s, size_type pos,
+  size_type find_last_not_of(absl::Nonnull<const char*> s, size_type pos,
                              size_type count) const {
     return find_last_not_of(string_view(s, count), pos);
   }
 
   // Overload of `string_view::find_last_not_of()` for finding a different
   // C-style string `s` within the `string_view`.
-  size_type find_last_not_of(const char* s, size_type pos = npos) const {
+  size_type find_last_not_of(absl::Nonnull<const char*> s,
+                             size_type pos = npos) const {
     return find_last_not_of(string_view(s), pos);
   }
 
@@ -646,7 +658,8 @@
   // The constructor from std::string delegates to this constructor.
   // See the comment on that constructor for the rationale.
   struct SkipCheckLengthTag {};
-  string_view(const char* data, size_type len, SkipCheckLengthTag) noexcept
+  string_view(absl::Nullable<const char*> data, size_type len,
+              SkipCheckLengthTag) noexcept
       : ptr_(data), length_(len) {}
 
   static constexpr size_type kMaxSize =
@@ -656,7 +669,7 @@
     return ABSL_HARDENING_ASSERT(len <= kMaxSize), len;
   }
 
-  static constexpr size_type StrlenInternal(const char* str) {
+  static constexpr size_type StrlenInternal(absl::Nonnull<const char*> str) {
 #if defined(_MSC_VER) && _MSC_VER >= 1910 && !defined(__clang__)
     // MSVC 2017+ can evaluate this at compile-time.
     const char* begin = str;
@@ -685,7 +698,7 @@
                                : (compare_result < 0 ? -1 : 1);
   }
 
-  const char* ptr_;
+  absl::Nullable<const char*> ptr_;
   size_type length_;
 };
 
@@ -746,7 +759,7 @@
 // Creates an `absl::string_view` from a pointer `p` even if it's null-valued.
 // This function should be used where an `absl::string_view` can be created from
 // a possibly-null pointer.
-constexpr string_view NullSafeStringView(const char* p) {
+constexpr string_view NullSafeStringView(absl::Nullable<const char*> p) {
   return p ? string_view(p) : string_view();
 }
 
diff --git a/absl/strings/strip.h b/absl/strings/strip.h
index 341e66f..e3cda5b 100644
--- a/absl/strings/strip.h
+++ b/absl/strings/strip.h
@@ -25,6 +25,7 @@
 #include <string>
 
 #include "absl/base/macros.h"
+#include "absl/base/nullability.h"
 #include "absl/strings/ascii.h"
 #include "absl/strings/match.h"
 #include "absl/strings/string_view.h"
@@ -43,7 +44,8 @@
 //   absl::string_view input("abc");
 //   EXPECT_TRUE(absl::ConsumePrefix(&input, "a"));
 //   EXPECT_EQ(input, "bc");
-inline bool ConsumePrefix(absl::string_view* str, absl::string_view expected) {
+inline bool ConsumePrefix(absl::Nonnull<absl::string_view*> str,
+                          absl::string_view expected) {
   if (!absl::StartsWith(*str, expected)) return false;
   str->remove_prefix(expected.size());
   return true;
@@ -59,7 +61,8 @@
 //   absl::string_view input("abcdef");
 //   EXPECT_TRUE(absl::ConsumeSuffix(&input, "def"));
 //   EXPECT_EQ(input, "abc");
-inline bool ConsumeSuffix(absl::string_view* str, absl::string_view expected) {
+inline bool ConsumeSuffix(absl::Nonnull<absl::string_view*> str,
+                          absl::string_view expected) {
   if (!absl::EndsWith(*str, expected)) return false;
   str->remove_suffix(expected.size());
   return true;
diff --git a/absl/strings/substitute.cc b/absl/strings/substitute.cc
index 354c070..dd32c75 100644
--- a/absl/strings/substitute.cc
+++ b/absl/strings/substitute.cc
@@ -22,6 +22,7 @@
 
 #include "absl/base/config.h"
 #include "absl/base/internal/raw_logging.h"
+#include "absl/base/nullability.h"
 #include "absl/strings/ascii.h"
 #include "absl/strings/escaping.h"
 #include "absl/strings/internal/resize_uninitialized.h"
@@ -33,9 +34,9 @@
 ABSL_NAMESPACE_BEGIN
 namespace substitute_internal {
 
-void SubstituteAndAppendArray(std::string* output, absl::string_view format,
-                              const absl::string_view* args_array,
-                              size_t num_args) {
+void SubstituteAndAppendArray(
+    absl::Nonnull<std::string*> output, absl::string_view format,
+    absl::Nullable<const absl::string_view*> args_array, size_t num_args) {
   // Determine total size needed.
   size_t size = 0;
   for (size_t i = 0; i < format.size(); i++) {
@@ -104,7 +105,7 @@
   assert(target == output->data() + output->size());
 }
 
-Arg::Arg(const void* value) {
+Arg::Arg(absl::Nullable<const void*> value) {
   static_assert(sizeof(scratch_) >= sizeof(value) * 2 + 2,
                 "fix sizeof(scratch_)");
   if (value == nullptr) {
diff --git a/absl/strings/substitute.h b/absl/strings/substitute.h
index 0d6cb7c..6c7cba4 100644
--- a/absl/strings/substitute.h
+++ b/absl/strings/substitute.h
@@ -78,6 +78,7 @@
 #include <vector>
 
 #include "absl/base/macros.h"
+#include "absl/base/nullability.h"
 #include "absl/base/port.h"
 #include "absl/strings/ascii.h"
 #include "absl/strings/escaping.h"
@@ -105,7 +106,7 @@
   // Overloads for string-y things
   //
   // Explicitly overload `const char*` so the compiler doesn't cast to `bool`.
-  Arg(const char* value)  // NOLINT(google-explicit-constructor)
+  Arg(absl::Nullable<const char*> value)  // NOLINT(google-explicit-constructor)
       : piece_(absl::NullSafeStringView(value)) {}
   template <typename Allocator>
   Arg(  // NOLINT
@@ -197,7 +198,8 @@
 
   // `void*` values, with the exception of `char*`, are printed as
   // "0x<hex value>". However, in the case of `nullptr`, "NULL" is printed.
-  Arg(const void* value);  // NOLINT(google-explicit-constructor)
+  Arg(  // NOLINT(google-explicit-constructor)
+      absl::Nullable<const void*> value);
 
   // Normal enums are already handled by the integer formatters.
   // This overload matches only scoped enums.
@@ -220,12 +222,12 @@
 
 // Internal helper function. Don't call this from outside this implementation.
 // This interface may change without notice.
-void SubstituteAndAppendArray(std::string* output, absl::string_view format,
-                              const absl::string_view* args_array,
-                              size_t num_args);
+void SubstituteAndAppendArray(
+    absl::Nonnull<std::string*> output, absl::string_view format,
+    absl::Nullable<const absl::string_view*> args_array, size_t num_args);
 
 #if defined(ABSL_BAD_CALL_IF)
-constexpr int CalculateOneBit(const char* format) {
+constexpr int CalculateOneBit(absl::Nonnull<const char*> format) {
   // Returns:
   // * 2^N for '$N' when N is in [0-9]
   // * 0 for correct '$' escaping: '$$'.
@@ -234,11 +236,11 @@
                                           : (1 << (*format - '0'));
 }
 
-constexpr const char* SkipNumber(const char* format) {
+constexpr const char* SkipNumber(absl::Nonnull<const char*> format) {
   return !*format ? format : (format + 1);
 }
 
-constexpr int PlaceholderBitmask(const char* format) {
+constexpr int PlaceholderBitmask(absl::Nonnull<const char*> format) {
   return !*format
              ? 0
              : *format != '$' ? PlaceholderBitmask(format + 1)
@@ -271,18 +273,21 @@
 //    absl::SubstituteAndAppend(boilerplate, format, args...);
 //  }
 //
-inline void SubstituteAndAppend(std::string* output, absl::string_view format) {
+inline void SubstituteAndAppend(absl::Nonnull<std::string*> output,
+                                absl::string_view format) {
   substitute_internal::SubstituteAndAppendArray(output, format, nullptr, 0);
 }
 
-inline void SubstituteAndAppend(std::string* output, absl::string_view format,
+inline void SubstituteAndAppend(absl::Nonnull<std::string*> output,
+                                absl::string_view format,
                                 const substitute_internal::Arg& a0) {
   const absl::string_view args[] = {a0.piece()};
   substitute_internal::SubstituteAndAppendArray(output, format, args,
                                                 ABSL_ARRAYSIZE(args));
 }
 
-inline void SubstituteAndAppend(std::string* output, absl::string_view format,
+inline void SubstituteAndAppend(absl::Nonnull<std::string*> output,
+                                absl::string_view format,
                                 const substitute_internal::Arg& a0,
                                 const substitute_internal::Arg& a1) {
   const absl::string_view args[] = {a0.piece(), a1.piece()};
@@ -290,7 +295,8 @@
                                                 ABSL_ARRAYSIZE(args));
 }
 
-inline void SubstituteAndAppend(std::string* output, absl::string_view format,
+inline void SubstituteAndAppend(absl::Nonnull<std::string*> output,
+                                absl::string_view format,
                                 const substitute_internal::Arg& a0,
                                 const substitute_internal::Arg& a1,
                                 const substitute_internal::Arg& a2) {
@@ -299,7 +305,8 @@
                                                 ABSL_ARRAYSIZE(args));
 }
 
-inline void SubstituteAndAppend(std::string* output, absl::string_view format,
+inline void SubstituteAndAppend(absl::Nonnull<std::string*> output,
+                                absl::string_view format,
                                 const substitute_internal::Arg& a0,
                                 const substitute_internal::Arg& a1,
                                 const substitute_internal::Arg& a2,
@@ -310,7 +317,8 @@
                                                 ABSL_ARRAYSIZE(args));
 }
 
-inline void SubstituteAndAppend(std::string* output, absl::string_view format,
+inline void SubstituteAndAppend(absl::Nonnull<std::string*> output,
+                                absl::string_view format,
                                 const substitute_internal::Arg& a0,
                                 const substitute_internal::Arg& a1,
                                 const substitute_internal::Arg& a2,
@@ -322,27 +330,23 @@
                                                 ABSL_ARRAYSIZE(args));
 }
 
-inline void SubstituteAndAppend(std::string* output, absl::string_view format,
-                                const substitute_internal::Arg& a0,
-                                const substitute_internal::Arg& a1,
-                                const substitute_internal::Arg& a2,
-                                const substitute_internal::Arg& a3,
-                                const substitute_internal::Arg& a4,
-                                const substitute_internal::Arg& a5) {
+inline void SubstituteAndAppend(
+    absl::Nonnull<std::string*> output, absl::string_view format,
+    const substitute_internal::Arg& a0, const substitute_internal::Arg& a1,
+    const substitute_internal::Arg& a2, const substitute_internal::Arg& a3,
+    const substitute_internal::Arg& a4, const substitute_internal::Arg& a5) {
   const absl::string_view args[] = {a0.piece(), a1.piece(), a2.piece(),
                                     a3.piece(), a4.piece(), a5.piece()};
   substitute_internal::SubstituteAndAppendArray(output, format, args,
                                                 ABSL_ARRAYSIZE(args));
 }
 
-inline void SubstituteAndAppend(std::string* output, absl::string_view format,
-                                const substitute_internal::Arg& a0,
-                                const substitute_internal::Arg& a1,
-                                const substitute_internal::Arg& a2,
-                                const substitute_internal::Arg& a3,
-                                const substitute_internal::Arg& a4,
-                                const substitute_internal::Arg& a5,
-                                const substitute_internal::Arg& a6) {
+inline void SubstituteAndAppend(
+    absl::Nonnull<std::string*> output, absl::string_view format,
+    const substitute_internal::Arg& a0, const substitute_internal::Arg& a1,
+    const substitute_internal::Arg& a2, const substitute_internal::Arg& a3,
+    const substitute_internal::Arg& a4, const substitute_internal::Arg& a5,
+    const substitute_internal::Arg& a6) {
   const absl::string_view args[] = {a0.piece(), a1.piece(), a2.piece(),
                                     a3.piece(), a4.piece(), a5.piece(),
                                     a6.piece()};
@@ -351,7 +355,7 @@
 }
 
 inline void SubstituteAndAppend(
-    std::string* output, absl::string_view format,
+    absl::Nonnull<std::string*> output, absl::string_view format,
     const substitute_internal::Arg& a0, const substitute_internal::Arg& a1,
     const substitute_internal::Arg& a2, const substitute_internal::Arg& a3,
     const substitute_internal::Arg& a4, const substitute_internal::Arg& a5,
@@ -364,7 +368,7 @@
 }
 
 inline void SubstituteAndAppend(
-    std::string* output, absl::string_view format,
+    absl::Nonnull<std::string*> output, absl::string_view format,
     const substitute_internal::Arg& a0, const substitute_internal::Arg& a1,
     const substitute_internal::Arg& a2, const substitute_internal::Arg& a3,
     const substitute_internal::Arg& a4, const substitute_internal::Arg& a5,
@@ -378,7 +382,7 @@
 }
 
 inline void SubstituteAndAppend(
-    std::string* output, absl::string_view format,
+    absl::Nonnull<std::string*> output, absl::string_view format,
     const substitute_internal::Arg& a0, const substitute_internal::Arg& a1,
     const substitute_internal::Arg& a2, const substitute_internal::Arg& a3,
     const substitute_internal::Arg& a4, const substitute_internal::Arg& a5,
@@ -394,14 +398,16 @@
 #if defined(ABSL_BAD_CALL_IF)
 // This body of functions catches cases where the number of placeholders
 // doesn't match the number of data arguments.
-void SubstituteAndAppend(std::string* output, const char* format)
+void SubstituteAndAppend(absl::Nonnull<std::string*> output,
+                         absl::Nonnull<const char*> format)
     ABSL_BAD_CALL_IF(
         substitute_internal::PlaceholderBitmask(format) != 0,
         "There were no substitution arguments "
         "but this format string either has a $[0-9] in it or contains "
         "an unescaped $ character (use $$ instead)");
 
-void SubstituteAndAppend(std::string* output, const char* format,
+void SubstituteAndAppend(absl::Nonnull<std::string*> output,
+                         absl::Nonnull<const char*> format,
                          const substitute_internal::Arg& a0)
     ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 1,
                      "There was 1 substitution argument given, but "
@@ -409,7 +415,8 @@
                      "one of $1-$9, or contains an unescaped $ character (use "
                      "$$ instead)");
 
-void SubstituteAndAppend(std::string* output, const char* format,
+void SubstituteAndAppend(absl::Nonnull<std::string*> output,
+                         absl::Nonnull<const char*> format,
                          const substitute_internal::Arg& a0,
                          const substitute_internal::Arg& a1)
     ABSL_BAD_CALL_IF(
@@ -418,7 +425,8 @@
         "missing its $0/$1, contains one of $2-$9, or contains an "
         "unescaped $ character (use $$ instead)");
 
-void SubstituteAndAppend(std::string* output, const char* format,
+void SubstituteAndAppend(absl::Nonnull<std::string*> output,
+                         absl::Nonnull<const char*> format,
                          const substitute_internal::Arg& a0,
                          const substitute_internal::Arg& a1,
                          const substitute_internal::Arg& a2)
@@ -428,7 +436,8 @@
         "this format string is missing its $0/$1/$2, contains one of "
         "$3-$9, or contains an unescaped $ character (use $$ instead)");
 
-void SubstituteAndAppend(std::string* output, const char* format,
+void SubstituteAndAppend(absl::Nonnull<std::string*> output,
+                         absl::Nonnull<const char*> format,
                          const substitute_internal::Arg& a0,
                          const substitute_internal::Arg& a1,
                          const substitute_internal::Arg& a2,
@@ -439,7 +448,8 @@
         "this format string is missing its $0-$3, contains one of "
         "$4-$9, or contains an unescaped $ character (use $$ instead)");
 
-void SubstituteAndAppend(std::string* output, const char* format,
+void SubstituteAndAppend(absl::Nonnull<std::string*> output,
+                         absl::Nonnull<const char*> format,
                          const substitute_internal::Arg& a0,
                          const substitute_internal::Arg& a1,
                          const substitute_internal::Arg& a2,
@@ -451,13 +461,11 @@
         "this format string is missing its $0-$4, contains one of "
         "$5-$9, or contains an unescaped $ character (use $$ instead)");
 
-void SubstituteAndAppend(std::string* output, const char* format,
-                         const substitute_internal::Arg& a0,
-                         const substitute_internal::Arg& a1,
-                         const substitute_internal::Arg& a2,
-                         const substitute_internal::Arg& a3,
-                         const substitute_internal::Arg& a4,
-                         const substitute_internal::Arg& a5)
+void SubstituteAndAppend(
+    absl::Nonnull<std::string*> output, absl::Nonnull<const char*> format,
+    const substitute_internal::Arg& a0, const substitute_internal::Arg& a1,
+    const substitute_internal::Arg& a2, const substitute_internal::Arg& a3,
+    const substitute_internal::Arg& a4, const substitute_internal::Arg& a5)
     ABSL_BAD_CALL_IF(
         substitute_internal::PlaceholderBitmask(format) != 63,
         "There were 6 substitution arguments given, but "
@@ -465,10 +473,11 @@
         "$6-$9, or contains an unescaped $ character (use $$ instead)");
 
 void SubstituteAndAppend(
-    std::string* output, const char* format, const substitute_internal::Arg& a0,
-    const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
-    const substitute_internal::Arg& a3, const substitute_internal::Arg& a4,
-    const substitute_internal::Arg& a5, const substitute_internal::Arg& a6)
+    absl::Nonnull<std::string*> output, absl::Nonnull<const char*> format,
+    const substitute_internal::Arg& a0, const substitute_internal::Arg& a1,
+    const substitute_internal::Arg& a2, const substitute_internal::Arg& a3,
+    const substitute_internal::Arg& a4, const substitute_internal::Arg& a5,
+    const substitute_internal::Arg& a6)
     ABSL_BAD_CALL_IF(
         substitute_internal::PlaceholderBitmask(format) != 127,
         "There were 7 substitution arguments given, but "
@@ -476,11 +485,11 @@
         "$7-$9, or contains an unescaped $ character (use $$ instead)");
 
 void SubstituteAndAppend(
-    std::string* output, const char* format, const substitute_internal::Arg& a0,
-    const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
-    const substitute_internal::Arg& a3, const substitute_internal::Arg& a4,
-    const substitute_internal::Arg& a5, const substitute_internal::Arg& a6,
-    const substitute_internal::Arg& a7)
+    absl::Nonnull<std::string*> output, absl::Nonnull<const char*> format,
+    const substitute_internal::Arg& a0, const substitute_internal::Arg& a1,
+    const substitute_internal::Arg& a2, const substitute_internal::Arg& a3,
+    const substitute_internal::Arg& a4, const substitute_internal::Arg& a5,
+    const substitute_internal::Arg& a6, const substitute_internal::Arg& a7)
     ABSL_BAD_CALL_IF(
         substitute_internal::PlaceholderBitmask(format) != 255,
         "There were 8 substitution arguments given, but "
@@ -488,11 +497,12 @@
         "$8-$9, or contains an unescaped $ character (use $$ instead)");
 
 void SubstituteAndAppend(
-    std::string* output, const char* format, const substitute_internal::Arg& a0,
-    const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
-    const substitute_internal::Arg& a3, const substitute_internal::Arg& a4,
-    const substitute_internal::Arg& a5, const substitute_internal::Arg& a6,
-    const substitute_internal::Arg& a7, const substitute_internal::Arg& a8)
+    absl::Nonnull<std::string*> output, absl::Nonnull<const char*> format,
+    const substitute_internal::Arg& a0, const substitute_internal::Arg& a1,
+    const substitute_internal::Arg& a2, const substitute_internal::Arg& a3,
+    const substitute_internal::Arg& a4, const substitute_internal::Arg& a5,
+    const substitute_internal::Arg& a6, const substitute_internal::Arg& a7,
+    const substitute_internal::Arg& a8)
     ABSL_BAD_CALL_IF(
         substitute_internal::PlaceholderBitmask(format) != 511,
         "There were 9 substitution arguments given, but "
@@ -500,12 +510,12 @@
         "contains an unescaped $ character (use $$ instead)");
 
 void SubstituteAndAppend(
-    std::string* output, const char* format, const substitute_internal::Arg& a0,
-    const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
-    const substitute_internal::Arg& a3, const substitute_internal::Arg& a4,
-    const substitute_internal::Arg& a5, const substitute_internal::Arg& a6,
-    const substitute_internal::Arg& a7, const substitute_internal::Arg& a8,
-    const substitute_internal::Arg& a9)
+    absl::Nonnull<std::string*> output, absl::Nonnull<const char*> format,
+    const substitute_internal::Arg& a0, const substitute_internal::Arg& a1,
+    const substitute_internal::Arg& a2, const substitute_internal::Arg& a3,
+    const substitute_internal::Arg& a4, const substitute_internal::Arg& a5,
+    const substitute_internal::Arg& a6, const substitute_internal::Arg& a7,
+    const substitute_internal::Arg& a8, const substitute_internal::Arg& a9)
     ABSL_BAD_CALL_IF(
         substitute_internal::PlaceholderBitmask(format) != 1023,
         "There were 10 substitution arguments given, but this "
@@ -633,20 +643,22 @@
 #if defined(ABSL_BAD_CALL_IF)
 // This body of functions catches cases where the number of placeholders
 // doesn't match the number of data arguments.
-std::string Substitute(const char* format)
+std::string Substitute(absl::Nonnull<const char*> format)
     ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 0,
                      "There were no substitution arguments "
                      "but this format string either has a $[0-9] in it or "
                      "contains an unescaped $ character (use $$ instead)");
 
-std::string Substitute(const char* format, const substitute_internal::Arg& a0)
+std::string Substitute(absl::Nonnull<const char*> format,
+                       const substitute_internal::Arg& a0)
     ABSL_BAD_CALL_IF(
         substitute_internal::PlaceholderBitmask(format) != 1,
         "There was 1 substitution argument given, but "
         "this format string is missing its $0, contains one of $1-$9, "
         "or contains an unescaped $ character (use $$ instead)");
 
-std::string Substitute(const char* format, const substitute_internal::Arg& a0,
+std::string Substitute(absl::Nonnull<const char*> format,
+                       const substitute_internal::Arg& a0,
                        const substitute_internal::Arg& a1)
     ABSL_BAD_CALL_IF(
         substitute_internal::PlaceholderBitmask(format) != 3,
@@ -654,7 +666,8 @@
         "this format string is missing its $0/$1, contains one of "
         "$2-$9, or contains an unescaped $ character (use $$ instead)");
 
-std::string Substitute(const char* format, const substitute_internal::Arg& a0,
+std::string Substitute(absl::Nonnull<const char*> format,
+                       const substitute_internal::Arg& a0,
                        const substitute_internal::Arg& a1,
                        const substitute_internal::Arg& a2)
     ABSL_BAD_CALL_IF(
@@ -663,7 +676,8 @@
         "this format string is missing its $0/$1/$2, contains one of "
         "$3-$9, or contains an unescaped $ character (use $$ instead)");
 
-std::string Substitute(const char* format, const substitute_internal::Arg& a0,
+std::string Substitute(absl::Nonnull<const char*> format,
+                       const substitute_internal::Arg& a0,
                        const substitute_internal::Arg& a1,
                        const substitute_internal::Arg& a2,
                        const substitute_internal::Arg& a3)
@@ -673,7 +687,8 @@
         "this format string is missing its $0-$3, contains one of "
         "$4-$9, or contains an unescaped $ character (use $$ instead)");
 
-std::string Substitute(const char* format, const substitute_internal::Arg& a0,
+std::string Substitute(absl::Nonnull<const char*> format,
+                       const substitute_internal::Arg& a0,
                        const substitute_internal::Arg& a1,
                        const substitute_internal::Arg& a2,
                        const substitute_internal::Arg& a3,
@@ -684,7 +699,8 @@
         "this format string is missing its $0-$4, contains one of "
         "$5-$9, or contains an unescaped $ character (use $$ instead)");
 
-std::string Substitute(const char* format, const substitute_internal::Arg& a0,
+std::string Substitute(absl::Nonnull<const char*> format,
+                       const substitute_internal::Arg& a0,
                        const substitute_internal::Arg& a1,
                        const substitute_internal::Arg& a2,
                        const substitute_internal::Arg& a3,
@@ -696,27 +712,23 @@
         "this format string is missing its $0-$5, contains one of "
         "$6-$9, or contains an unescaped $ character (use $$ instead)");
 
-std::string Substitute(const char* format, const substitute_internal::Arg& a0,
-                       const substitute_internal::Arg& a1,
-                       const substitute_internal::Arg& a2,
-                       const substitute_internal::Arg& a3,
-                       const substitute_internal::Arg& a4,
-                       const substitute_internal::Arg& a5,
-                       const substitute_internal::Arg& a6)
+std::string Substitute(
+    absl::Nonnull<const char*> format, const substitute_internal::Arg& a0,
+    const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
+    const substitute_internal::Arg& a3, const substitute_internal::Arg& a4,
+    const substitute_internal::Arg& a5, const substitute_internal::Arg& a6)
     ABSL_BAD_CALL_IF(
         substitute_internal::PlaceholderBitmask(format) != 127,
         "There were 7 substitution arguments given, but "
         "this format string is missing its $0-$6, contains one of "
         "$7-$9, or contains an unescaped $ character (use $$ instead)");
 
-std::string Substitute(const char* format, const substitute_internal::Arg& a0,
-                       const substitute_internal::Arg& a1,
-                       const substitute_internal::Arg& a2,
-                       const substitute_internal::Arg& a3,
-                       const substitute_internal::Arg& a4,
-                       const substitute_internal::Arg& a5,
-                       const substitute_internal::Arg& a6,
-                       const substitute_internal::Arg& a7)
+std::string Substitute(
+    absl::Nonnull<const char*> format, const substitute_internal::Arg& a0,
+    const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
+    const substitute_internal::Arg& a3, const substitute_internal::Arg& a4,
+    const substitute_internal::Arg& a5, const substitute_internal::Arg& a6,
+    const substitute_internal::Arg& a7)
     ABSL_BAD_CALL_IF(
         substitute_internal::PlaceholderBitmask(format) != 255,
         "There were 8 substitution arguments given, but "
@@ -724,7 +736,7 @@
         "$8-$9, or contains an unescaped $ character (use $$ instead)");
 
 std::string Substitute(
-    const char* format, const substitute_internal::Arg& a0,
+    absl::Nonnull<const char*> format, const substitute_internal::Arg& a0,
     const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
     const substitute_internal::Arg& a3, const substitute_internal::Arg& a4,
     const substitute_internal::Arg& a5, const substitute_internal::Arg& a6,
@@ -736,7 +748,7 @@
         "contains an unescaped $ character (use $$ instead)");
 
 std::string Substitute(
-    const char* format, const substitute_internal::Arg& a0,
+    absl::Nonnull<const char*> format, const substitute_internal::Arg& a0,
     const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
     const substitute_internal::Arg& a3, const substitute_internal::Arg& a4,
     const substitute_internal::Arg& a5, const substitute_internal::Arg& a6,
diff --git a/absl/types/BUILD.bazel b/absl/types/BUILD.bazel
index 3d5cf01..ce8f605 100644
--- a/absl/types/BUILD.bazel
+++ b/absl/types/BUILD.bazel
@@ -118,6 +118,7 @@
     deps = [
         "//absl/algorithm",
         "//absl/base:core_headers",
+        "//absl/base:nullability",
         "//absl/base:throw_delegate",
         "//absl/meta:type_traits",
     ],
@@ -154,6 +155,7 @@
         "//absl/base:base_internal",
         "//absl/base:config",
         "//absl/base:core_headers",
+        "//absl/base:nullability",
         "//absl/memory",
         "//absl/meta:type_traits",
         "//absl/utility",
@@ -293,6 +295,7 @@
     copts = ABSL_DEFAULT_COPTS,
     linkopts = ABSL_DEFAULT_LINKOPTS,
     deps = [
+        "//absl/base:config",
         "//absl/base:core_headers",
         "//absl/meta:type_traits",
     ],
diff --git a/absl/types/CMakeLists.txt b/absl/types/CMakeLists.txt
index 1adf3c7..92b4ae4 100644
--- a/absl/types/CMakeLists.txt
+++ b/absl/types/CMakeLists.txt
@@ -115,6 +115,7 @@
   DEPS
     absl::algorithm
     absl::core_headers
+    absl::nullability
     absl::throw_delegate
     absl::type_traits
   PUBLIC
@@ -175,6 +176,7 @@
     absl::config
     absl::core_headers
     absl::memory
+    absl::nullability
     absl::type_traits
     absl::utility
   PUBLIC
@@ -284,6 +286,7 @@
   COPTS
     ${ABSL_DEFAULT_COPTS}
   DEPS
+    absl::config
     absl::core_headers
     absl::type_traits
   PUBLIC
diff --git a/absl/types/compare.h b/absl/types/compare.h
index 2b89b69..3cf4a91 100644
--- a/absl/types/compare.h
+++ b/absl/types/compare.h
@@ -16,20 +16,31 @@
 // 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.
+// This header file defines the `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
+// (`std::partial_ordering`, 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 "absl/base/config.h"
+
+#ifdef ABSL_USES_STD_ORDERING
+
+#include <compare>  // IWYU pragma: export
+#include <type_traits>
+
+#include "absl/meta/type_traits.h"
+
+#else
+
 #include <cstddef>
 #include <cstdint>
 #include <cstdlib>
@@ -39,8 +50,19 @@
 #include "absl/base/macros.h"
 #include "absl/meta/type_traits.h"
 
+#endif
+
 namespace absl {
 ABSL_NAMESPACE_BEGIN
+
+#ifdef ABSL_USES_STD_ORDERING
+
+using std::partial_ordering;
+using std::strong_ordering;
+using std::weak_ordering;
+
+#else
+
 namespace compare_internal {
 
 using value_type = int8_t;
@@ -122,20 +144,6 @@
 // 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_COMPARE_INLINE_BASECLASS_DECL(equivalent);
-  ABSL_COMPARE_INLINE_BASECLASS_DECL(nonequivalent);
-};
-
-template <typename T>
-struct strong_equality_base {
-  ABSL_COMPARE_INLINE_BASECLASS_DECL(equal);
-  ABSL_COMPARE_INLINE_BASECLASS_DECL(nonequal);
-  ABSL_COMPARE_INLINE_BASECLASS_DECL(equivalent);
-  ABSL_COMPARE_INLINE_BASECLASS_DECL(nonequivalent);
-};
-
-template <typename T>
 struct partial_ordering_base {
   ABSL_COMPARE_INLINE_BASECLASS_DECL(less);
   ABSL_COMPARE_INLINE_BASECLASS_DECL(equivalent);
@@ -160,104 +168,6 @@
 
 }  // 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:
-  ABSL_COMPARE_INLINE_SUBCLASS_DECL(weak_equality, equivalent);
-  ABSL_COMPARE_INLINE_SUBCLASS_DECL(weak_equality, nonequivalent);
-
-  // 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_;
-  }
-  friend constexpr bool operator==(weak_equality v1,
-                                   weak_equality v2) noexcept {
-    return v1.value_ == v2.value_;
-  }
-  friend constexpr bool operator!=(weak_equality v1,
-                                   weak_equality v2) noexcept {
-    return v1.value_ != v2.value_;
-  }
-
- private:
-  compare_internal::value_type value_;
-};
-ABSL_COMPARE_INLINE_INIT(weak_equality, equivalent,
-                         compare_internal::eq::equivalent);
-ABSL_COMPARE_INLINE_INIT(weak_equality, nonequivalent,
-                         compare_internal::eq::nonequivalent);
-
-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:
-  ABSL_COMPARE_INLINE_SUBCLASS_DECL(strong_equality, equal);
-  ABSL_COMPARE_INLINE_SUBCLASS_DECL(strong_equality, nonequal);
-  ABSL_COMPARE_INLINE_SUBCLASS_DECL(strong_equality, equivalent);
-  ABSL_COMPARE_INLINE_SUBCLASS_DECL(strong_equality, nonequivalent);
-
-  // 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_;
-  }
-  friend constexpr bool operator==(strong_equality v1,
-                                   strong_equality v2) noexcept {
-    return v1.value_ == v2.value_;
-  }
-  friend constexpr bool operator!=(strong_equality v1,
-                                   strong_equality v2) noexcept {
-    return v1.value_ != v2.value_;
-  }
-
- private:
-  compare_internal::value_type value_;
-};
-ABSL_COMPARE_INLINE_INIT(strong_equality, equal, compare_internal::eq::equal);
-ABSL_COMPARE_INLINE_INIT(strong_equality, nonequal,
-                         compare_internal::eq::nonequal);
-ABSL_COMPARE_INLINE_INIT(strong_equality, equivalent,
-                         compare_internal::eq::equivalent);
-ABSL_COMPARE_INLINE_INIT(strong_equality, nonequivalent,
-                         compare_internal::eq::nonequivalent);
-
 class partial_ordering
     : public compare_internal::partial_ordering_base<partial_ordering> {
   explicit constexpr partial_ordering(compare_internal::eq v) noexcept
@@ -279,11 +189,6 @@
   ABSL_COMPARE_INLINE_SUBCLASS_DECL(partial_ordering, greater);
   ABSL_COMPARE_INLINE_SUBCLASS_DECL(partial_ordering, unordered);
 
-  // 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 {
@@ -367,10 +272,6 @@
   ABSL_COMPARE_INLINE_SUBCLASS_DECL(weak_ordering, greater);
 
   // 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
@@ -458,13 +359,6 @@
   ABSL_COMPARE_INLINE_SUBCLASS_DECL(strong_ordering, greater);
 
   // 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
@@ -547,6 +441,8 @@
 #undef ABSL_COMPARE_INLINE_SUBCLASS_DECL
 #undef ABSL_COMPARE_INLINE_INIT
 
+#endif  // ABSL_USES_STD_ORDERING
+
 namespace compare_internal {
 // We also provide these comparator adapter functions for internal absl use.
 
diff --git a/absl/types/compare_test.cc b/absl/types/compare_test.cc
index 8095baf..455cdbb 100644
--- a/absl/types/compare_test.cc
+++ b/absl/types/compare_test.cc
@@ -26,45 +26,6 @@
 // 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));
-  const weak_equality values[] = {weak_equality::equivalent,
-                                  weak_equality::nonequivalent};
-  for (const auto& lhs : values) {
-    for (const auto& rhs : values) {
-      const bool are_equal = &lhs == &rhs;
-      EXPECT_EQ(lhs == rhs, are_equal);
-      EXPECT_EQ(lhs != rhs, !are_equal);
-    }
-  }
-}
-
-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));
-  const strong_equality values[] = {strong_equality::equal,
-                                    strong_equality::nonequal};
-  for (const auto& lhs : values) {
-    for (const auto& rhs : values) {
-      const bool are_equal = &lhs == &rhs;
-      EXPECT_EQ(lhs == rhs, are_equal);
-      EXPECT_EQ(lhs != rhs, !are_equal);
-    }
-  }
-  EXPECT_TRUE(Identity(strong_equality::equivalent == strong_equality::equal));
-  EXPECT_TRUE(
-      Identity(strong_equality::nonequivalent == strong_equality::nonequal));
-}
-
 TEST(Compare, PartialOrdering) {
   EXPECT_TRUE(Identity(partial_ordering::less < 0));
   EXPECT_TRUE(Identity(0 > partial_ordering::less));
@@ -147,30 +108,6 @@
 
 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));
@@ -186,24 +123,6 @@
       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));
@@ -360,14 +279,6 @@
 
 #ifdef __cpp_inline_variables
 TEST(Compare, StaticAsserts) {
-  static_assert(weak_equality::equivalent == 0, "");
-  static_assert(weak_equality::nonequivalent != 0, "");
-
-  static_assert(strong_equality::equal == 0, "");
-  static_assert(strong_equality::nonequal != 0, "");
-  static_assert(strong_equality::equivalent == 0, "");
-  static_assert(strong_equality::nonequivalent != 0, "");
-
   static_assert(partial_ordering::less < 0, "");
   static_assert(partial_ordering::equivalent == 0, "");
   static_assert(partial_ordering::greater > 0, "");
diff --git a/absl/types/optional.h b/absl/types/optional.h
index 0a8080d..395fe62 100644
--- a/absl/types/optional.h
+++ b/absl/types/optional.h
@@ -61,6 +61,7 @@
 #include <utility>
 
 #include "absl/base/attributes.h"
+#include "absl/base/nullability.h"
 #include "absl/base/internal/inline_variable.h"
 #include "absl/meta/type_traits.h"
 #include "absl/types/bad_optional_access.h"
@@ -415,11 +416,11 @@
   // `optional` is empty, behavior is undefined.
   //
   // If you need myOpt->foo in constexpr, use (*myOpt).foo instead.
-  const T* operator->() const ABSL_ATTRIBUTE_LIFETIME_BOUND {
+  absl::Nonnull<const T*> operator->() const ABSL_ATTRIBUTE_LIFETIME_BOUND {
     ABSL_HARDENING_ASSERT(this->engaged_);
     return std::addressof(this->data_);
   }
-  T* operator->() ABSL_ATTRIBUTE_LIFETIME_BOUND {
+  absl::Nonnull<T*> operator->() ABSL_ATTRIBUTE_LIFETIME_BOUND {
     ABSL_HARDENING_ASSERT(this->engaged_);
     return std::addressof(this->data_);
   }
diff --git a/absl/types/span.h b/absl/types/span.h
index 70ed8eb..7d1d016 100644
--- a/absl/types/span.h
+++ b/absl/types/span.h
@@ -63,6 +63,7 @@
 #include "absl/base/attributes.h"
 #include "absl/base/internal/throw_delegate.h"
 #include "absl/base/macros.h"
+#include "absl/base/nullability.h"
 #include "absl/base/optimization.h"
 #include "absl/base/port.h"    // TODO(strel): remove this include
 #include "absl/meta/type_traits.h"
@@ -172,8 +173,8 @@
  public:
   using element_type = T;
   using value_type = absl::remove_cv_t<T>;
-  using pointer = T*;
-  using const_pointer = const T*;
+  using pointer = absl::Nullable<T*>;
+  using const_pointer = absl::Nullable<const T*>;
   using reference = T&;
   using const_reference = const T&;
   using iterator = pointer;
@@ -679,12 +680,12 @@
 //   }
 //
 template <int&... ExplicitArgumentBarrier, typename T>
-constexpr Span<T> MakeSpan(T* ptr, size_t size) noexcept {
+constexpr Span<T> MakeSpan(absl::Nullable<T*> ptr, size_t size) noexcept {
   return Span<T>(ptr, size);
 }
 
 template <int&... ExplicitArgumentBarrier, typename T>
-Span<T> MakeSpan(T* begin, T* end) noexcept {
+Span<T> MakeSpan(absl::Nullable<T*> begin, absl::Nullable<T*> end) noexcept {
   return ABSL_HARDENING_ASSERT(begin <= end),
          Span<T>(begin, static_cast<size_t>(end - begin));
 }
@@ -725,12 +726,14 @@
 //   ProcessInts(absl::MakeConstSpan(std::vector<int>{ 0, 0, 0 }));
 //
 template <int&... ExplicitArgumentBarrier, typename T>
-constexpr Span<const T> MakeConstSpan(T* ptr, size_t size) noexcept {
+constexpr Span<const T> MakeConstSpan(absl::Nullable<T*> ptr,
+                                      size_t size) noexcept {
   return Span<const T>(ptr, size);
 }
 
 template <int&... ExplicitArgumentBarrier, typename T>
-Span<const T> MakeConstSpan(T* begin, T* end) noexcept {
+Span<const T> MakeConstSpan(absl::Nullable<T*> begin,
+                            absl::Nullable<T*> end) noexcept {
   return ABSL_HARDENING_ASSERT(begin <= end), Span<const T>(begin, end - begin);
 }
 
diff --git a/absl/utility/utility.h b/absl/utility/utility.h
index bf92322..fc0d1f6 100644
--- a/absl/utility/utility.h
+++ b/absl/utility/utility.h
@@ -12,17 +12,12 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 //
-// This header file contains C++11 versions of standard <utility> header
-// abstractions available within C++14 and C++17, and are designed to be drop-in
+// This header file contains C++14 versions of standard <utility> header
+// abstractions available within C++17, and are designed to be drop-in
 // replacement for code compliant with C++14 and C++17.
 //
 // The following abstractions are defined:
 //
-//   * integer_sequence<T, Ints...>  == std::integer_sequence<T, Ints...>
-//   * index_sequence<Ints...>       == std::index_sequence<Ints...>
-//   * make_integer_sequence<T, N>   == std::make_integer_sequence<T, N>
-//   * make_index_sequence<N>        == std::make_index_sequence<N>
-//   * index_sequence_for<Ts...>     == std::index_sequence_for<Ts...>
 //   * apply<Functor, Tuple>         == std::apply<Functor, Tuple>
 //   * exchange<T>                   == std::exchange<T>
 //   * make_from_tuple<T>            == std::make_from_tuple<T>
@@ -33,7 +28,6 @@
 //
 // References:
 //
-//  https://en.cppreference.com/w/cpp/utility/integer_sequence
 //  https://en.cppreference.com/w/cpp/utility/apply
 //  http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3658.html
 
@@ -53,68 +47,18 @@
 namespace absl {
 ABSL_NAMESPACE_BEGIN
 
-// integer_sequence
-//
-// Class template representing a compile-time integer sequence. An instantiation
-// of `integer_sequence<T, Ints...>` has a sequence of integers encoded in its
-// type through its template arguments (which is a common need when
-// working with C++11 variadic templates). `absl::integer_sequence` is designed
-// to be a drop-in replacement for C++14's `std::integer_sequence`.
-//
-// Example:
-//
-//   template< class T, T... Ints >
-//   void user_function(integer_sequence<T, Ints...>);
-//
-//   int main()
-//   {
-//     // user_function's `T` will be deduced to `int` and `Ints...`
-//     // will be deduced to `0, 1, 2, 3, 4`.
-//     user_function(make_integer_sequence<int, 5>());
-//   }
-template <typename T, T... Ints>
-struct integer_sequence {
-  using value_type = T;
-  static constexpr size_t size() noexcept { return sizeof...(Ints); }
-};
-
-// index_sequence
-//
-// A helper template for an `integer_sequence` of `size_t`,
-// `absl::index_sequence` is designed to be a drop-in replacement for C++14's
-// `std::index_sequence`.
-template <size_t... Ints>
-using index_sequence = integer_sequence<size_t, Ints...>;
+// Historical note: Abseil once provided implementations of these
+// abstractions for platforms that had not yet provided them. Those
+// platforms are no longer supported. New code should simply use the
+// the ones from std directly.
+using std::index_sequence;
+using std::index_sequence_for;
+using std::integer_sequence;
+using std::make_index_sequence;
+using std::make_integer_sequence;
 
 namespace utility_internal {
 
-template <typename Seq, size_t SeqSize, size_t Rem>
-struct Extend;
-
-// Note that SeqSize == sizeof...(Ints). It's passed explicitly for efficiency.
-template <typename T, T... Ints, size_t SeqSize>
-struct Extend<integer_sequence<T, Ints...>, SeqSize, 0> {
-  using type = integer_sequence<T, Ints..., (Ints + SeqSize)...>;
-};
-
-template <typename T, T... Ints, size_t SeqSize>
-struct Extend<integer_sequence<T, Ints...>, SeqSize, 1> {
-  using type = integer_sequence<T, Ints..., (Ints + SeqSize)..., 2 * SeqSize>;
-};
-
-// Recursion helper for 'make_integer_sequence<T, N>'.
-// 'Gen<T, N>::type' is an alias for 'integer_sequence<T, 0, 1, ... N-1>'.
-template <typename T, size_t N>
-struct Gen {
-  using type =
-      typename Extend<typename Gen<T, N / 2>::type, N / 2, N % 2>::type;
-};
-
-template <typename T>
-struct Gen<T, 0> {
-  using type = integer_sequence<T>;
-};
-
 template <typename T>
 struct InPlaceTypeTag {
   explicit InPlaceTypeTag() = delete;
@@ -131,32 +75,6 @@
 
 }  // namespace utility_internal
 
-// Compile-time sequences of integers
-
-// make_integer_sequence
-//
-// This template alias is equivalent to
-// `integer_sequence<int, 0, 1, ..., N-1>`, and is designed to be a drop-in
-// replacement for C++14's `std::make_integer_sequence`.
-template <typename T, T N>
-using make_integer_sequence = typename utility_internal::Gen<T, N>::type;
-
-// make_index_sequence
-//
-// This template alias is equivalent to `index_sequence<0, 1, ..., N-1>`,
-// and is designed to be a drop-in replacement for C++14's
-// `std::make_index_sequence`.
-template <size_t N>
-using make_index_sequence = make_integer_sequence<size_t, N>;
-
-// index_sequence_for
-//
-// Converts a typename pack into an index sequence of the same length, and
-// is designed to be a drop-in replacement for C++14's
-// `std::index_sequence_for()`
-template <typename... Ts>
-using index_sequence_for = make_index_sequence<sizeof...(Ts)>;
-
 // Tag types
 
 #ifdef ABSL_USES_STD_OPTIONAL
diff --git a/absl/utility/utility_test.cc b/absl/utility/utility_test.cc
index 2f0509a..1af6813 100644
--- a/absl/utility/utility_test.cc
+++ b/absl/utility/utility_test.cc
@@ -30,139 +30,10 @@
 
 namespace {
 
-#ifdef _MSC_VER
-// Warnings for unused variables in this test are false positives.  On other
-// platforms, they are suppressed by ABSL_ATTRIBUTE_UNUSED, but that doesn't
-// work on MSVC.
-// Both the unused variables and the name length warnings are due to calls
-// to absl::make_index_sequence with very large values, creating very long type
-// names. The resulting warnings are so long they make build output unreadable.
-#pragma warning(push)
-#pragma warning(disable : 4503)  // decorated name length exceeded
-#pragma warning(disable : 4101)  // unreferenced local variable
-#endif                           // _MSC_VER
-
 using ::testing::ElementsAre;
 using ::testing::Pointee;
 using ::testing::StaticAssertTypeEq;
 
-TEST(IntegerSequenceTest, ValueType) {
-  StaticAssertTypeEq<int, absl::integer_sequence<int>::value_type>();
-  StaticAssertTypeEq<char, absl::integer_sequence<char>::value_type>();
-}
-
-TEST(IntegerSequenceTest, Size) {
-  EXPECT_EQ(0, (absl::integer_sequence<int>::size()));
-  EXPECT_EQ(1, (absl::integer_sequence<int, 0>::size()));
-  EXPECT_EQ(1, (absl::integer_sequence<int, 1>::size()));
-  EXPECT_EQ(2, (absl::integer_sequence<int, 1, 2>::size()));
-  EXPECT_EQ(3, (absl::integer_sequence<int, 0, 1, 2>::size()));
-  EXPECT_EQ(3, (absl::integer_sequence<int, -123, 123, 456>::size()));
-  constexpr size_t sz = absl::integer_sequence<int, 0, 1>::size();
-  EXPECT_EQ(2, sz);
-}
-
-TEST(IntegerSequenceTest, MakeIndexSequence) {
-  StaticAssertTypeEq<absl::index_sequence<>, absl::make_index_sequence<0>>();
-  StaticAssertTypeEq<absl::index_sequence<0>, absl::make_index_sequence<1>>();
-  StaticAssertTypeEq<absl::index_sequence<0, 1>,
-                     absl::make_index_sequence<2>>();
-  StaticAssertTypeEq<absl::index_sequence<0, 1, 2>,
-                     absl::make_index_sequence<3>>();
-}
-
-TEST(IntegerSequenceTest, MakeIntegerSequence) {
-  StaticAssertTypeEq<absl::integer_sequence<int>,
-                     absl::make_integer_sequence<int, 0>>();
-  StaticAssertTypeEq<absl::integer_sequence<int, 0>,
-                     absl::make_integer_sequence<int, 1>>();
-  StaticAssertTypeEq<absl::integer_sequence<int, 0, 1>,
-                     absl::make_integer_sequence<int, 2>>();
-  StaticAssertTypeEq<absl::integer_sequence<int, 0, 1, 2>,
-                     absl::make_integer_sequence<int, 3>>();
-}
-
-template <typename... Ts>
-class Counter {};
-
-template <size_t... Is>
-void CountAll(absl::index_sequence<Is...>) {
-  // We only need an alias here, but instantiate a variable to silence warnings
-  // for unused typedefs in some compilers.
-  ABSL_ATTRIBUTE_UNUSED Counter<absl::make_index_sequence<Is>...> seq;
-}
-
-// This test verifies that absl::make_index_sequence can handle large arguments
-// without blowing up template instantiation stack, going OOM or taking forever
-// to compile (there is hard 15 minutes limit imposed by forge).
-TEST(IntegerSequenceTest, MakeIndexSequencePerformance) {
-  // O(log N) template instantiations.
-  // We only need an alias here, but instantiate a variable to silence warnings
-  // for unused typedefs in some compilers.
-  ABSL_ATTRIBUTE_UNUSED absl::make_index_sequence<(1 << 16) - 1> seq;
-  // O(N) template instantiations.
-  CountAll(absl::make_index_sequence<(1 << 8) - 1>());
-}
-
-template <typename F, typename Tup, size_t... Is>
-auto ApplyFromTupleImpl(F f, const Tup& tup, absl::index_sequence<Is...>)
-    -> decltype(f(std::get<Is>(tup)...)) {
-  return f(std::get<Is>(tup)...);
-}
-
-template <typename Tup>
-using TupIdxSeq = absl::make_index_sequence<std::tuple_size<Tup>::value>;
-
-template <typename F, typename Tup>
-auto ApplyFromTuple(F f, const Tup& tup)
-    -> decltype(ApplyFromTupleImpl(f, tup, TupIdxSeq<Tup>{})) {
-  return ApplyFromTupleImpl(f, tup, TupIdxSeq<Tup>{});
-}
-
-template <typename T>
-std::string Fmt(const T& x) {
-  std::ostringstream os;
-  os << x;
-  return os.str();
-}
-
-struct PoorStrCat {
-  template <typename... Args>
-  std::string operator()(const Args&... args) const {
-    std::string r;
-    for (const auto& e : {Fmt(args)...}) r += e;
-    return r;
-  }
-};
-
-template <typename Tup, size_t... Is>
-std::vector<std::string> TupStringVecImpl(const Tup& tup,
-                                          absl::index_sequence<Is...>) {
-  return {Fmt(std::get<Is>(tup))...};
-}
-
-template <typename... Ts>
-std::vector<std::string> TupStringVec(const std::tuple<Ts...>& tup) {
-  return TupStringVecImpl(tup, absl::index_sequence_for<Ts...>());
-}
-
-TEST(MakeIndexSequenceTest, ApplyFromTupleExample) {
-  PoorStrCat f{};
-  EXPECT_EQ("12abc3.14", f(12, "abc", 3.14));
-  EXPECT_EQ("12abc3.14", ApplyFromTuple(f, std::make_tuple(12, "abc", 3.14)));
-}
-
-TEST(IndexSequenceForTest, Basic) {
-  StaticAssertTypeEq<absl::index_sequence<>, absl::index_sequence_for<>>();
-  StaticAssertTypeEq<absl::index_sequence<0>, absl::index_sequence_for<int>>();
-  StaticAssertTypeEq<absl::index_sequence<0, 1, 2, 3>,
-                     absl::index_sequence_for<int, void, char, int>>();
-}
-
-TEST(IndexSequenceForTest, Example) {
-  EXPECT_THAT(TupStringVec(std::make_tuple(12, "abc", 3.14)),
-              ElementsAre("12", "abc", "3.14"));
-}
 
 int Function(int a, int b) { return a - b; }
 
diff --git a/ci/absl_alternate_options.h b/ci/absl_alternate_options.h
index 82d2ecf..a563859 100644
--- a/ci/absl_alternate_options.h
+++ b/ci/absl_alternate_options.h
@@ -22,6 +22,7 @@
 #define ABSL_OPTION_USE_STD_OPTIONAL 0
 #define ABSL_OPTION_USE_STD_STRING_VIEW 0
 #define ABSL_OPTION_USE_STD_VARIANT 0
+#define ABSL_OPTION_USE_STD_ORDERING 0
 #define ABSL_OPTION_USE_INLINE_NAMESPACE 1
 #define ABSL_OPTION_INLINE_NAMESPACE_NAME ns
 #define ABSL_OPTION_HARDENED 1