Upgrade ABSL to LTS version 20240116.1 am: 3fd0a1be59

Original change: https://android-review.googlesource.com/c/platform/external/abseil-cpp/+/2982152

Change-Id: Ib52d59c278ce8eaa4b91108ad345825e7dc5c242
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/.gitignore b/.gitignore
index d54fa5a..3a729f4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,5 @@
+# Bzlmod lockfile
+MODULE.bazel.lock
 # Ignore all bazel-* symlinks.
 /bazel-*
 # Ignore Bazel verbose explanations
diff --git a/CMake/AbseilDll.cmake b/CMake/AbseilDll.cmake
index f0d984a..47f3bee 100644
--- a/CMake/AbseilDll.cmake
+++ b/CMake/AbseilDll.cmake
@@ -28,7 +28,6 @@
   "base/internal/low_level_scheduling.h"
   "base/internal/nullability_impl.h"
   "base/internal/per_thread_tls.h"
-  "base/internal/prefetch.h"
   "base/prefetch.h"
   "base/internal/pretty_function.h"
   "base/internal/raw_logging.cc"
@@ -44,7 +43,6 @@
   "base/internal/spinlock_wait.h"
   "base/internal/sysinfo.cc"
   "base/internal/sysinfo.h"
-  "base/internal/thread_annotations.h"
   "base/internal/thread_identity.cc"
   "base/internal/thread_identity.h"
   "base/internal/throw_delegate.cc"
@@ -57,6 +55,7 @@
   "base/log_severity.cc"
   "base/log_severity.h"
   "base/macros.h"
+  "base/no_destructor.h"
   "base/nullability.h"
   "base/optimization.h"
   "base/options.h"
@@ -77,7 +76,6 @@
   "container/internal/common_policy_traits.h"
   "container/internal/compressed_tuple.h"
   "container/internal/container_memory.h"
-  "container/internal/counting_allocator.h"
   "container/internal/hash_function_defaults.h"
   "container/internal/hash_policy_traits.h"
   "container/internal/hashtable_debug.h"
@@ -109,7 +107,7 @@
   "crc/internal/crc_x86_arm_combined.cc"
   "crc/internal/crc_memcpy_fallback.cc"
   "crc/internal/crc_memcpy.h"
-  "crc/internal/crc_memcpy_x86_64.cc"
+  "crc/internal/crc_memcpy_x86_arm_combined.cc"
   "crc/internal/crc_non_temporal_memcpy.cc"
   "crc/internal/crc_x86_arm_combined.cc"
   "crc/internal/non_temporal_arm_intrinsics.h"
@@ -141,6 +139,7 @@
   "functional/function_ref.h"
   "functional/internal/any_invocable.h"
   "functional/internal/function_ref.h"
+  "functional/overload.h"
   "hash/hash.h"
   "hash/internal/city.h"
   "hash/internal/city.cc"
@@ -151,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"
@@ -163,6 +163,8 @@
   "log/internal/conditions.cc"
   "log/internal/conditions.h"
   "log/internal/config.h"
+  "log/internal/fnmatch.h"
+  "log/internal/fnmatch.cc"
   "log/internal/globals.cc"
   "log/internal/globals.h"
   "log/internal/log_format.cc"
@@ -179,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"
@@ -190,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"
@@ -250,6 +255,7 @@
   "random/uniform_real_distribution.h"
   "random/zipf_distribution.h"
   "status/internal/status_internal.h"
+  "status/internal/status_internal.cc"
   "status/internal/statusor_internal.h"
   "status/status.h"
   "status/status.cc"
@@ -261,6 +267,7 @@
   "strings/ascii.h"
   "strings/charconv.cc"
   "strings/charconv.h"
+  "strings/charset.h"
   "strings/cord.cc"
   "strings/cord.h"
   "strings/cord_analysis.cc"
@@ -287,9 +294,6 @@
   "strings/internal/cord_rep_consume.h"
   "strings/internal/cord_rep_consume.cc"
   "strings/internal/cord_rep_flat.h"
-  "strings/internal/cord_rep_ring.cc"
-  "strings/internal/cord_rep_ring.h"
-  "strings/internal/cord_rep_ring_reader.h"
   "strings/internal/cordz_functions.cc"
   "strings/internal/cordz_functions.h"
   "strings/internal/cordz_handle.cc"
@@ -308,6 +312,8 @@
   "strings/internal/stringify_sink.h"
   "strings/internal/stringify_sink.cc"
   "strings/internal/has_absl_stringify.h"
+  "strings/has_absl_stringify.h"
+  "strings/has_ostream_operator.h"
   "strings/match.cc"
   "strings/match.h"
   "strings/numbers.cc"
@@ -325,7 +331,6 @@
   "strings/strip.h"
   "strings/substitute.cc"
   "strings/substitute.h"
-  "strings/internal/char_map.h"
   "strings/internal/escaping.h"
   "strings/internal/escaping.cc"
   "strings/internal/memutil.cc"
@@ -421,11 +426,6 @@
   "types/bad_variant_access.cc"
   "types/bad_variant_access.h"
   "types/compare.h"
-  "types/internal/conformance_aliases.h"
-  "types/internal/conformance_archetype.h"
-  "types/internal/conformance_profile.h"
-  "types/internal/parentheses.h"
-  "types/internal/transform_args.h"
   "types/internal/variant.h"
   "types/optional.h"
   "types/internal/optional.h"
@@ -627,17 +627,32 @@
 check_cxx_source_compiles(
   [==[
 #ifdef _MSC_VER
-#  if _MSVC_LANG < 201700L
+#  if _MSVC_LANG < 201703L
 #    error "The compiler defaults or is configured for C++ < 17"
 #  endif
-#elif __cplusplus < 201700L
+#elif __cplusplus < 201703L
 #  error "The compiler defaults or is configured for C++ < 17"
 #endif
 int main() { return 0; }
 ]==]
   ABSL_INTERNAL_AT_LEAST_CXX17)
 
-if(ABSL_INTERNAL_AT_LEAST_CXX17)
+check_cxx_source_compiles(
+  [==[
+#ifdef _MSC_VER
+#  if _MSVC_LANG < 202002L
+#    error "The compiler defaults or is configured for C++ < 20"
+#  endif
+#elif __cplusplus < 202002L
+#  error "The compiler defaults or is configured for C++ < 20"
+#endif
+int main() { return 0; }
+]==]
+  ABSL_INTERNAL_AT_LEAST_CXX20)
+
+if(ABSL_INTERNAL_AT_LEAST_CXX20)
+  set(ABSL_INTERNAL_CXX_STD_FEATURE cxx_std_20)
+elseif(ABSL_INTERNAL_AT_LEAST_CXX17)
   set(ABSL_INTERNAL_CXX_STD_FEATURE cxx_std_17)
 else()
   set(ABSL_INTERNAL_CXX_STD_FEATURE cxx_std_14)
@@ -807,8 +822,8 @@
 
   if(ABSL_PROPAGATE_CXX_STD)
     # Abseil libraries require C++14 as the current minimum standard. When
-    # compiled with C++17 (either because it is the compiler's default or
-    # explicitly requested), then Abseil requires C++17.
+    # compiled with a higher minimum (either because it is the compiler's
+    # default or explicitly requested), then Abseil requires that standard.
     target_compile_features(${_dll} PUBLIC ${ABSL_INTERNAL_CXX_STD_FEATURE})
   endif()
 
diff --git a/CMake/AbseilHelpers.cmake b/CMake/AbseilHelpers.cmake
index 3bd33ce..c53b358 100644
--- a/CMake/AbseilHelpers.cmake
+++ b/CMake/AbseilHelpers.cmake
@@ -80,7 +80,7 @@
 #     absl::fantastic_lib
 # )
 #
-# TODO: Implement "ALWAYSLINK"
+# TODO(b/320467376): Implement "ALWAYSLINK".
 function(absl_cc_library)
   cmake_parse_arguments(ABSL_CC_LIB
     "DISABLE_INSTALL;PUBLIC;TESTONLY"
@@ -287,8 +287,8 @@
 
     if(ABSL_PROPAGATE_CXX_STD)
       # Abseil libraries require C++14 as the current minimum standard. When
-      # compiled with C++17 (either because it is the compiler's default or
-      # explicitly requested), then Abseil requires C++17.
+      # compiled with a higher standard (either because it is the compiler's
+      # default or explicitly requested), then Abseil requires that standard.
       target_compile_features(${_NAME} PUBLIC ${ABSL_INTERNAL_CXX_STD_FEATURE})
     endif()
 
@@ -298,7 +298,7 @@
     if(ABSL_ENABLE_INSTALL)
       set_target_properties(${_NAME} PROPERTIES
         OUTPUT_NAME "absl_${_NAME}"
-        SOVERSION "2308.0.0"
+        SOVERSION "2401.0.0"
       )
     endif()
   else()
diff --git a/CMakeLists.txt b/CMakeLists.txt
index eef6626..194f870 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -33,6 +33,11 @@
   cmake_policy(SET CMP0048 NEW)
 endif (POLICY CMP0048)
 
+# Honor the GTest_ROOT variable if specified
+if (POLICY CMP0074)
+  cmake_policy(SET CMP0074 NEW)
+endif (POLICY CMP0074)
+
 # option() honor variables
 if (POLICY CMP0077)
   cmake_policy(SET CMP0077 NEW)
@@ -53,7 +58,7 @@
   cmake_policy(SET CMP0141 NEW)
 endif (POLICY CMP0141)
 
-project(absl LANGUAGES CXX VERSION 20230802)
+project(absl LANGUAGES CXX VERSION 20240116)
 include(CTest)
 
 # Output directory is correct by default for most build setups. However, when
@@ -221,20 +226,41 @@
       PATTERN "testdata" EXCLUDE
     )
 
+  # Rewrite options.h to use the compiled ABI.
   file(READ "absl/base/options.h" ABSL_INTERNAL_OPTIONS_H_CONTENTS)
-  if (ABSL_INTERNAL_AT_LEAST_CXX17)
-    string(REGEX REPLACE
-      "#define ABSL_OPTION_USE_STD_([^ ]*) 2"
-      "#define ABSL_OPTION_USE_STD_\\1 1"
+
+  # Handle features that require at least C++20.
+  if (ABSL_INTERNAL_AT_LEAST_CXX20)
+    foreach(FEATURE "ORDERING")
+      string(REPLACE
+      "#define ABSL_OPTION_USE_STD_${FEATURE} 2"
+      "#define ABSL_OPTION_USE_STD_${FEATURE} 1"
       ABSL_INTERNAL_OPTIONS_H_PINNED
       "${ABSL_INTERNAL_OPTIONS_H_CONTENTS}")
-  else()
-    string(REGEX REPLACE
-      "#define ABSL_OPTION_USE_STD_([^ ]*) 2"
-      "#define ABSL_OPTION_USE_STD_\\1 0"
-      ABSL_INTERNAL_OPTIONS_H_PINNED
-      "${ABSL_INTERNAL_OPTIONS_H_CONTENTS}")
+      set(ABSL_INTERNAL_OPTIONS_H_CONTENTS "${ABSL_INTERNAL_OPTIONS_H_PINNED}")
+    endforeach()
   endif()
+
+  # Handle features that require at least C++17.
+  if (ABSL_INTERNAL_AT_LEAST_CXX17)
+    foreach(FEATURE "ANY" "OPTIONAL" "STRING_VIEW" "VARIANT")
+      string(REPLACE
+      "#define ABSL_OPTION_USE_STD_${FEATURE} 2"
+      "#define ABSL_OPTION_USE_STD_${FEATURE} 1"
+      ABSL_INTERNAL_OPTIONS_H_PINNED
+      "${ABSL_INTERNAL_OPTIONS_H_CONTENTS}")
+      set(ABSL_INTERNAL_OPTIONS_H_CONTENTS "${ABSL_INTERNAL_OPTIONS_H_PINNED}")
+    endforeach()
+  endif()
+
+  # Any feature that still has the value of 2 (because it was not handled above)
+  # should be set to 0.
+  string(REGEX REPLACE
+    "#define ABSL_OPTION_USE_STD_([^ ]*) 2"
+    "#define ABSL_OPTION_USE_STD_\\1 0"
+    ABSL_INTERNAL_OPTIONS_H_PINNED
+    "${ABSL_INTERNAL_OPTIONS_H_CONTENTS}")
+
   file(WRITE "${CMAKE_BINARY_DIR}/options-pinned.h" "${ABSL_INTERNAL_OPTIONS_H_PINNED}")
 
   install(FILES "${CMAKE_BINARY_DIR}/options-pinned.h"
diff --git a/METADATA b/METADATA
index ebc58f7..6c196d6 100644
--- a/METADATA
+++ b/METADATA
@@ -12,7 +12,7 @@
     type: GIT
     value: "https://github.com/abseil/abseil-cpp"
   }
-  version: "20230802.2"
-  last_upgrade_date { year: 2023 month: 8 day: 18 }
+  version: "20240116.1"
+  last_upgrade_date { year: 2024 month: 2 day: 28 }
   license_type: NOTICE
 }
diff --git a/MODULE.bazel b/MODULE.bazel
new file mode 100644
index 0000000..efbc88b
--- /dev/null
+++ b/MODULE.bazel
@@ -0,0 +1,39 @@
+# Copyright 2024 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.
+
+# https://bazel.build/external/overview#bzlmod
+
+module(
+    name = "abseil-cpp",
+    version = "20240116.0",
+    compatibility_level = 1,
+)
+
+# Only direct dependencies need to be listed below.
+# Please keep the versions in sync with the versions in the WORKSPACE file.
+
+bazel_dep(name = "bazel_skylib",
+          version = "1.5.0")
+
+bazel_dep(name = "google_benchmark",
+          version = "1.8.3",
+          repo_name = "com_github_google_benchmark",
+          dev_dependency = True)
+
+bazel_dep(name = "googletest",
+          version = "1.14.0.bcr.1",
+          repo_name = "com_google_googletest")
+
+bazel_dep(name = "platforms",
+          version = "0.0.8")
diff --git a/PrivacyInfo.xcprivacy b/PrivacyInfo.xcprivacy
new file mode 100644
index 0000000..6af1641
--- /dev/null
+++ b/PrivacyInfo.xcprivacy
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>NSPrivacyTracking</key>
+	<false/>
+	<key>NSPrivacyCollectedDataTypes</key>
+	<array/>
+	<key>NSPrivacyTrackingDomains</key>
+	<array/>
+	<key>NSPrivacyAccessedAPITypes</key>
+	<array/>
+</dict>
+</plist>
diff --git a/WORKSPACE b/WORKSPACE
index fdb615f..0d88609 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -20,41 +20,40 @@
 
 # GoogleTest/GoogleMock framework. Used by most unit-tests.
 http_archive(
-    name = "com_google_googletest",  # 2023-08-02T16:45:10Z
-    sha256 = "1f357c27ca988c3f7c6b4bf68a9395005ac6761f034046e9dde0896e3aba00e4",
-    strip_prefix = "googletest-1.14.0",
-    # Keep this URL in sync with ABSL_GOOGLETEST_COMMIT in ci/cmake_common.sh.
-    urls = ["https://github.com/google/googletest/archive/refs/tags/v1.14.0.zip"],
+  name = "com_google_googletest",
+  sha256 = "8ad598c73ad796e0d8280b082cebd82a630d73e73cd3c70057938a6501bba5d7",
+  strip_prefix = "googletest-1.14.0",
+  # Keep this URL in sync with ABSL_GOOGLETEST_COMMIT in ci/cmake_common.sh and
+  # ci/windows_msvc_cmake.bat.
+  urls = ["https://github.com/google/googletest/archive/refs/tags/v1.14.0.tar.gz"],
 )
 
 # RE2 (the regular expression library used by GoogleTest)
 http_archive(
-    name = "com_googlesource_code_re2",  # 2023-03-17T11:36:51Z
-    sha256 = "cb8b5312a65f2598954545a76e8bce913f35fbb3a21a5c88797a4448e9f9b9d9",
-    strip_prefix = "re2-578843a516fd1da7084ae46209a75f3613b6065e",
-    urls = ["https://github.com/google/re2/archive/578843a516fd1da7084ae46209a75f3613b6065e.zip"],
+    name = "com_googlesource_code_re2",
+    sha256 = "828341ad08524618a626167bd320b0c2acc97bd1c28eff693a9ea33a7ed2a85f",
+    strip_prefix = "re2-2023-11-01",
+    urls = ["https://github.com/google/re2/releases/download/2023-11-01/re2-2023-11-01.zip"],
 )
 
 # Google benchmark.
 http_archive(
-    name = "com_github_google_benchmark",  # 2023-08-01T07:47:09Z
-    sha256 = "db1e39ee71dc38aa7e57ed007f2c8b3bb59e13656435974781a9dc0617d75cc9",
-    strip_prefix = "benchmark-02a354f3f323ae8256948e1dc77ddcb1dfc297da",
-    urls = ["https://github.com/google/benchmark/archive/02a354f3f323ae8256948e1dc77ddcb1dfc297da.zip"],
+    name = "com_github_google_benchmark",
+    sha256 = "6bc180a57d23d4d9515519f92b0c83d61b05b5bab188961f36ac7b06b0d9e9ce",
+    strip_prefix = "benchmark-1.8.3",
+    urls = ["https://github.com/google/benchmark/archive/refs/tags/v1.8.3.tar.gz"],
 )
 
 # Bazel Skylib.
 http_archive(
-    name = "bazel_skylib",  # 2023-05-31T19:24:07Z
-    sha256 = "08c0386f45821ce246bbbf77503c973246ed6ee5c3463e41efc197fa9bc3a7f4",
-    strip_prefix = "bazel-skylib-288731ef9f7f688932bd50e704a91a45ec185f9b",
-    urls = ["https://github.com/bazelbuild/bazel-skylib/archive/288731ef9f7f688932bd50e704a91a45ec185f9b.zip"],
+  name = "bazel_skylib",
+  sha256 = "cd55a062e763b9349921f0f5db8c3933288dc8ba4f76dd9416aac68acee3cb94",
+  urls = ["https://github.com/bazelbuild/bazel-skylib/releases/download/1.5.0/bazel-skylib-1.5.0.tar.gz"],
 )
 
 # Bazel platform rules.
 http_archive(
-    name = "platforms",  # 2023-07-28T19:44:27Z
-    sha256 = "40eb313613ff00a5c03eed20aba58890046f4d38dec7344f00bb9a8867853526",
-    strip_prefix = "platforms-4ad40ef271da8176d4fc0194d2089b8a76e19d7b",
-    urls = ["https://github.com/bazelbuild/platforms/archive/4ad40ef271da8176d4fc0194d2089b8a76e19d7b.zip"],
+    name = "platforms",
+    sha256 = "8150406605389ececb6da07cbcb509d5637a3ab9a24bc69b1101531367d89d74",
+    urls = ["https://github.com/bazelbuild/platforms/releases/download/0.0.8/platforms-0.0.8.tar.gz"],
 )
diff --git a/absl/BUILD.bazel b/absl/BUILD.bazel
index 253c0ae..14c30b3 100644
--- a/absl/BUILD.bazel
+++ b/absl/BUILD.bazel
@@ -68,6 +68,17 @@
     visibility = [":__subpackages__"],
 )
 
+# x64_windows-clang-cl - used for selecting clang-cl for CI builds
+platform(
+    name = "x64_windows-clang-cl",
+    constraint_values = [
+        "@platforms//cpu:x86_64",
+        "@platforms//os:windows",
+        "@bazel_tools//tools/cpp:clang-cl",
+    ],
+    visibility = [":__subpackages__"],
+)
+
 config_setting(
     name = "osx",
     constraint_values = [
diff --git a/absl/abseil.podspec.gen.py b/absl/abseil.podspec.gen.py
index 6375298..c83edbf 100755
--- a/absl/abseil.podspec.gen.py
+++ b/absl/abseil.podspec.gen.py
@@ -30,6 +30,9 @@
     :git => 'https://github.com/abseil/abseil-cpp.git',
     :tag => '${tag}',
   }
+  s.resource_bundles = {
+    s.module_name => 'PrivacyInfo.xcprivacy',
+  }
   s.module_name = 'absl'
   s.header_mappings_dir = 'absl'
   s.header_dir = 'absl'
diff --git a/absl/algorithm/BUILD.bazel b/absl/algorithm/BUILD.bazel
index 3a9ab01..ddf9e11 100644
--- a/absl/algorithm/BUILD.bazel
+++ b/absl/algorithm/BUILD.bazel
@@ -21,7 +21,14 @@
     "ABSL_TEST_COPTS",
 )
 
-package(default_visibility = ["//visibility:public"])
+package(
+    default_visibility = ["//visibility:public"],
+    features = [
+        "header_modules",
+        "layering_check",
+        "parse_headers",
+    ],
+)
 
 licenses(["notice"])
 
@@ -44,24 +51,11 @@
     deps = [
         ":algorithm",
         "//absl/base:config",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
 
-cc_binary(
-    name = "algorithm_benchmark",
-    testonly = 1,
-    srcs = ["equal_benchmark.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    tags = ["benchmark"],
-    deps = [
-        ":algorithm",
-        "//absl/base:core_headers",
-        "@com_github_google_benchmark//:benchmark_main",
-    ],
-)
-
 cc_library(
     name = "container",
     hdrs = [
@@ -72,6 +66,7 @@
     deps = [
         ":algorithm",
         "//absl/base:core_headers",
+        "//absl/base:nullability",
         "//absl/meta:type_traits",
     ],
 )
@@ -87,6 +82,7 @@
         "//absl/base:core_headers",
         "//absl/memory",
         "//absl/types:span",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
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/algorithm.h b/absl/algorithm/algorithm.h
index e9b4733..59aeed7 100644
--- a/absl/algorithm/algorithm.h
+++ b/absl/algorithm/algorithm.h
@@ -31,92 +31,17 @@
 namespace absl {
 ABSL_NAMESPACE_BEGIN
 
-namespace algorithm_internal {
-
-// Performs comparisons with operator==, similar to C++14's `std::equal_to<>`.
-struct EqualTo {
-  template <typename T, typename U>
-  bool operator()(const T& a, const U& b) const {
-    return a == b;
-  }
-};
-
-template <typename InputIter1, typename InputIter2, typename Pred>
-bool EqualImpl(InputIter1 first1, InputIter1 last1, InputIter2 first2,
-               InputIter2 last2, Pred pred, std::input_iterator_tag,
-               std::input_iterator_tag) {
-  while (true) {
-    if (first1 == last1) return first2 == last2;
-    if (first2 == last2) return false;
-    if (!pred(*first1, *first2)) return false;
-    ++first1;
-    ++first2;
-  }
-}
-
-template <typename InputIter1, typename InputIter2, typename Pred>
-bool EqualImpl(InputIter1 first1, InputIter1 last1, InputIter2 first2,
-               InputIter2 last2, Pred&& pred, std::random_access_iterator_tag,
-               std::random_access_iterator_tag) {
-  return (last1 - first1 == last2 - first2) &&
-         std::equal(first1, last1, first2, std::forward<Pred>(pred));
-}
-
-// When we are using our own internal predicate that just applies operator==, we
-// forward to the non-predicate form of std::equal. This enables an optimization
-// in libstdc++ that can result in std::memcmp being used for integer types.
-template <typename InputIter1, typename InputIter2>
-bool EqualImpl(InputIter1 first1, InputIter1 last1, InputIter2 first2,
-               InputIter2 last2, algorithm_internal::EqualTo /* unused */,
-               std::random_access_iterator_tag,
-               std::random_access_iterator_tag) {
-  return (last1 - first1 == last2 - first2) &&
-         std::equal(first1, last1, first2);
-}
-
-template <typename It>
-It RotateImpl(It first, It middle, It last, std::true_type) {
-  return std::rotate(first, middle, last);
-}
-
-template <typename It>
-It RotateImpl(It first, It middle, It last, std::false_type) {
-  std::rotate(first, middle, last);
-  return std::next(first, std::distance(middle, last));
-}
-
-}  // namespace algorithm_internal
-
 // equal()
+// rotate()
 //
-// Compares the equality of two ranges specified by pairs of iterators, using
-// the given predicate, returning true iff for each corresponding iterator i1
-// and i2 in the first and second range respectively, pred(*i1, *i2) == true
+// Historical note: Abseil once provided implementations of these algorithms
+// prior to their adoption in C++14. New code should prefer to use the std
+// variants.
 //
-// This comparison takes at most min(`last1` - `first1`, `last2` - `first2`)
-// invocations of the predicate. Additionally, if InputIter1 and InputIter2 are
-// both random-access iterators, and `last1` - `first1` != `last2` - `first2`,
-// then the predicate is never invoked and the function returns false.
-//
-// This is a C++11-compatible implementation of C++14 `std::equal`.  See
-// https://en.cppreference.com/w/cpp/algorithm/equal for more information.
-template <typename InputIter1, typename InputIter2, typename Pred>
-bool equal(InputIter1 first1, InputIter1 last1, InputIter2 first2,
-           InputIter2 last2, Pred&& pred) {
-  return algorithm_internal::EqualImpl(
-      first1, last1, first2, last2, std::forward<Pred>(pred),
-      typename std::iterator_traits<InputIter1>::iterator_category{},
-      typename std::iterator_traits<InputIter2>::iterator_category{});
-}
-
-// Overload of equal() that performs comparison of two ranges specified by pairs
-// of iterators using operator==.
-template <typename InputIter1, typename InputIter2>
-bool equal(InputIter1 first1, InputIter1 last1, InputIter2 first2,
-           InputIter2 last2) {
-  return absl::equal(first1, last1, first2, last2,
-                     algorithm_internal::EqualTo{});
-}
+// See the documentation for the STL <algorithm> header for more information:
+// https://en.cppreference.com/w/cpp/header/algorithm
+using std::equal;
+using std::rotate;
 
 // linear_search()
 //
@@ -133,26 +58,6 @@
   return std::find(first, last, value) != last;
 }
 
-// rotate()
-//
-// Performs a left rotation on a range of elements (`first`, `last`) such that
-// `middle` is now the first element. `rotate()` returns an iterator pointing to
-// the first element before rotation. This function is exactly the same as
-// `std::rotate`, but fixes a bug in gcc
-// <= 4.9 where `std::rotate` returns `void` instead of an iterator.
-//
-// The complexity of this algorithm is the same as that of `std::rotate`, but if
-// `ForwardIterator` is not a random-access iterator, then `absl::rotate`
-// performs an additional pass over the range to construct the return value.
-template <typename ForwardIterator>
-ForwardIterator rotate(ForwardIterator first, ForwardIterator middle,
-                       ForwardIterator last) {
-  return algorithm_internal::RotateImpl(
-      first, middle, last,
-      std::is_same<decltype(std::rotate(first, middle, last)),
-                   ForwardIterator>());
-}
-
 ABSL_NAMESPACE_END
 }  // namespace absl
 
diff --git a/absl/algorithm/algorithm_test.cc b/absl/algorithm/algorithm_test.cc
index d18df02..e6ee469 100644
--- a/absl/algorithm/algorithm_test.cc
+++ b/absl/algorithm/algorithm_test.cc
@@ -24,137 +24,6 @@
 
 namespace {
 
-TEST(EqualTest, DefaultComparisonRandomAccess) {
-  std::vector<int> v1{1, 2, 3};
-  std::vector<int> v2 = v1;
-  std::vector<int> v3 = {1, 2};
-  std::vector<int> v4 = {1, 2, 4};
-
-  EXPECT_TRUE(absl::equal(v1.begin(), v1.end(), v2.begin(), v2.end()));
-  EXPECT_FALSE(absl::equal(v1.begin(), v1.end(), v3.begin(), v3.end()));
-  EXPECT_FALSE(absl::equal(v1.begin(), v1.end(), v4.begin(), v4.end()));
-}
-
-TEST(EqualTest, DefaultComparison) {
-  std::list<int> lst1{1, 2, 3};
-  std::list<int> lst2 = lst1;
-  std::list<int> lst3{1, 2};
-  std::list<int> lst4{1, 2, 4};
-
-  EXPECT_TRUE(absl::equal(lst1.begin(), lst1.end(), lst2.begin(), lst2.end()));
-  EXPECT_FALSE(absl::equal(lst1.begin(), lst1.end(), lst3.begin(), lst3.end()));
-  EXPECT_FALSE(absl::equal(lst1.begin(), lst1.end(), lst4.begin(), lst4.end()));
-}
-
-TEST(EqualTest, EmptyRange) {
-  std::vector<int> v1{1, 2, 3};
-  std::vector<int> empty1;
-  std::vector<int> empty2;
-
-  // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105705
-#if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(12, 0)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wnonnull"
-#endif
-  EXPECT_FALSE(absl::equal(v1.begin(), v1.end(), empty1.begin(), empty1.end()));
-#if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(12, 0)
-#pragma GCC diagnostic pop
-#endif
-  EXPECT_FALSE(absl::equal(empty1.begin(), empty1.end(), v1.begin(), v1.end()));
-  EXPECT_TRUE(
-      absl::equal(empty1.begin(), empty1.end(), empty2.begin(), empty2.end()));
-}
-
-TEST(EqualTest, MixedIterTypes) {
-  std::vector<int> v1{1, 2, 3};
-  std::list<int> lst1{v1.begin(), v1.end()};
-  std::list<int> lst2{1, 2, 4};
-  std::list<int> lst3{1, 2};
-
-  EXPECT_TRUE(absl::equal(v1.begin(), v1.end(), lst1.begin(), lst1.end()));
-  EXPECT_FALSE(absl::equal(v1.begin(), v1.end(), lst2.begin(), lst2.end()));
-  EXPECT_FALSE(absl::equal(v1.begin(), v1.end(), lst3.begin(), lst3.end()));
-}
-
-TEST(EqualTest, MixedValueTypes) {
-  std::vector<int> v1{1, 2, 3};
-  std::vector<char> v2{1, 2, 3};
-  std::vector<char> v3{1, 2};
-  std::vector<char> v4{1, 2, 4};
-
-  EXPECT_TRUE(absl::equal(v1.begin(), v1.end(), v2.begin(), v2.end()));
-  EXPECT_FALSE(absl::equal(v1.begin(), v1.end(), v3.begin(), v3.end()));
-  EXPECT_FALSE(absl::equal(v1.begin(), v1.end(), v4.begin(), v4.end()));
-}
-
-TEST(EqualTest, WeirdIterators) {
-  std::vector<bool> v1{true, false};
-  std::vector<bool> v2 = v1;
-  std::vector<bool> v3{true};
-  std::vector<bool> v4{true, true, true};
-
-  EXPECT_TRUE(absl::equal(v1.begin(), v1.end(), v2.begin(), v2.end()));
-  EXPECT_FALSE(absl::equal(v1.begin(), v1.end(), v3.begin(), v3.end()));
-  EXPECT_FALSE(absl::equal(v1.begin(), v1.end(), v4.begin(), v4.end()));
-}
-
-TEST(EqualTest, CustomComparison) {
-  int n[] = {1, 2, 3, 4};
-  std::vector<int*> v1{&n[0], &n[1], &n[2]};
-  std::vector<int*> v2 = v1;
-  std::vector<int*> v3{&n[0], &n[1], &n[3]};
-  std::vector<int*> v4{&n[0], &n[1]};
-
-  auto eq = [](int* a, int* b) { return *a == *b; };
-
-  EXPECT_TRUE(absl::equal(v1.begin(), v1.end(), v2.begin(), v2.end(), eq));
-  EXPECT_FALSE(absl::equal(v1.begin(), v1.end(), v3.begin(), v3.end(), eq));
-  EXPECT_FALSE(absl::equal(v1.begin(), v1.end(), v4.begin(), v4.end(), eq));
-}
-
-TEST(EqualTest, MoveOnlyPredicate) {
-  std::vector<int> v1{1, 2, 3};
-  std::vector<int> v2{4, 5, 6};
-
-  // move-only equality predicate
-  struct Eq {
-    Eq() = default;
-    Eq(Eq &&) = default;
-    Eq(const Eq &) = delete;
-    Eq &operator=(const Eq &) = delete;
-    bool operator()(const int a, const int b) const { return a == b; }
-  };
-
-  EXPECT_TRUE(absl::equal(v1.begin(), v1.end(), v1.begin(), v1.end(), Eq()));
-  EXPECT_FALSE(absl::equal(v1.begin(), v1.end(), v2.begin(), v2.end(), Eq()));
-}
-
-struct CountingTrivialPred {
-  int* count;
-  bool operator()(int, int) const {
-    ++*count;
-    return true;
-  }
-};
-
-TEST(EqualTest, RandomAccessComplexity) {
-  std::vector<int> v1{1, 1, 3};
-  std::vector<int> v2 = v1;
-  std::vector<int> v3{1, 2};
-
-  do {
-    int count = 0;
-    absl::equal(v1.begin(), v1.end(), v2.begin(), v2.end(),
-                CountingTrivialPred{&count});
-    EXPECT_LE(count, 3);
-  } while (std::next_permutation(v2.begin(), v2.end()));
-
-  int count = 0;
-  absl::equal(v1.begin(), v1.end(), v3.begin(), v3.end(),
-              CountingTrivialPred{&count});
-  EXPECT_EQ(count, 0);
-}
-
 class LinearSearchTest : public testing::Test {
  protected:
   LinearSearchTest() : container_{1, 2, 3} {}
@@ -178,14 +47,4 @@
       absl::linear_search(const_container->begin(), const_container->end(), 4));
 }
 
-TEST(RotateTest, Rotate) {
-  std::vector<int> v{0, 1, 2, 3, 4};
-  EXPECT_EQ(*absl::rotate(v.begin(), v.begin() + 2, v.end()), 0);
-  EXPECT_THAT(v, testing::ElementsAreArray({2, 3, 4, 0, 1}));
-
-  std::list<int> l{0, 1, 2, 3, 4};
-  EXPECT_EQ(*absl::rotate(l.begin(), std::next(l.begin(), 3), l.end()), 0);
-  EXPECT_THAT(l, testing::ElementsAreArray({3, 4, 0, 1, 2}));
-}
-
 }  // namespace
diff --git a/absl/algorithm/container.h b/absl/algorithm/container.h
index 679e026..c7bafae 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 {
@@ -116,18 +117,6 @@
 struct IsUnorderedContainer<std::unordered_set<Key, Hash, KeyEqual, Allocator>>
     : std::true_type {};
 
-// container_algorithm_internal::c_size. It is meant for internal use only.
-
-template <class C>
-auto c_size(C& c) -> decltype(c.size()) {
-  return c.size();
-}
-
-template <class T, std::size_t N>
-constexpr std::size_t c_size(T (&)[N]) {
-  return N;
-}
-
 }  // namespace container_algorithm_internal
 
 // PUBLIC API
@@ -348,20 +337,10 @@
 template <typename C1, typename C2>
 container_algorithm_internal::ContainerIterPairType<C1, C2> c_mismatch(C1& c1,
                                                                        C2& c2) {
-  auto first1 = container_algorithm_internal::c_begin(c1);
-  auto last1 = container_algorithm_internal::c_end(c1);
-  auto first2 = container_algorithm_internal::c_begin(c2);
-  auto last2 = container_algorithm_internal::c_end(c2);
-
-  for (; first1 != last1 && first2 != last2; ++first1, (void)++first2) {
-    // Negates equality because Cpp17EqualityComparable doesn't require clients
-    // to overload both `operator==` and `operator!=`.
-    if (!(*first1 == *first2)) {
-      break;
-    }
-  }
-
-  return std::make_pair(first1, first2);
+  return std::mismatch(container_algorithm_internal::c_begin(c1),
+                       container_algorithm_internal::c_end(c1),
+                       container_algorithm_internal::c_begin(c2),
+                       container_algorithm_internal::c_end(c2));
 }
 
 // Overload of c_mismatch() for using a predicate evaluation other than `==` as
@@ -370,56 +349,33 @@
 template <typename C1, typename C2, typename BinaryPredicate>
 container_algorithm_internal::ContainerIterPairType<C1, C2> c_mismatch(
     C1& c1, C2& c2, BinaryPredicate pred) {
-  auto first1 = container_algorithm_internal::c_begin(c1);
-  auto last1 = container_algorithm_internal::c_end(c1);
-  auto first2 = container_algorithm_internal::c_begin(c2);
-  auto last2 = container_algorithm_internal::c_end(c2);
-
-  for (; first1 != last1 && first2 != last2; ++first1, (void)++first2) {
-    if (!pred(*first1, *first2)) {
-      break;
-    }
-  }
-
-  return std::make_pair(first1, first2);
+  return std::mismatch(container_algorithm_internal::c_begin(c1),
+                       container_algorithm_internal::c_end(c1),
+                       container_algorithm_internal::c_begin(c2),
+                       container_algorithm_internal::c_end(c2), pred);
 }
 
 // c_equal()
 //
 // Container-based version of the <algorithm> `std::equal()` function to
 // test whether two containers are equal.
-//
-// NOTE: the semantics of c_equal() are slightly different than those of
-// equal(): while the latter iterates over the second container only up to the
-// size of the first container, c_equal() also checks whether the container
-// sizes are equal.  This better matches expectations about c_equal() based on
-// its signature.
-//
-// Example:
-//   vector v1 = <1, 2, 3>;
-//   vector v2 = <1, 2, 3, 4>;
-//   equal(std::begin(v1), std::end(v1), std::begin(v2)) returns true
-//   c_equal(v1, v2) returns false
-
 template <typename C1, typename C2>
 bool c_equal(const C1& c1, const C2& c2) {
-  return ((container_algorithm_internal::c_size(c1) ==
-           container_algorithm_internal::c_size(c2)) &&
-          std::equal(container_algorithm_internal::c_begin(c1),
-                     container_algorithm_internal::c_end(c1),
-                     container_algorithm_internal::c_begin(c2)));
+  return std::equal(container_algorithm_internal::c_begin(c1),
+                    container_algorithm_internal::c_end(c1),
+                    container_algorithm_internal::c_begin(c2),
+                    container_algorithm_internal::c_end(c2));
 }
 
 // Overload of c_equal() for using a predicate evaluation other than `==` as
 // the function's test condition.
 template <typename C1, typename C2, typename BinaryPredicate>
 bool c_equal(const C1& c1, const C2& c2, BinaryPredicate&& pred) {
-  return ((container_algorithm_internal::c_size(c1) ==
-           container_algorithm_internal::c_size(c2)) &&
-          std::equal(container_algorithm_internal::c_begin(c1),
-                     container_algorithm_internal::c_end(c1),
-                     container_algorithm_internal::c_begin(c2),
-                     std::forward<BinaryPredicate>(pred)));
+  return std::equal(container_algorithm_internal::c_begin(c1),
+                    container_algorithm_internal::c_end(c1),
+                    container_algorithm_internal::c_begin(c2),
+                    container_algorithm_internal::c_end(c2),
+                    std::forward<BinaryPredicate>(pred));
 }
 
 // c_is_permutation()
@@ -428,20 +384,20 @@
 // to test whether a container is a permutation of another.
 template <typename C1, typename C2>
 bool c_is_permutation(const C1& c1, const C2& c2) {
-  using std::begin;
-  using std::end;
-  return c1.size() == c2.size() &&
-         std::is_permutation(begin(c1), end(c1), begin(c2));
+  return std::is_permutation(container_algorithm_internal::c_begin(c1),
+                             container_algorithm_internal::c_end(c1),
+                             container_algorithm_internal::c_begin(c2),
+                             container_algorithm_internal::c_end(c2));
 }
 
 // Overload of c_is_permutation() for using a predicate evaluation other than
 // `==` as the function's test condition.
 template <typename C1, typename C2, typename BinaryPredicate>
 bool c_is_permutation(const C1& c1, const C2& c2, BinaryPredicate&& pred) {
-  using std::begin;
-  using std::end;
-  return c1.size() == c2.size() &&
-         std::is_permutation(begin(c1), end(c1), begin(c2),
+  return std::is_permutation(container_algorithm_internal::c_begin(c1),
+                             container_algorithm_internal::c_end(c1),
+                             container_algorithm_internal::c_begin(c2),
+                             container_algorithm_internal::c_end(c2),
                              std::forward<BinaryPredicate>(pred));
 }
 
@@ -818,6 +774,36 @@
                std::forward<UniformRandomBitGenerator>(gen));
 }
 
+// c_sample()
+//
+// Container-based version of the <algorithm> `std::sample()` function to
+// randomly sample elements from the container without replacement using a
+// `gen()` uniform random number generator and write them to an iterator range.
+template <typename C, typename OutputIterator, typename Distance,
+          typename UniformRandomBitGenerator>
+OutputIterator c_sample(const C& c, OutputIterator result, Distance n,
+                        UniformRandomBitGenerator&& gen) {
+#if defined(__cpp_lib_sample) && __cpp_lib_sample >= 201603L
+  return std::sample(container_algorithm_internal::c_begin(c),
+                     container_algorithm_internal::c_end(c), result, n,
+                     std::forward<UniformRandomBitGenerator>(gen));
+#else
+  // Fall back to a stable selection-sampling implementation.
+  auto first = container_algorithm_internal::c_begin(c);
+  Distance unsampled_elements = c_distance(c);
+  n = (std::min)(n, unsampled_elements);
+  for (; n != 0; ++first) {
+    Distance r =
+        std::uniform_int_distribution<Distance>(0, --unsampled_elements)(gen);
+    if (r < n) {
+      *result++ = *first;
+      --n;
+    }
+  }
+  return result;
+#endif
+}
+
 //------------------------------------------------------------------------------
 // <algorithm> Partition functions
 //------------------------------------------------------------------------------
@@ -1657,7 +1643,7 @@
 //
 // Container-based version of the <numeric> `std::iota()` function
 // to compute successive values of `value`, as if incremented with `++value`
-// after each element is written. and write them to the container.
+// after each element is written, and write them to the container.
 template <typename Sequence, typename T>
 void c_iota(Sequence& sequence, const T& value) {
   std::iota(container_algorithm_internal::c_begin(sequence),
diff --git a/absl/algorithm/container_test.cc b/absl/algorithm/container_test.cc
index 0fbc777..c01f5fc 100644
--- a/absl/algorithm/container_test.cc
+++ b/absl/algorithm/container_test.cc
@@ -14,6 +14,7 @@
 
 #include "absl/algorithm/container.h"
 
+#include <algorithm>
 #include <functional>
 #include <initializer_list>
 #include <iterator>
@@ -40,8 +41,10 @@
 using ::testing::ElementsAre;
 using ::testing::Gt;
 using ::testing::IsNull;
+using ::testing::IsSubsetOf;
 using ::testing::Lt;
 using ::testing::Pointee;
+using ::testing::SizeIs;
 using ::testing::Truly;
 using ::testing::UnorderedElementsAre;
 
@@ -963,12 +966,29 @@
   EXPECT_THAT(actual, ElementsAre(3, 4, 1, 2, 5));
 }
 
+template <typename T>
+T RandomlySeededPrng() {
+  std::random_device rdev;
+  std::seed_seq::result_type data[T::state_size];
+  std::generate_n(data, T::state_size, std::ref(rdev));
+  std::seed_seq prng_seed(data, data + T::state_size);
+  return T(prng_seed);
+}
+
 TEST(MutatingTest, Shuffle) {
   std::vector<int> actual = {1, 2, 3, 4, 5};
-  absl::c_shuffle(actual, std::random_device());
+  absl::c_shuffle(actual, RandomlySeededPrng<std::mt19937_64>());
   EXPECT_THAT(actual, UnorderedElementsAre(1, 2, 3, 4, 5));
 }
 
+TEST(MutatingTest, Sample) {
+  std::vector<int> actual;
+  absl::c_sample(std::vector<int>{1, 2, 3, 4, 5}, std::back_inserter(actual), 3,
+                 RandomlySeededPrng<std::mt19937_64>());
+  EXPECT_THAT(actual, IsSubsetOf({1, 2, 3, 4, 5}));
+  EXPECT_THAT(actual, SizeIs(3));
+}
+
 TEST(MutatingTest, PartialSort) {
   std::vector<int> sequence{5, 3, 42, 0};
   absl::c_partial_sort(sequence, sequence.begin() + 2);
diff --git a/absl/algorithm/equal_benchmark.cc b/absl/algorithm/equal_benchmark.cc
deleted file mode 100644
index 948cd65..0000000
--- a/absl/algorithm/equal_benchmark.cc
+++ /dev/null
@@ -1,126 +0,0 @@
-// Copyright 2017 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 <cstdint>
-#include <cstring>
-
-#include "absl/algorithm/algorithm.h"
-#include "benchmark/benchmark.h"
-
-namespace {
-
-// The range of sequence sizes to benchmark.
-constexpr int kMinBenchmarkSize = 1024;
-constexpr int kMaxBenchmarkSize = 8 * 1024 * 1024;
-
-// A user-defined type for use in equality benchmarks. Note that we expect
-// std::memcmp to win for this type: libstdc++'s std::equal only defers to
-// memcmp for integral types. This is because it is not straightforward to
-// guarantee that std::memcmp would produce a result "as-if" compared by
-// operator== for other types (example gotchas: NaN floats, structs with
-// padding).
-struct EightBits {
-  explicit EightBits(int /* unused */) : data(0) {}
-  bool operator==(const EightBits& rhs) const { return data == rhs.data; }
-  uint8_t data;
-};
-
-template <typename T>
-void BM_absl_equal_benchmark(benchmark::State& state) {
-  std::vector<T> xs(state.range(0), T(0));
-  std::vector<T> ys = xs;
-  while (state.KeepRunning()) {
-    const bool same = absl::equal(xs.begin(), xs.end(), ys.begin(), ys.end());
-    benchmark::DoNotOptimize(same);
-  }
-}
-
-template <typename T>
-void BM_std_equal_benchmark(benchmark::State& state) {
-  std::vector<T> xs(state.range(0), T(0));
-  std::vector<T> ys = xs;
-  while (state.KeepRunning()) {
-    const bool same = std::equal(xs.begin(), xs.end(), ys.begin());
-    benchmark::DoNotOptimize(same);
-  }
-}
-
-template <typename T>
-void BM_memcmp_benchmark(benchmark::State& state) {
-  std::vector<T> xs(state.range(0), T(0));
-  std::vector<T> ys = xs;
-  while (state.KeepRunning()) {
-    const bool same =
-        std::memcmp(xs.data(), ys.data(), xs.size() * sizeof(T)) == 0;
-    benchmark::DoNotOptimize(same);
-  }
-}
-
-// The expectation is that the compiler should be able to elide the equality
-// comparison altogether for sufficiently simple types.
-template <typename T>
-void BM_absl_equal_self_benchmark(benchmark::State& state) {
-  std::vector<T> xs(state.range(0), T(0));
-  while (state.KeepRunning()) {
-    const bool same = absl::equal(xs.begin(), xs.end(), xs.begin(), xs.end());
-    benchmark::DoNotOptimize(same);
-  }
-}
-
-BENCHMARK_TEMPLATE(BM_absl_equal_benchmark, uint8_t)
-    ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
-BENCHMARK_TEMPLATE(BM_std_equal_benchmark, uint8_t)
-    ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
-BENCHMARK_TEMPLATE(BM_memcmp_benchmark, uint8_t)
-    ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
-BENCHMARK_TEMPLATE(BM_absl_equal_self_benchmark, uint8_t)
-    ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
-
-BENCHMARK_TEMPLATE(BM_absl_equal_benchmark, uint16_t)
-    ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
-BENCHMARK_TEMPLATE(BM_std_equal_benchmark, uint16_t)
-    ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
-BENCHMARK_TEMPLATE(BM_memcmp_benchmark, uint16_t)
-    ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
-BENCHMARK_TEMPLATE(BM_absl_equal_self_benchmark, uint16_t)
-    ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
-
-BENCHMARK_TEMPLATE(BM_absl_equal_benchmark, uint32_t)
-    ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
-BENCHMARK_TEMPLATE(BM_std_equal_benchmark, uint32_t)
-    ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
-BENCHMARK_TEMPLATE(BM_memcmp_benchmark, uint32_t)
-    ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
-BENCHMARK_TEMPLATE(BM_absl_equal_self_benchmark, uint32_t)
-    ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
-
-BENCHMARK_TEMPLATE(BM_absl_equal_benchmark, uint64_t)
-    ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
-BENCHMARK_TEMPLATE(BM_std_equal_benchmark, uint64_t)
-    ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
-BENCHMARK_TEMPLATE(BM_memcmp_benchmark, uint64_t)
-    ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
-BENCHMARK_TEMPLATE(BM_absl_equal_self_benchmark, uint64_t)
-    ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
-
-BENCHMARK_TEMPLATE(BM_absl_equal_benchmark, EightBits)
-    ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
-BENCHMARK_TEMPLATE(BM_std_equal_benchmark, EightBits)
-    ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
-BENCHMARK_TEMPLATE(BM_memcmp_benchmark, EightBits)
-    ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
-BENCHMARK_TEMPLATE(BM_absl_equal_self_benchmark, EightBits)
-    ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
-
-}  // namespace
diff --git a/absl/base/BUILD.bazel b/absl/base/BUILD.bazel
index fb008db..0eb735d 100644
--- a/absl/base/BUILD.bazel
+++ b/absl/base/BUILD.bazel
@@ -21,7 +21,14 @@
     "ABSL_TEST_COPTS",
 )
 
-package(default_visibility = ["//visibility:public"])
+package(
+    default_visibility = ["//visibility:public"],
+    features = [
+        "header_modules",
+        "layering_check",
+        "parse_headers",
+    ],
+)
 
 licenses(["notice"])
 
@@ -63,6 +70,14 @@
 )
 
 cc_library(
+    name = "no_destructor",
+    hdrs = ["no_destructor.h"],
+    copts = ABSL_DEFAULT_COPTS,
+    linkopts = ABSL_DEFAULT_LINKOPTS,
+    deps = [":config"],
+)
+
+cc_library(
     name = "nullability",
     srcs = ["internal/nullability_impl.h"],
     hdrs = ["nullability.h"],
@@ -160,9 +175,6 @@
 
 cc_library(
     name = "core_headers",
-    srcs = [
-        "internal/thread_annotations.h",
-    ],
     hdrs = [
         "attributes.h",
         "const_init.h",
@@ -273,6 +285,7 @@
         ":cycleclock_internal",
         ":dynamic_annotations",
         ":log_severity",
+        ":nullability",
         ":raw_logging_internal",
         ":spinlock_wait",
         "//absl/meta:type_traits",
@@ -302,6 +315,7 @@
         ":atomic_hook",
         ":atomic_hook_test_helper",
         ":core_headers",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -317,6 +331,7 @@
     deps = [
         ":base",
         ":core_headers",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -344,6 +359,7 @@
     deps = [
         ":config",
         ":throw_delegate",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -357,6 +373,7 @@
     deps = [
         ":errno_saver",
         ":strerror",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -380,7 +397,9 @@
     name = "pretty_function",
     hdrs = ["internal/pretty_function.h"],
     linkopts = ABSL_DEFAULT_LINKOPTS,
-    visibility = ["//absl:__subpackages__"],
+    visibility = [
+        "//absl:__subpackages__",
+    ],
 )
 
 cc_library(
@@ -409,6 +428,7 @@
     deps = [
         ":exception_safety_testing",
         "//absl/memory",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -426,6 +446,7 @@
     linkopts = ABSL_DEFAULT_LINKOPTS,
     deps = [
         ":base_internal",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -440,6 +461,7 @@
         ":base_internal",
         "//absl/memory",
         "//absl/strings",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -478,6 +500,7 @@
         ":config",
         ":core_headers",
         "//absl/synchronization",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -494,6 +517,7 @@
     deps = [
         ":base",
         ":base_internal",
+        ":no_destructor",
         ":raw_logging_internal",
         "//absl/synchronization",
         "@com_github_google_benchmark//:benchmark_main",
@@ -525,6 +549,7 @@
         ":base",
         ":config",
         ":core_headers",
+        ":nullability",
     ],
 )
 
@@ -535,6 +560,7 @@
     deps = [
         ":config",
         ":endian",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -547,6 +573,7 @@
     deps = [
         ":config",
         "//absl/synchronization:thread_pool",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -560,16 +587,47 @@
         ":base",
         ":core_headers",
         "//absl/synchronization",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
 
 cc_test(
+    name = "no_destructor_test",
+    srcs = ["no_destructor_test.cc"],
+    copts = ABSL_TEST_COPTS,
+    linkopts = ABSL_DEFAULT_LINKOPTS,
+    deps = [
+        ":config",
+        ":no_destructor",
+        ":raw_logging_internal",
+        "@com_google_googletest//:gtest",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
+
+cc_binary(
+    name = "no_destructor_benchmark",
+    testonly = 1,
+    srcs = ["no_destructor_benchmark.cc"],
+    copts = ABSL_TEST_COPTS,
+    linkopts = ABSL_DEFAULT_LINKOPTS,
+    tags = ["benchmark"],
+    visibility = ["//visibility:private"],
+    deps = [
+        ":no_destructor",
+        ":raw_logging_internal",
+        "@com_github_google_benchmark//:benchmark_main",
+    ],
+)
+
+cc_test(
     name = "nullability_test",
     srcs = ["nullability_test.cc"],
     deps = [
         ":core_headers",
         ":nullability",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -582,6 +640,7 @@
     deps = [
         ":raw_logging_internal",
         "//absl/strings",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -595,6 +654,7 @@
     deps = [
         ":base",
         "//absl/synchronization",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -628,6 +688,7 @@
         ":base",
         ":core_headers",
         "//absl/synchronization",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -643,6 +704,7 @@
         ":base",
         "//absl/synchronization",
         "@com_github_google_benchmark//:benchmark_main",
+        "@com_google_googletest//:gtest",
     ],
 )
 
@@ -669,6 +731,7 @@
     linkopts = ABSL_DEFAULT_LINKOPTS,
     deps = [
         ":scoped_set_env",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -684,6 +747,7 @@
         "//absl/flags:flag_internal",
         "//absl/flags:marshalling",
         "//absl/strings",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -713,6 +777,7 @@
     deps = [
         ":strerror",
         "//absl/strings",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -752,6 +817,7 @@
     linkopts = ABSL_DEFAULT_LINKOPTS,
     deps = [
         ":fast_type_id",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -759,14 +825,13 @@
 cc_library(
     name = "prefetch",
     hdrs = [
-        "internal/prefetch.h",
         "prefetch.h",
     ],
     copts = ABSL_DEFAULT_COPTS,
     linkopts = ABSL_DEFAULT_LINKOPTS,
     deps = [
         ":config",
-        ":core_headers",  # TODO(b/265984188): remove
+        ":core_headers",
     ],
 )
 
@@ -774,13 +839,13 @@
     name = "prefetch_test",
     size = "small",
     srcs = [
-        "internal/prefetch_test.cc",
         "prefetch_test.cc",
     ],
     copts = ABSL_TEST_COPTS,
     linkopts = ABSL_DEFAULT_LINKOPTS,
     deps = [
         ":prefetch",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -795,6 +860,7 @@
     deps = [
         ":core_headers",
         "//absl/strings",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -808,6 +874,7 @@
     deps = [
         ":core_headers",
         "//absl/types:optional",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
diff --git a/absl/base/CMakeLists.txt b/absl/base/CMakeLists.txt
index 76c4ff1..4cfc228 100644
--- a/absl/base/CMakeLists.txt
+++ b/absl/base/CMakeLists.txt
@@ -49,6 +49,7 @@
   SRCS
     "log_severity.cc"
   DEPS
+    absl::config
     absl::core_headers
   COPTS
     ${ABSL_DEFAULT_COPTS}
@@ -56,6 +57,17 @@
 
 absl_cc_library(
   NAME
+    no_destructor
+  HDRS
+    "no_destructor.h"
+  DEPS
+    absl::config
+  COPTS
+    ${ABSL_DEFAULT_COPTS}
+)
+
+absl_cc_library(
+  NAME
     nullability
   HDRS
     "nullability.h"
@@ -155,7 +167,6 @@
     "optimization.h"
     "port.h"
     "thread_annotations.h"
-    "internal/thread_annotations.h"
   COPTS
     ${ABSL_DEFAULT_COPTS}
   DEPS
@@ -236,6 +247,7 @@
     absl::core_headers
     absl::dynamic_annotations
     absl::log_severity
+    absl::nullability
     absl::raw_logging_internal
     absl::spinlock_wait
     absl::type_traits
@@ -464,6 +476,7 @@
     absl::base
     absl::config
     absl::core_headers
+    absl::nullability
   PUBLIC
 )
 
@@ -510,6 +523,20 @@
 
 absl_cc_test(
   NAME
+    no_destructor_test
+  SRCS
+    "no_destructor_test.cc"
+  COPTS
+    ${ABSL_TEST_COPTS}
+  DEPS
+    absl::no_destructor
+    absl::config
+    absl::raw_logging_internal
+    GTest::gtest_main
+)
+
+absl_cc_test(
+  NAME
     raw_logging_test
   SRCS
     "raw_logging_test.cc"
@@ -677,14 +704,13 @@
     prefetch
   HDRS
     "prefetch.h"
-    "internal/prefetch.h"
   COPTS
     ${ABSL_DEFAULT_COPTS}
   LINKOPTS
     ${ABSL_DEFAULT_LINKOPTS}
   DEPS
     absl::config
-    absl::core_headers  # TODO(b/265984188): remove
+    absl::core_headers
 )
 
 absl_cc_test(
@@ -692,7 +718,6 @@
     prefetch_test
   SRCS
     "prefetch_test.cc"
-    "internal/prefetch_test.cc"
   COPTS
     ${ABSL_TEST_COPTS}
   DEPS
diff --git a/absl/base/attributes.h b/absl/base/attributes.h
index a7f279a..d4f67a1 100644
--- a/absl/base/attributes.h
+++ b/absl/base/attributes.h
@@ -687,7 +687,7 @@
 
 // When deprecating Abseil code, it is sometimes necessary to turn off the
 // warning within Abseil, until the deprecated code is actually removed. The
-// deprecated code can be surrounded with these directives to acheive that
+// deprecated code can be surrounded with these directives to achieve that
 // result.
 //
 // class ABSL_DEPRECATED("Use Bar instead") Foo;
@@ -747,9 +747,52 @@
 #define ABSL_CONST_INIT
 #endif
 
-// These annotations are not available yet due to fear of breaking code.
-#define ABSL_ATTRIBUTE_PURE_FUNCTION
-#define ABSL_ATTRIBUTE_CONST_FUNCTION
+// ABSL_ATTRIBUTE_PURE_FUNCTION
+//
+// ABSL_ATTRIBUTE_PURE_FUNCTION is used to annotate declarations of "pure"
+// functions. A function is pure if its return value is only a function of its
+// arguments. The pure attribute prohibits a function from modifying the state
+// of the program that is observable by means other than inspecting the
+// function's return value. Declaring such functions with the pure attribute
+// allows the compiler to avoid emitting some calls in repeated invocations of
+// the function with the same argument values.
+//
+// Example:
+//
+//  ABSL_ATTRIBUTE_PURE_FUNCTION std::string FormatTime(Time t);
+#if ABSL_HAVE_CPP_ATTRIBUTE(gnu::pure)
+#define ABSL_ATTRIBUTE_PURE_FUNCTION [[gnu::pure]]
+#elif ABSL_HAVE_ATTRIBUTE(pure)
+#define ABSL_ATTRIBUTE_PURE_FUNCTION __attribute__((pure))
+#else
+// If the attribute isn't defined, we'll fallback to ABSL_MUST_USE_RESULT since
+// pure functions are useless if its return is ignored.
+#define ABSL_ATTRIBUTE_PURE_FUNCTION ABSL_MUST_USE_RESULT
+#endif
+
+// ABSL_ATTRIBUTE_CONST_FUNCTION
+//
+// ABSL_ATTRIBUTE_CONST_FUNCTION is used to annotate declarations of "const"
+// functions. A const function is similar to a pure function, with one
+// exception: Pure functions may return value that depend on a non-volatile
+// object that isn't provided as a function argument, while the const function
+// is guaranteed to return the same result given the same arguments.
+//
+// Example:
+//
+//  ABSL_ATTRIBUTE_CONST_FUNCTION int64_t ToInt64Milliseconds(Duration d);
+#if defined(_MSC_VER) && !defined(__clang__)
+// Put the MSVC case first since MSVC seems to parse const as a C++ keyword.
+#define ABSL_ATTRIBUTE_CONST_FUNCTION ABSL_ATTRIBUTE_PURE_FUNCTION
+#elif ABSL_HAVE_CPP_ATTRIBUTE(gnu::const)
+#define ABSL_ATTRIBUTE_CONST_FUNCTION [[gnu::const]]
+#elif ABSL_HAVE_ATTRIBUTE(const)
+#define ABSL_ATTRIBUTE_CONST_FUNCTION __attribute__((const))
+#else
+// Since const functions are more restrictive pure function, we'll fallback to a
+// pure function if the const attribute is not handled.
+#define ABSL_ATTRIBUTE_CONST_FUNCTION ABSL_ATTRIBUTE_PURE_FUNCTION
+#endif
 
 // ABSL_ATTRIBUTE_LIFETIME_BOUND indicates that a resource owned by a function
 // parameter or implicit object parameter is retained by the return value of the
@@ -800,15 +843,11 @@
 // See also the upstream documentation:
 // https://clang.llvm.org/docs/AttributeReference.html#trivial-abi
 //
-#if ABSL_HAVE_CPP_ATTRIBUTE(clang::trivial_abi)
-#define ABSL_ATTRIBUTE_TRIVIAL_ABI [[clang::trivial_abi]]
-#define ABSL_HAVE_ATTRIBUTE_TRIVIAL_ABI 1
-#elif ABSL_HAVE_ATTRIBUTE(trivial_abi)
-#define ABSL_ATTRIBUTE_TRIVIAL_ABI __attribute__((trivial_abi))
-#define ABSL_HAVE_ATTRIBUTE_TRIVIAL_ABI 1
-#else
+// b/321691395 - This is currently disabled in open-source builds since
+// compiler support differs. If system libraries compiled with GCC are mixed
+// with libraries compiled with Clang, types will have different ideas about
+// their ABI, leading to hard to debug crashes.
 #define ABSL_ATTRIBUTE_TRIVIAL_ABI
-#endif
 
 // ABSL_ATTRIBUTE_NO_UNIQUE_ADDRESS
 //
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/casts.h b/absl/base/casts.h
index d195888..e0b11bb 100644
--- a/absl/base/casts.h
+++ b/absl/base/casts.h
@@ -90,7 +90,7 @@
 //
 // Such implicit cast chaining may be useful within template logic.
 template <typename To>
-constexpr To implicit_cast(typename absl::internal::identity_t<To> to) {
+constexpr To implicit_cast(typename absl::internal::type_identity_t<To> to) {
   return to;
 }
 
diff --git a/absl/base/config.h b/absl/base/config.h
index 1de7993..c9165ac 100644
--- a/absl/base/config.h
+++ b/absl/base/config.h
@@ -75,6 +75,12 @@
 #define ABSL_INTERNAL_CPLUSPLUS_LANG __cplusplus
 #endif
 
+#if defined(ABSL_INTERNAL_CPLUSPLUS_LANG) && \
+    ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
+// Include library feature test macros.
+#include <version>
+#endif
+
 #if defined(__APPLE__)
 // Included for TARGET_OS_IPHONE, __IPHONE_OS_VERSION_MIN_REQUIRED,
 // __IPHONE_8_0.
@@ -111,8 +117,8 @@
 //
 // LTS releases can be obtained from
 // https://github.com/abseil/abseil-cpp/releases.
-#define ABSL_LTS_RELEASE_VERSION 20230802
-#define ABSL_LTS_RELEASE_PATCH_LEVEL 0
+#define ABSL_LTS_RELEASE_VERSION 20240116
+#define ABSL_LTS_RELEASE_PATCH_LEVEL 1
 
 // Helper macro to convert a CPP variable to a string literal.
 #define ABSL_INTERNAL_DO_TOKEN_STR(x) #x
@@ -332,8 +338,8 @@
 #ifdef ABSL_HAVE_INTRINSIC_INT128
 #error ABSL_HAVE_INTRINSIC_INT128 cannot be directly set
 #elif defined(__SIZEOF_INT128__)
-#if (defined(__clang__) && !defined(_WIN32)) || \
-    (defined(__CUDACC__) && __CUDACC_VER_MAJOR__ >= 9) ||                \
+#if (defined(__clang__) && !defined(_WIN32)) ||           \
+    (defined(__CUDACC__) && __CUDACC_VER_MAJOR__ >= 9) || \
     (defined(__GNUC__) && !defined(__clang__) && !defined(__CUDACC__))
 #define ABSL_HAVE_INTRINSIC_INT128 1
 #elif defined(__CUDACC__)
@@ -395,7 +401,7 @@
 //   Windows                           _WIN32
 //   NaCL                              __native_client__
 //   AsmJS                             __asmjs__
-//   WebAssembly                       __wasm__
+//   WebAssembly (Emscripten)          __EMSCRIPTEN__
 //   Fuchsia                           __Fuchsia__
 //
 // Note that since Android defines both __ANDROID__ and __linux__, one
@@ -407,11 +413,11 @@
 // POSIX.1-2001.
 #ifdef ABSL_HAVE_MMAP
 #error ABSL_HAVE_MMAP cannot be directly set
-#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \
-    defined(_AIX) || defined(__ros__) || defined(__native_client__) ||    \
-    defined(__asmjs__) || defined(__wasm__) || defined(__Fuchsia__) ||    \
-    defined(__sun) || defined(__ASYLO__) || defined(__myriad2__) ||       \
-    defined(__HAIKU__) || defined(__OpenBSD__) || defined(__NetBSD__) ||  \
+#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) ||    \
+    defined(_AIX) || defined(__ros__) || defined(__native_client__) ||       \
+    defined(__asmjs__) || defined(__EMSCRIPTEN__) || defined(__Fuchsia__) || \
+    defined(__sun) || defined(__ASYLO__) || defined(__myriad2__) ||          \
+    defined(__HAIKU__) || defined(__OpenBSD__) || defined(__NetBSD__) ||     \
     defined(__QNX__) || defined(__VXWORKS__) || defined(__hexagon__)
 #define ABSL_HAVE_MMAP 1
 #endif
@@ -484,6 +490,8 @@
 // https://sourceforge.net/p/mingw-w64/mingw-w64/ci/master/tree/mingw-w64-crt/misc/alarm.c
 #elif defined(__EMSCRIPTEN__)
 // emscripten doesn't support signals
+#elif defined(__wasi__)
+// WASI doesn't support signals
 #elif defined(__Fuchsia__)
 // Signals don't exist on fuchsia.
 #elif defined(__native_client__)
@@ -536,14 +544,14 @@
 // and
 // https://github.com/llvm/llvm-project/commit/0bc451e7e137c4ccadcd3377250874f641ca514a
 // The second has the actually correct versions, thus, is what we copy here.
-#if defined(__APPLE__) &&                                           \
-    ((defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) &&     \
-      __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 101300) ||    \
-     (defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__) &&    \
-      __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ < 120000) ||   \
-     (defined(__ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__) &&     \
-      __ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__ < 50000) ||     \
-     (defined(__ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__) &&        \
+#if defined(__APPLE__) &&                                         \
+    ((defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) &&   \
+      __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 101300) ||  \
+     (defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__) &&  \
+      __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ < 120000) || \
+     (defined(__ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__) &&   \
+      __ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__ < 50000) ||   \
+     (defined(__ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__) &&      \
       __ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__ < 120000))
 #define ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE 1
 #else
@@ -555,6 +563,8 @@
 // Checks whether C++17 std::any is available.
 #ifdef ABSL_HAVE_STD_ANY
 #error "ABSL_HAVE_STD_ANY cannot be directly set."
+#elif defined(__cpp_lib_any) && __cpp_lib_any >= 201606L
+#define ABSL_HAVE_STD_ANY 1
 #elif defined(ABSL_INTERNAL_CPLUSPLUS_LANG) && \
     ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L && \
     !ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE
@@ -566,7 +576,9 @@
 // Checks whether C++17 std::optional is available.
 #ifdef ABSL_HAVE_STD_OPTIONAL
 #error "ABSL_HAVE_STD_OPTIONAL cannot be directly set."
-#elif defined(ABSL_INTERNAL_CPLUSPLUS_LANG) &&  \
+#elif defined(__cpp_lib_optional) && __cpp_lib_optional >= 202106L
+#define ABSL_HAVE_STD_OPTIONAL 1
+#elif defined(ABSL_INTERNAL_CPLUSPLUS_LANG) && \
     ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L && \
     !ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE
 #define ABSL_HAVE_STD_OPTIONAL 1
@@ -577,6 +589,8 @@
 // Checks whether C++17 std::variant is available.
 #ifdef ABSL_HAVE_STD_VARIANT
 #error "ABSL_HAVE_STD_VARIANT cannot be directly set."
+#elif defined(__cpp_lib_variant) && __cpp_lib_variant >= 201606L
+#define ABSL_HAVE_STD_VARIANT 1
 #elif defined(ABSL_INTERNAL_CPLUSPLUS_LANG) && \
     ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L && \
     !ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE
@@ -588,11 +602,29 @@
 // Checks whether C++17 std::string_view is available.
 #ifdef ABSL_HAVE_STD_STRING_VIEW
 #error "ABSL_HAVE_STD_STRING_VIEW cannot be directly set."
+#elif defined(__cpp_lib_string_view) && __cpp_lib_string_view >= 201606L
+#define ABSL_HAVE_STD_STRING_VIEW 1
 #elif defined(ABSL_INTERNAL_CPLUSPLUS_LANG) && \
     ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L
 #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.
@@ -655,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
@@ -837,11 +885,30 @@
 // RTTI support.
 #ifdef ABSL_INTERNAL_HAS_RTTI
 #error ABSL_INTERNAL_HAS_RTTI cannot be directly set
-#elif (defined(__GNUC__) && defined(__GXX_RTTI)) || \
-    (defined(_MSC_VER) && defined(_CPPRTTI)) ||     \
-    (!defined(__GNUC__) && !defined(_MSC_VER))
+#elif ABSL_HAVE_FEATURE(cxx_rtti)
 #define ABSL_INTERNAL_HAS_RTTI 1
-#endif  // !defined(__GNUC__) || defined(__GXX_RTTI)
+#elif defined(__GNUC__) && defined(__GXX_RTTI)
+#define ABSL_INTERNAL_HAS_RTTI 1
+#elif defined(_MSC_VER) && defined(_CPPRTTI)
+#define ABSL_INTERNAL_HAS_RTTI 1
+#elif !defined(__GNUC__) && !defined(_MSC_VER)
+// Unknown compiler, default to RTTI
+#define ABSL_INTERNAL_HAS_RTTI 1
+#endif
+
+// `ABSL_INTERNAL_HAS_CXA_DEMANGLE` determines whether `abi::__cxa_demangle` is
+// available.
+#ifdef ABSL_INTERNAL_HAS_CXA_DEMANGLE
+#error ABSL_INTERNAL_HAS_CXA_DEMANGLE cannot be directly set
+#elif defined(OS_ANDROID) && (defined(__i386__) || defined(__x86_64__))
+#define ABSL_INTERNAL_HAS_CXA_DEMANGLE 0
+#elif defined(__GNUC__) && defined(__GNUC_MINOR__) &&            \
+    (__GNUC__ >= 4 || (__GNUC__ >= 3 && __GNUC_MINOR__ >= 4)) && \
+    !defined(__mips__)
+#define ABSL_INTERNAL_HAS_CXA_DEMANGLE 1
+#elif defined(__clang__) && !defined(_MSC_VER)
+#define ABSL_INTERNAL_HAS_CXA_DEMANGLE 1
+#endif
 
 // ABSL_INTERNAL_HAVE_SSE is used for compile-time detection of SSE support.
 // See https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html for an overview of
@@ -928,8 +995,8 @@
 #if __EMSCRIPTEN_tiny__ >= 1000
 #error __EMSCRIPTEN_tiny__ is too big to fit in ABSL_INTERNAL_EMSCRIPTEN_VERSION
 #endif
-#define ABSL_INTERNAL_EMSCRIPTEN_VERSION                          \
-  ((__EMSCRIPTEN_major__)*1000000 + (__EMSCRIPTEN_minor__)*1000 + \
+#define ABSL_INTERNAL_EMSCRIPTEN_VERSION                              \
+  ((__EMSCRIPTEN_major__) * 1000000 + (__EMSCRIPTEN_minor__) * 1000 + \
    (__EMSCRIPTEN_tiny__))
 #endif
 #endif
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/identity.h b/absl/base/internal/identity.h
index a3154ed..365207b 100644
--- a/absl/base/internal/identity.h
+++ b/absl/base/internal/identity.h
@@ -22,13 +22,15 @@
 ABSL_NAMESPACE_BEGIN
 namespace internal {
 
+// This is a back-fill of C++20's `std::type_identity`.
 template <typename T>
-struct identity {
+struct type_identity {
   typedef T type;
 };
 
+// This is a back-fill of C++20's `std::type_identity_t`.
 template <typename T>
-using identity_t = typename identity<T>::type;
+using type_identity_t = typename type_identity<T>::type;
 
 }  // namespace internal
 ABSL_NAMESPACE_END
diff --git a/absl/base/internal/inline_variable.h b/absl/base/internal/inline_variable.h
index df933fa..09daf0f 100644
--- a/absl/base/internal/inline_variable.h
+++ b/absl/base/internal/inline_variable.h
@@ -63,12 +63,12 @@
 // Bug: https://bugs.llvm.org/show_bug.cgi?id=35862
 //
 // Note:
-//   identity_t is used here so that the const and name are in the
+//   type_identity_t is used here so that the const and name are in the
 //   appropriate place for pointer types, reference types, function pointer
 //   types, etc..
 #if defined(__clang__)
 #define ABSL_INTERNAL_EXTERN_DECL(type, name) \
-  extern const ::absl::internal::identity_t<type> name;
+  extern const ::absl::internal::type_identity_t<type> name;
 #else  // Otherwise, just define the macro to do nothing.
 #define ABSL_INTERNAL_EXTERN_DECL(type, name)
 #endif  // defined(__clang__)
@@ -76,30 +76,31 @@
 // See above comment at top of file for details.
 #define ABSL_INTERNAL_INLINE_CONSTEXPR(type, name, init) \
   ABSL_INTERNAL_EXTERN_DECL(type, name)                  \
-  inline constexpr ::absl::internal::identity_t<type> name = init
+  inline constexpr ::absl::internal::type_identity_t<type> name = init
 
 #else
 
 // See above comment at top of file for details.
 //
 // Note:
-//   identity_t is used here so that the const and name are in the
+//   type_identity_t is used here so that the const and name are in the
 //   appropriate place for pointer types, reference types, function pointer
 //   types, etc..
-#define ABSL_INTERNAL_INLINE_CONSTEXPR(var_type, name, init)                  \
-  template <class /*AbslInternalDummy*/ = void>                               \
-  struct AbslInternalInlineVariableHolder##name {                             \
-    static constexpr ::absl::internal::identity_t<var_type> kInstance = init; \
-  };                                                                          \
-                                                                              \
-  template <class AbslInternalDummy>                                          \
-  constexpr ::absl::internal::identity_t<var_type>                            \
-      AbslInternalInlineVariableHolder##name<AbslInternalDummy>::kInstance;   \
-                                                                              \
-  static constexpr const ::absl::internal::identity_t<var_type>&              \
-      name = /* NOLINT */                                                     \
-      AbslInternalInlineVariableHolder##name<>::kInstance;                    \
-  static_assert(sizeof(void (*)(decltype(name))) != 0,                        \
+#define ABSL_INTERNAL_INLINE_CONSTEXPR(var_type, name, init)                 \
+  template <class /*AbslInternalDummy*/ = void>                              \
+  struct AbslInternalInlineVariableHolder##name {                            \
+    static constexpr ::absl::internal::type_identity_t<var_type> kInstance = \
+        init;                                                                \
+  };                                                                         \
+                                                                             \
+  template <class AbslInternalDummy>                                         \
+  constexpr ::absl::internal::type_identity_t<var_type>                      \
+      AbslInternalInlineVariableHolder##name<AbslInternalDummy>::kInstance;  \
+                                                                             \
+  static constexpr const ::absl::internal::type_identity_t<var_type>&        \
+      name = /* NOLINT */                                                    \
+      AbslInternalInlineVariableHolder##name<>::kInstance;                   \
+  static_assert(sizeof(void (*)(decltype(name))) != 0,                       \
                 "Silence unused variable warnings.")
 
 #endif  // __cpp_inline_variables
diff --git a/absl/base/internal/low_level_alloc.cc b/absl/base/internal/low_level_alloc.cc
index 6d2cfea..a563f7b 100644
--- a/absl/base/internal/low_level_alloc.cc
+++ b/absl/base/internal/low_level_alloc.cc
@@ -329,7 +329,7 @@
   SYSTEM_INFO system_info;
   GetSystemInfo(&system_info);
   return std::max(system_info.dwPageSize, system_info.dwAllocationGranularity);
-#elif defined(__wasm__) || defined(__asmjs__)
+#elif defined(__wasm__) || defined(__asmjs__) || defined(__hexagon__)
   return getpagesize();
 #else
   return static_cast<size_t>(sysconf(_SC_PAGESIZE));
diff --git a/absl/base/internal/prefetch.h b/absl/base/internal/prefetch.h
deleted file mode 100644
index aecfd87..0000000
--- a/absl/base/internal/prefetch.h
+++ /dev/null
@@ -1,137 +0,0 @@
-// 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.
-
-// TODO(b/265984188): remove all uses and delete this header.
-
-#ifndef ABSL_BASE_INTERNAL_PREFETCH_H_
-#define ABSL_BASE_INTERNAL_PREFETCH_H_
-
-#include "absl/base/attributes.h"
-#include "absl/base/config.h"
-#include "absl/base/prefetch.h"
-
-#ifdef __SSE__
-#include <xmmintrin.h>
-#endif
-
-#if defined(_MSC_VER) && defined(ABSL_INTERNAL_HAVE_SSE)
-#include <intrin.h>
-#pragma intrinsic(_mm_prefetch)
-#endif
-
-// Compatibility wrappers around __builtin_prefetch, to prefetch data
-// for read if supported by the toolchain.
-
-// Move data into the cache before it is read, or "prefetch" it.
-//
-// The value of `addr` is the address of the memory to prefetch. If
-// the target and compiler support it, data prefetch instructions are
-// generated. If the prefetch is done some time before the memory is
-// read, it may be in the cache by the time the read occurs.
-//
-// The function names specify the temporal locality heuristic applied,
-// using the names of Intel prefetch instructions:
-//
-//   T0 - high degree of temporal locality; data should be left in as
-//        many levels of the cache possible
-//   T1 - moderate degree of temporal locality
-//   T2 - low degree of temporal locality
-//   Nta - no temporal locality, data need not be left in the cache
-//         after the read
-//
-// Incorrect or gratuitous use of these functions can degrade
-// performance, so use them only when representative benchmarks show
-// an improvement.
-//
-// Example usage:
-//
-//   absl::base_internal::PrefetchT0(addr);
-//
-// Currently, the different prefetch calls behave on some Intel
-// architectures as follows:
-//
-//                 SNB..SKL   SKX
-// PrefetchT0()   L1/L2/L3  L1/L2
-// PrefetchT1()      L2/L3     L2
-// PrefetchT2()      L2/L3     L2
-// PrefetchNta()  L1/--/L3  L1*
-//
-// * On SKX PrefetchNta() will bring the line into L1 but will evict
-//   from L3 cache. This might result in surprising behavior.
-//
-// SNB = Sandy Bridge, SKL = Skylake, SKX = Skylake Xeon.
-//
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace base_internal {
-
-ABSL_DEPRECATED("Use absl::PrefetchToLocalCache() instead")
-inline void PrefetchT0(const void* address) {
-  absl::PrefetchToLocalCache(address);
-}
-
-ABSL_DEPRECATED("Use absl::PrefetchToLocalCache() instead")
-inline void PrefetchNta(const void* address) {
-  absl::PrefetchToLocalCacheNta(address);
-}
-
-ABSL_DEPRECATED("Use __builtin_prefetch() for advanced prefetch logic instead")
-void PrefetchT1(const void* addr);
-
-ABSL_DEPRECATED("Use __builtin_prefetch() for advanced prefetch logic instead")
-void PrefetchT2(const void* addr);
-
-// Implementation details follow.
-
-#if ABSL_HAVE_BUILTIN(__builtin_prefetch) || defined(__GNUC__)
-
-#define ABSL_INTERNAL_HAVE_PREFETCH 1
-
-// See __builtin_prefetch:
-// https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html.
-//
-// These functions speculatively load for read only. This is
-// safe for all currently supported platforms. However, prefetch for
-// store may have problems depending on the target platform.
-//
-inline void PrefetchT1(const void* addr) {
-  // Note: this uses prefetcht1 on Intel.
-  __builtin_prefetch(addr, 0, 2);
-}
-inline void PrefetchT2(const void* addr) {
-  // Note: this uses prefetcht2 on Intel.
-  __builtin_prefetch(addr, 0, 1);
-}
-
-#elif defined(ABSL_INTERNAL_HAVE_SSE)
-
-#define ABSL_INTERNAL_HAVE_PREFETCH 1
-
-inline void PrefetchT1(const void* addr) {
-  _mm_prefetch(reinterpret_cast<const char*>(addr), _MM_HINT_T1);
-}
-inline void PrefetchT2(const void* addr) {
-  _mm_prefetch(reinterpret_cast<const char*>(addr), _MM_HINT_T2);
-}
-
-#else
-inline void PrefetchT1(const void*) {}
-inline void PrefetchT2(const void*) {}
-#endif
-
-}  // namespace base_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_BASE_INTERNAL_PREFETCH_H_
diff --git a/absl/base/internal/prefetch_test.cc b/absl/base/internal/prefetch_test.cc
deleted file mode 100644
index 7c1dae4..0000000
--- a/absl/base/internal/prefetch_test.cc
+++ /dev/null
@@ -1,43 +0,0 @@
-// 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/base/internal/prefetch.h"
-
-#include "gtest/gtest.h"
-
-namespace {
-
-int number = 42;
-
-TEST(Prefetch, TemporalLocalityNone) {
-  absl::base_internal::PrefetchNta(&number);
-  EXPECT_EQ(number, 42);
-}
-
-TEST(Prefetch, TemporalLocalityLow) {
-  absl::base_internal::PrefetchT2(&number);
-  EXPECT_EQ(number, 42);
-}
-
-TEST(Prefetch, TemporalLocalityMedium) {
-  absl::base_internal::PrefetchT1(&number);
-  EXPECT_EQ(number, 42);
-}
-
-TEST(Prefetch, TemporalLocalityHigh) {
-  absl::base_internal::PrefetchT0(&number);
-  EXPECT_EQ(number, 42);
-}
-
-}  // namespace
diff --git a/absl/base/internal/raw_logging.cc b/absl/base/internal/raw_logging.cc
index 4c922cc..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>
 
@@ -56,8 +57,7 @@
 // ABSL_HAVE_SYSCALL_WRITE is defined when the platform provides the syscall
 //   syscall(SYS_write, /*int*/ fd, /*char* */ buf, /*size_t*/ len);
 // for low level operations that want to avoid libc.
-#if (defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__)) && \
-    !defined(__ANDROID__)
+#if (defined(__linux__) || defined(__FreeBSD__)) && !defined(__ANDROID__)
 #include <sys/syscall.h>
 #define ABSL_HAVE_SYSCALL_WRITE 1
 #define ABSL_LOW_LEVEL_WRITE_SUPPORTED 1
@@ -93,8 +93,7 @@
 bool VADoRawLog(char** buf, int* size, const char* format, va_list ap)
     ABSL_PRINTF_ATTRIBUTE(3, 0);
 bool VADoRawLog(char** buf, int* size, const char* format, va_list ap) {
-  if (*size < 0)
-    return false;
+  if (*size < 0) return false;
   int n = vsnprintf(*buf, static_cast<size_t>(*size), format, ap);
   bool result = true;
   if (n < 0 || n > *size) {
@@ -122,8 +121,7 @@
 bool DoRawLog(char** buf, int* size, const char* format, ...)
     ABSL_PRINTF_ATTRIBUTE(3, 4);
 bool DoRawLog(char** buf, int* size, const char* format, ...) {
-  if (*size < 0)
-    return false;
+  if (*size < 0) return false;
   va_list ap;
   va_start(ap, format);
   int n = vsnprintf(*buf, static_cast<size_t>(*size), format, ap);
@@ -242,8 +240,8 @@
   _write(/* stderr */ 2, s, static_cast<unsigned>(len));
 #else
   // stderr logging unsupported on this platform
-  (void) s;
-  (void) len;
+  (void)s;
+  (void)len;
 #endif
 }
 
@@ -258,7 +256,7 @@
 bool RawLoggingFullySupported() {
 #ifdef ABSL_LOW_LEVEL_WRITE_SUPPORTED
   return true;
-#else  // !ABSL_LOW_LEVEL_WRITE_SUPPORTED
+#else   // !ABSL_LOW_LEVEL_WRITE_SUPPORTED
   return false;
 #endif  // !ABSL_LOW_LEVEL_WRITE_SUPPORTED
 }
diff --git a/absl/base/internal/raw_logging.h b/absl/base/internal/raw_logging.h
index b79550b..d7cfbc5 100644
--- a/absl/base/internal/raw_logging.h
+++ b/absl/base/internal/raw_logging.h
@@ -108,6 +108,7 @@
 #define ABSL_RAW_LOG_INTERNAL_WARNING ::absl::LogSeverity::kWarning
 #define ABSL_RAW_LOG_INTERNAL_ERROR ::absl::LogSeverity::kError
 #define ABSL_RAW_LOG_INTERNAL_FATAL ::absl::LogSeverity::kFatal
+#define ABSL_RAW_LOG_INTERNAL_DFATAL ::absl::kLogDebugFatal
 #define ABSL_RAW_LOG_INTERNAL_LEVEL(severity) \
   ::absl::NormalizeLogSeverity(severity)
 
@@ -115,6 +116,7 @@
 #define ABSL_RAW_LOG_INTERNAL_MAYBE_UNREACHABLE_WARNING
 #define ABSL_RAW_LOG_INTERNAL_MAYBE_UNREACHABLE_ERROR
 #define ABSL_RAW_LOG_INTERNAL_MAYBE_UNREACHABLE_FATAL ABSL_UNREACHABLE()
+#define ABSL_RAW_LOG_INTERNAL_MAYBE_UNREACHABLE_DFATAL
 #define ABSL_RAW_LOG_INTERNAL_MAYBE_UNREACHABLE_LEVEL(severity)
 
 namespace absl {
diff --git a/absl/base/internal/spinlock.h b/absl/base/internal/spinlock.h
index 09ba582..2929cd6 100644
--- a/absl/base/internal/spinlock.h
+++ b/absl/base/internal/spinlock.h
@@ -19,10 +19,10 @@
 //   - for use by Abseil internal code that Mutex itself depends on
 //   - for async signal safety (see below)
 
-// SpinLock is async signal safe.  If a spinlock is used within a signal
-// handler, all code that acquires the lock must ensure that the signal cannot
-// arrive while they are holding the lock.  Typically, this is done by blocking
-// the signal.
+// SpinLock with a base_internal::SchedulingMode::SCHEDULE_KERNEL_ONLY is async
+// signal safe. If a spinlock is used within a signal handler, all code that
+// acquires the lock must ensure that the signal cannot arrive while they are
+// holding the lock. Typically, this is done by blocking the signal.
 //
 // Threads waiting on a SpinLock may be woken in an arbitrary order.
 
@@ -41,6 +41,14 @@
 #include "absl/base/internal/tsan_mutex_interface.h"
 #include "absl/base/thread_annotations.h"
 
+namespace tcmalloc {
+namespace tcmalloc_internal {
+
+class AllocationGuardSpinLockHolder;
+
+}  // namespace tcmalloc_internal
+}  // namespace tcmalloc
+
 namespace absl {
 ABSL_NAMESPACE_BEGIN
 namespace base_internal {
@@ -137,6 +145,7 @@
 
   // Provide access to protected method above.  Use for testing only.
   friend struct SpinLockTest;
+  friend class tcmalloc::tcmalloc_internal::AllocationGuardSpinLockHolder;
 
  private:
   // lockword_ is used to store the following:
@@ -171,6 +180,10 @@
     return scheduling_mode == base_internal::SCHEDULE_COOPERATIVE_AND_KERNEL;
   }
 
+  bool IsCooperative() const {
+    return lockword_.load(std::memory_order_relaxed) & kSpinLockCooperative;
+  }
+
   uint32_t TryLockInternal(uint32_t lock_value, uint32_t wait_cycles);
   void SlowLock() ABSL_ATTRIBUTE_COLD;
   void SlowUnlock(uint32_t lock_value) ABSL_ATTRIBUTE_COLD;
diff --git a/absl/base/internal/spinlock_benchmark.cc b/absl/base/internal/spinlock_benchmark.cc
index 0451c65..1790d96 100644
--- a/absl/base/internal/spinlock_benchmark.cc
+++ b/absl/base/internal/spinlock_benchmark.cc
@@ -18,22 +18,39 @@
 #include "absl/base/internal/raw_logging.h"
 #include "absl/base/internal/scheduling_mode.h"
 #include "absl/base/internal/spinlock.h"
+#include "absl/base/no_destructor.h"
 #include "absl/synchronization/internal/create_thread_identity.h"
 #include "benchmark/benchmark.h"
 
 namespace {
 
 template <absl::base_internal::SchedulingMode scheduling_mode>
-static void BM_SpinLock(benchmark::State& state) {
-  // Ensure a ThreadIdentity is installed.
+static void BM_TryLock(benchmark::State& state) {
+  // Ensure a ThreadIdentity is installed so that KERNEL_ONLY has an effect.
   ABSL_INTERNAL_CHECK(
       absl::synchronization_internal::GetOrCreateCurrentThreadIdentity() !=
           nullptr,
       "GetOrCreateCurrentThreadIdentity() failed");
 
-  static auto* spinlock = new absl::base_internal::SpinLock(scheduling_mode);
+  static absl::NoDestructor<absl::base_internal::SpinLock> spinlock(
+      scheduling_mode);
   for (auto _ : state) {
-    absl::base_internal::SpinLockHolder holder(spinlock);
+    if (spinlock->TryLock()) spinlock->Unlock();
+  }
+}
+
+template <absl::base_internal::SchedulingMode scheduling_mode>
+static void BM_SpinLock(benchmark::State& state) {
+  // Ensure a ThreadIdentity is installed so that KERNEL_ONLY has an effect.
+  ABSL_INTERNAL_CHECK(
+      absl::synchronization_internal::GetOrCreateCurrentThreadIdentity() !=
+          nullptr,
+      "GetOrCreateCurrentThreadIdentity() failed");
+
+  static absl::NoDestructor<absl::base_internal::SpinLock> spinlock(
+      scheduling_mode);
+  for (auto _ : state) {
+    absl::base_internal::SpinLockHolder holder(spinlock.get());
   }
 }
 
@@ -49,4 +66,15 @@
     ->Threads(1)
     ->ThreadPerCpu();
 
+BENCHMARK_TEMPLATE(BM_TryLock, absl::base_internal::SCHEDULE_KERNEL_ONLY)
+    ->UseRealTime()
+    ->Threads(1)
+    ->ThreadPerCpu();
+
+BENCHMARK_TEMPLATE(BM_TryLock,
+                   absl::base_internal::SCHEDULE_COOPERATIVE_AND_KERNEL)
+    ->UseRealTime()
+    ->Threads(1)
+    ->ThreadPerCpu();
+
 }  // namespace
diff --git a/absl/base/internal/sysinfo.cc b/absl/base/internal/sysinfo.cc
index 8bcc4fa..79eaba3 100644
--- a/absl/base/internal/sysinfo.cc
+++ b/absl/base/internal/sysinfo.cc
@@ -34,6 +34,14 @@
 #include <sys/sysctl.h>
 #endif
 
+#ifdef __FreeBSD__
+#include <pthread_np.h>
+#endif
+
+#ifdef __NetBSD__
+#include <lwp.h>
+#endif
+
 #if defined(__myriad2__)
 #include <rtems.h>
 #endif
@@ -432,6 +440,18 @@
   return static_cast<pid_t>(tid);
 }
 
+#elif defined(__FreeBSD__)
+
+pid_t GetTID() { return static_cast<pid_t>(pthread_getthreadid_np()); }
+
+#elif defined(__OpenBSD__)
+
+pid_t GetTID() { return getthrid(); }
+
+#elif defined(__NetBSD__)
+
+pid_t GetTID() { return static_cast<pid_t>(_lwp_self()); }
+
 #elif defined(__native_client__)
 
 pid_t GetTID() {
diff --git a/absl/base/internal/thread_annotations.h b/absl/base/internal/thread_annotations.h
deleted file mode 100644
index 8c5c67e..0000000
--- a/absl/base/internal/thread_annotations.h
+++ /dev/null
@@ -1,280 +0,0 @@
-// Copyright 2019 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: thread_annotations.h
-// -----------------------------------------------------------------------------
-//
-// WARNING: This is a backwards compatible header and it will be removed after
-// the migration to prefixed thread annotations is finished; please include
-// "absl/base/thread_annotations.h".
-//
-// This header file contains macro definitions for thread safety annotations
-// that allow developers to document the locking policies of multi-threaded
-// code. The annotations can also help program analysis tools to identify
-// potential thread safety issues.
-//
-// These annotations are implemented using compiler attributes. Using the macros
-// defined here instead of raw attributes allow for portability and future
-// compatibility.
-//
-// When referring to mutexes in the arguments of the attributes, you should
-// use variable names or more complex expressions (e.g. my_object->mutex_)
-// that evaluate to a concrete mutex object whenever possible. If the mutex
-// you want to refer to is not in scope, you may use a member pointer
-// (e.g. &MyClass::mutex_) to refer to a mutex in some (unknown) object.
-
-#ifndef ABSL_BASE_INTERNAL_THREAD_ANNOTATIONS_H_
-#define ABSL_BASE_INTERNAL_THREAD_ANNOTATIONS_H_
-
-// ABSL_LEGACY_THREAD_ANNOTATIONS is a *temporary* compatibility macro that can
-// be defined on the compile command-line to restore the legacy spellings of the
-// thread annotations macros/functions. The macros in this file are available
-// under ABSL_ prefixed spellings in absl/base/thread_annotations.h. This macro
-// and the legacy spellings will be removed in the future.
-#ifdef ABSL_LEGACY_THREAD_ANNOTATIONS
-
-#if defined(__clang__)
-#define THREAD_ANNOTATION_ATTRIBUTE__(x)   __attribute__((x))
-#else
-#define THREAD_ANNOTATION_ATTRIBUTE__(x)   // no-op
-#endif
-
-// GUARDED_BY()
-//
-// Documents if a shared field or global variable needs to be protected by a
-// mutex. GUARDED_BY() allows the user to specify a particular mutex that
-// should be held when accessing the annotated variable.
-//
-// Although this annotation (and PT_GUARDED_BY, below) cannot be applied to
-// local variables, a local variable and its associated mutex can often be
-// combined into a small class or struct, thereby allowing the annotation.
-//
-// Example:
-//
-//   class Foo {
-//     Mutex mu_;
-//     int p1_ GUARDED_BY(mu_);
-//     ...
-//   };
-#define GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x))
-
-// PT_GUARDED_BY()
-//
-// Documents if the memory location pointed to by a pointer should be guarded
-// by a mutex when dereferencing the pointer.
-//
-// Example:
-//   class Foo {
-//     Mutex mu_;
-//     int *p1_ PT_GUARDED_BY(mu_);
-//     ...
-//   };
-//
-// Note that a pointer variable to a shared memory location could itself be a
-// shared variable.
-//
-// Example:
-//
-//   // `q_`, guarded by `mu1_`, points to a shared memory location that is
-//   // guarded by `mu2_`:
-//   int *q_ GUARDED_BY(mu1_) PT_GUARDED_BY(mu2_);
-#define PT_GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x))
-
-// ACQUIRED_AFTER() / ACQUIRED_BEFORE()
-//
-// Documents the acquisition order between locks that can be held
-// simultaneously by a thread. For any two locks that need to be annotated
-// to establish an acquisition order, only one of them needs the annotation.
-// (i.e. You don't have to annotate both locks with both ACQUIRED_AFTER
-// and ACQUIRED_BEFORE.)
-//
-// As with GUARDED_BY, this is only applicable to mutexes that are shared
-// fields or global variables.
-//
-// Example:
-//
-//   Mutex m1_;
-//   Mutex m2_ ACQUIRED_AFTER(m1_);
-#define ACQUIRED_AFTER(...) \
-  THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(__VA_ARGS__))
-
-#define ACQUIRED_BEFORE(...) \
-  THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(__VA_ARGS__))
-
-// EXCLUSIVE_LOCKS_REQUIRED() / SHARED_LOCKS_REQUIRED()
-//
-// Documents a function that expects a mutex to be held prior to entry.
-// The mutex is expected to be held both on entry to, and exit from, the
-// function.
-//
-// An exclusive lock allows read-write access to the guarded data member(s), and
-// only one thread can acquire a lock exclusively at any one time. A shared lock
-// allows read-only access, and any number of threads can acquire a shared lock
-// concurrently.
-//
-// Generally, non-const methods should be annotated with
-// EXCLUSIVE_LOCKS_REQUIRED, while const methods should be annotated with
-// SHARED_LOCKS_REQUIRED.
-//
-// Example:
-//
-//   Mutex mu1, mu2;
-//   int a GUARDED_BY(mu1);
-//   int b GUARDED_BY(mu2);
-//
-//   void foo() EXCLUSIVE_LOCKS_REQUIRED(mu1, mu2) { ... }
-//   void bar() const SHARED_LOCKS_REQUIRED(mu1, mu2) { ... }
-#define EXCLUSIVE_LOCKS_REQUIRED(...) \
-  THREAD_ANNOTATION_ATTRIBUTE__(exclusive_locks_required(__VA_ARGS__))
-
-#define SHARED_LOCKS_REQUIRED(...) \
-  THREAD_ANNOTATION_ATTRIBUTE__(shared_locks_required(__VA_ARGS__))
-
-// LOCKS_EXCLUDED()
-//
-// Documents the locks acquired in the body of the function. These locks
-// cannot be held when calling this function (as Abseil's `Mutex` locks are
-// non-reentrant).
-#define LOCKS_EXCLUDED(...) \
-  THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__))
-
-// LOCK_RETURNED()
-//
-// Documents a function that returns a mutex without acquiring it.  For example,
-// a public getter method that returns a pointer to a private mutex should
-// be annotated with LOCK_RETURNED.
-#define LOCK_RETURNED(x) \
-  THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x))
-
-// LOCKABLE
-//
-// Documents if a class/type is a lockable type (such as the `Mutex` class).
-#define LOCKABLE \
-  THREAD_ANNOTATION_ATTRIBUTE__(lockable)
-
-// SCOPED_LOCKABLE
-//
-// Documents if a class does RAII locking (such as the `MutexLock` class).
-// The constructor should use `LOCK_FUNCTION()` to specify the mutex that is
-// acquired, and the destructor should use `UNLOCK_FUNCTION()` with no
-// arguments; the analysis will assume that the destructor unlocks whatever the
-// constructor locked.
-#define SCOPED_LOCKABLE \
-  THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable)
-
-// EXCLUSIVE_LOCK_FUNCTION()
-//
-// Documents functions that acquire a lock in the body of a function, and do
-// not release it.
-#define EXCLUSIVE_LOCK_FUNCTION(...) \
-  THREAD_ANNOTATION_ATTRIBUTE__(exclusive_lock_function(__VA_ARGS__))
-
-// SHARED_LOCK_FUNCTION()
-//
-// Documents functions that acquire a shared (reader) lock in the body of a
-// function, and do not release it.
-#define SHARED_LOCK_FUNCTION(...) \
-  THREAD_ANNOTATION_ATTRIBUTE__(shared_lock_function(__VA_ARGS__))
-
-// UNLOCK_FUNCTION()
-//
-// Documents functions that expect a lock to be held on entry to the function,
-// and release it in the body of the function.
-#define UNLOCK_FUNCTION(...) \
-  THREAD_ANNOTATION_ATTRIBUTE__(unlock_function(__VA_ARGS__))
-
-// EXCLUSIVE_TRYLOCK_FUNCTION() / SHARED_TRYLOCK_FUNCTION()
-//
-// Documents functions that try to acquire a lock, and return success or failure
-// (or a non-boolean value that can be interpreted as a boolean).
-// The first argument should be `true` for functions that return `true` on
-// success, or `false` for functions that return `false` on success. The second
-// argument specifies the mutex that is locked on success. If unspecified, this
-// mutex is assumed to be `this`.
-#define EXCLUSIVE_TRYLOCK_FUNCTION(...) \
-  THREAD_ANNOTATION_ATTRIBUTE__(exclusive_trylock_function(__VA_ARGS__))
-
-#define SHARED_TRYLOCK_FUNCTION(...) \
-  THREAD_ANNOTATION_ATTRIBUTE__(shared_trylock_function(__VA_ARGS__))
-
-// ASSERT_EXCLUSIVE_LOCK() / ASSERT_SHARED_LOCK()
-//
-// Documents functions that dynamically check to see if a lock is held, and fail
-// if it is not held.
-#define ASSERT_EXCLUSIVE_LOCK(...) \
-  THREAD_ANNOTATION_ATTRIBUTE__(assert_exclusive_lock(__VA_ARGS__))
-
-#define ASSERT_SHARED_LOCK(...) \
-  THREAD_ANNOTATION_ATTRIBUTE__(assert_shared_lock(__VA_ARGS__))
-
-// NO_THREAD_SAFETY_ANALYSIS
-//
-// Turns off thread safety checking within the body of a particular function.
-// This annotation is used to mark functions that are known to be correct, but
-// the locking behavior is more complicated than the analyzer can handle.
-#define NO_THREAD_SAFETY_ANALYSIS \
-  THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis)
-
-//------------------------------------------------------------------------------
-// Tool-Supplied Annotations
-//------------------------------------------------------------------------------
-
-// TS_UNCHECKED should be placed around lock expressions that are not valid
-// C++ syntax, but which are present for documentation purposes.  These
-// annotations will be ignored by the analysis.
-#define TS_UNCHECKED(x) ""
-
-// TS_FIXME is used to mark lock expressions that are not valid C++ syntax.
-// It is used by automated tools to mark and disable invalid expressions.
-// The annotation should either be fixed, or changed to TS_UNCHECKED.
-#define TS_FIXME(x) ""
-
-// Like NO_THREAD_SAFETY_ANALYSIS, this turns off checking within the body of
-// a particular function.  However, this attribute is used to mark functions
-// that are incorrect and need to be fixed.  It is used by automated tools to
-// avoid breaking the build when the analysis is updated.
-// Code owners are expected to eventually fix the routine.
-#define NO_THREAD_SAFETY_ANALYSIS_FIXME  NO_THREAD_SAFETY_ANALYSIS
-
-// Similar to NO_THREAD_SAFETY_ANALYSIS_FIXME, this macro marks a GUARDED_BY
-// annotation that needs to be fixed, because it is producing thread safety
-// warning.  It disables the GUARDED_BY.
-#define GUARDED_BY_FIXME(x)
-
-// Disables warnings for a single read operation.  This can be used to avoid
-// warnings when it is known that the read is not actually involved in a race,
-// but the compiler cannot confirm that.
-#define TS_UNCHECKED_READ(x) thread_safety_analysis::ts_unchecked_read(x)
-
-
-namespace thread_safety_analysis {
-
-// Takes a reference to a guarded data member, and returns an unguarded
-// reference.
-template <typename T>
-inline const T& ts_unchecked_read(const T& v) NO_THREAD_SAFETY_ANALYSIS {
-  return v;
-}
-
-template <typename T>
-inline T& ts_unchecked_read(T& v) NO_THREAD_SAFETY_ANALYSIS {
-  return v;
-}
-
-}  // namespace thread_safety_analysis
-
-#endif  // defined(ABSL_LEGACY_THREAD_ANNOTATIONS)
-
-#endif  // ABSL_BASE_INTERNAL_THREAD_ANNOTATIONS_H_
diff --git a/absl/base/internal/thread_identity.cc b/absl/base/internal/thread_identity.cc
index 252443e..0471a25 100644
--- a/absl/base/internal/thread_identity.cc
+++ b/absl/base/internal/thread_identity.cc
@@ -16,8 +16,12 @@
 
 #if !defined(_WIN32) || defined(__MINGW32__)
 #include <pthread.h>
+#ifndef __wasi__
+// WASI does not provide this header, either way we disable use
+// of signals with it below.
 #include <signal.h>
 #endif
+#endif
 
 #include <atomic>
 #include <cassert>
@@ -80,10 +84,12 @@
   absl::call_once(init_thread_identity_key_once, AllocateThreadIdentityKey,
                   reclaimer);
 
-#if defined(__EMSCRIPTEN__) || defined(__MINGW32__) || defined(__hexagon__)
-  // Emscripten and MinGW pthread implementations does not support signals.
-  // See https://kripken.github.io/emscripten-site/docs/porting/pthreads.html
-  // for more information.
+#if defined(__wasi__) || defined(__EMSCRIPTEN__) || defined(__MINGW32__) || \
+    defined(__hexagon__)
+  // Emscripten, WASI and MinGW pthread implementations does not support
+  // signals. See
+  // https://kripken.github.io/emscripten-site/docs/porting/pthreads.html for
+  // more information.
   pthread_setspecific(thread_identity_pthread_key,
                       reinterpret_cast<void*>(identity));
 #else
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.cc b/absl/base/log_severity.cc
index 60a8fc1..8e7bbbc 100644
--- a/absl/base/log_severity.cc
+++ b/absl/base/log_severity.cc
@@ -17,6 +17,7 @@
 #include <ostream>
 
 #include "absl/base/attributes.h"
+#include "absl/base/config.h"
 
 namespace absl {
 ABSL_NAMESPACE_BEGIN
diff --git a/absl/base/log_severity.h b/absl/base/log_severity.h
index 8bdca38..de9702a 100644
--- a/absl/base/log_severity.h
+++ b/absl/base/log_severity.h
@@ -64,6 +64,8 @@
 //   --my_log_level=info
 //   --my_log_level=0
 //
+// `DFATAL` and `kLogDebugFatal` are similarly accepted.
+//
 // Unparsing a flag produces the same result as `absl::LogSeverityName()` for
 // the standard levels and a base-ten integer otherwise.
 enum class LogSeverity : int {
@@ -82,18 +84,28 @@
            absl::LogSeverity::kError, absl::LogSeverity::kFatal}};
 }
 
+// `absl::kLogDebugFatal` equals `absl::LogSeverity::kFatal` in debug builds
+// (i.e. when `NDEBUG` is not defined) and `absl::LogSeverity::kError`
+// otherwise.  Avoid ODR-using this variable as it has internal linkage and thus
+// distinct storage in different TUs.
+#ifdef NDEBUG
+static constexpr absl::LogSeverity kLogDebugFatal = absl::LogSeverity::kError;
+#else
+static constexpr absl::LogSeverity kLogDebugFatal = absl::LogSeverity::kFatal;
+#endif
+
 // LogSeverityName()
 //
 // 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";
+  }
+  return "UNKNOWN";
 }
 
 // NormalizeLogSeverity()
@@ -101,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/log_severity_test.cc b/absl/base/log_severity_test.cc
index 16091a5..3394ecd 100644
--- a/absl/base/log_severity_test.cc
+++ b/absl/base/log_severity_test.cc
@@ -146,7 +146,12 @@
            std::make_tuple("fatal", absl::LogSeverity::kFatal),
            std::make_tuple("kFatal", absl::LogSeverity::kFatal),
            std::make_tuple("FaTaL", absl::LogSeverity::kFatal),
-           std::make_tuple("KfAtAl", absl::LogSeverity::kFatal)));
+           std::make_tuple("KfAtAl", absl::LogSeverity::kFatal),
+           std::make_tuple("DFATAL", absl::kLogDebugFatal),
+           std::make_tuple("dfatal", absl::kLogDebugFatal),
+           std::make_tuple("kLogDebugFatal", absl::kLogDebugFatal),
+           std::make_tuple("dFaTaL", absl::kLogDebugFatal),
+           std::make_tuple("kLoGdEbUgFaTaL", absl::kLogDebugFatal)));
 TEST_P(ParseFlagFromEnumeratorTest, YieldsExpectedValue) {
   const absl::string_view to_parse = std::get<0>(GetParam());
   const absl::LogSeverity expected = std::get<1>(GetParam());
@@ -158,7 +163,8 @@
 
 using ParseFlagFromGarbageTest = TestWithParam<absl::string_view>;
 INSTANTIATE_TEST_SUITE_P(Instantiation, ParseFlagFromGarbageTest,
-                         Values("", "\0", " ", "garbage", "kkinfo", "I"));
+                         Values("", "\0", " ", "garbage", "kkinfo", "I",
+                                "kDFATAL", "LogDebugFatal", "lOgDeBuGfAtAl"));
 TEST_P(ParseFlagFromGarbageTest, ReturnsError) {
   const absl::string_view to_parse = GetParam();
   absl::LogSeverity value;
diff --git a/absl/base/no_destructor.h b/absl/base/no_destructor.h
new file mode 100644
index 0000000..d4b16a6
--- /dev/null
+++ b/absl/base/no_destructor.h
@@ -0,0 +1,217 @@
+// 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.
+//
+// -----------------------------------------------------------------------------
+// File: no_destructor.h
+// -----------------------------------------------------------------------------
+//
+// This header file defines the absl::NoDestructor<T> wrapper for defining a
+// static type that does not need to be destructed upon program exit. Instead,
+// such an object survives during program exit (and can be safely accessed at
+// any time).
+//
+// Objects of such type, if constructed safely and under the right conditions,
+// provide two main benefits over other alternatives:
+//
+//   * Global objects not normally allowed due to concerns of destruction order
+//     (i.e. no "complex globals") can be safely allowed, provided that such
+//     objects can be constant initialized.
+//   * Function scope static objects can be optimized to avoid heap allocation,
+//     pointer chasing, and allow lazy construction.
+//
+// See below for complete details.
+
+
+#ifndef ABSL_BASE_NO_DESTRUCTOR_H_
+#define ABSL_BASE_NO_DESTRUCTOR_H_
+
+#include <new>
+#include <type_traits>
+#include <utility>
+
+#include "absl/base/config.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+
+// absl::NoDestructor<T>
+//
+// NoDestructor<T> is a wrapper around an object of type T that behaves as an
+// object of type T but never calls T's destructor. NoDestructor<T> makes it
+// safer and/or more efficient to use such objects in static storage contexts:
+// as global or function scope static variables.
+//
+// An instance of absl::NoDestructor<T> has similar type semantics to an
+// instance of T:
+//
+// * Constructs in the same manner as an object of type T through perfect
+//   forwarding.
+// * Provides pointer/reference semantic access to the object of type T via
+//   `->`, `*`, and `get()`.
+//   (Note that `const NoDestructor<T>` works like a pointer to const `T`.)
+//
+// An object of type NoDestructor<T> should be defined in static storage:
+// as either a global static object, or as a function scope static variable.
+//
+// Additionally, NoDestructor<T> provides the following benefits:
+//
+// * Never calls T's destructor for the object
+// * If the object is a function-local static variable, the type can be
+//   lazily constructed.
+//
+// An object of type NoDestructor<T> is "trivially destructible" in the notion
+// that its destructor is never run. Provided that an object of this type can be
+// safely initialized and does not need to be cleaned up on program shutdown,
+// NoDestructor<T> allows you to define global static variables, since Google's
+// C++ style guide ban on such objects doesn't apply to objects that are
+// trivially destructible.
+//
+// Usage as Global Static Variables
+//
+// NoDestructor<T> allows declaration of a global object with a non-trivial
+// constructor in static storage without needing to add a destructor.
+// However, such objects still need to worry about initialization order, so
+// such objects should be const initialized:
+//
+//    // Global or namespace scope.
+//    ABSL_CONST_INIT absl::NoDestructor<MyRegistry> reg{"foo", "bar", 8008};
+//
+// Note that if your object already has a trivial destructor, you don't need to
+// use NoDestructor<T>.
+//
+// Usage as Function Scope Static Variables
+//
+// Function static objects will be lazily initialized within static storage:
+//
+//    // Function scope.
+//    const std::string& MyString() {
+//      static const absl::NoDestructor<std::string> x("foo");
+//      return *x;
+//    }
+//
+// For function static variables, NoDestructor avoids heap allocation and can be
+// inlined in static storage, resulting in exactly-once, thread-safe
+// construction of an object, and very fast access thereafter (the cost is a few
+// extra cycles).
+//
+// Using NoDestructor<T> in this manner is generally better than other patterns
+// which require pointer chasing:
+//
+//   // Prefer using absl::NoDestructor<T> instead for the static variable.
+//   const std::string& MyString() {
+//     static const std::string* x = new std::string("foo");
+//     return *x;
+//   }
+//
+template <typename T>
+class NoDestructor {
+ public:
+  // Forwards arguments to the T's constructor: calls T(args...).
+  template <typename... Ts,
+            // Disable this overload when it might collide with copy/move.
+            typename std::enable_if<!std::is_same<void(std::decay_t<Ts>&...),
+                                                  void(NoDestructor&)>::value,
+                                    int>::type = 0>
+  explicit constexpr NoDestructor(Ts&&... args)
+      : impl_(std::forward<Ts>(args)...) {}
+
+  // Forwards copy and move construction for T. Enables usage like this:
+  //   static NoDestructor<std::array<string, 3>> x{{{"1", "2", "3"}}};
+  //   static NoDestructor<std::vector<int>> x{{1, 2, 3}};
+  explicit constexpr NoDestructor(const T& x) : impl_(x) {}
+  explicit constexpr NoDestructor(T&& x)
+      : impl_(std::move(x)) {}
+
+  // No copying.
+  NoDestructor(const NoDestructor&) = delete;
+  NoDestructor& operator=(const NoDestructor&) = delete;
+
+  // Pretend to be a smart pointer to T with deep constness.
+  // Never returns a null pointer.
+  T& operator*() { return *get(); }
+  T* operator->() { return get(); }
+  T* get() { return impl_.get(); }
+  const T& operator*() const { return *get(); }
+  const T* operator->() const { return get(); }
+  const T* get() const { return impl_.get(); }
+
+ private:
+  class DirectImpl {
+   public:
+    template <typename... Args>
+    explicit constexpr DirectImpl(Args&&... args)
+        : value_(std::forward<Args>(args)...) {}
+    const T* get() const { return &value_; }
+    T* get() { return &value_; }
+
+   private:
+    T value_;
+  };
+
+  class PlacementImpl {
+   public:
+    template <typename... Args>
+    explicit PlacementImpl(Args&&... args) {
+      new (&space_) T(std::forward<Args>(args)...);
+    }
+    const T* get() const {
+      return Launder(reinterpret_cast<const T*>(&space_));
+    }
+    T* get() { return Launder(reinterpret_cast<T*>(&space_)); }
+
+   private:
+    template <typename P>
+    static P* Launder(P* p) {
+#if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606L
+      return std::launder(p);
+#elif ABSL_HAVE_BUILTIN(__builtin_launder)
+      return __builtin_launder(p);
+#else
+      // When `std::launder` or equivalent are not available, we rely on
+      // undefined behavior, which works as intended on Abseil's officially
+      // supported platforms as of Q3 2023.
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wstrict-aliasing"
+#endif
+      return p;
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+#endif
+    }
+
+    alignas(T) unsigned char space_[sizeof(T)];
+  };
+
+  // If the object is trivially destructible we use a member directly to avoid
+  // potential once-init runtime initialization. It somewhat defeats the
+  // purpose of NoDestructor in this case, but this makes the class more
+  // friendly to generic code.
+  std::conditional_t<std::is_trivially_destructible<T>::value, DirectImpl,
+                     PlacementImpl>
+      impl_;
+};
+
+#ifdef ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION
+// Provide 'Class Template Argument Deduction': the type of NoDestructor's T
+// will be the same type as the argument passed to NoDestructor's constructor.
+template <typename T>
+NoDestructor(T) -> NoDestructor<T>;
+#endif  // ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION
+
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif  // ABSL_BASE_NO_DESTRUCTOR_H_
diff --git a/absl/base/no_destructor_benchmark.cc b/absl/base/no_destructor_benchmark.cc
new file mode 100644
index 0000000..5fc88f1
--- /dev/null
+++ b/absl/base/no_destructor_benchmark.cc
@@ -0,0 +1,165 @@
+// 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 <cstdint>
+
+#include "absl/base/internal/raw_logging.h"
+#include "absl/base/no_destructor.h"
+#include "benchmark/benchmark.h"
+
+namespace {
+
+// Number of static-NoDestructor-in-a-function to exercise.
+// This must be low enough not to hit template instantiation limits
+// (happens around 1000).
+constexpr int kNumObjects = 1;  // set to 512 when doing benchmarks
+                                // 1 is faster to compile: just one templated
+                                // function instantiation
+
+// Size of individual objects to benchmark static-NoDestructor-in-a-function
+// usage with.
+constexpr int kObjSize = sizeof(void*)*1;
+
+// Simple object of kObjSize bytes (rounded to int).
+// We benchmark complete reading of its state via Verify().
+class BM_Blob {
+ public:
+  BM_Blob(int val) { for (auto& d : data_) d = val; }
+  BM_Blob() : BM_Blob(-1) {}
+  void Verify(int val) const {  // val must be the c-tor argument
+    for (auto& d : data_) ABSL_INTERNAL_CHECK(d == val, "");
+  }
+ private:
+  int data_[kObjSize / sizeof(int) > 0 ? kObjSize / sizeof(int) : 1];
+};
+
+// static-NoDestructor-in-a-function pattern instances.
+// We'll instantiate kNumObjects of them.
+template<int i>
+const BM_Blob& NoDestrBlobFunc() {
+  static absl::NoDestructor<BM_Blob> x(i);
+  return *x;
+}
+
+// static-heap-ptr-in-a-function pattern instances
+// We'll instantiate kNumObjects of them.
+template<int i>
+const BM_Blob& OnHeapBlobFunc() {
+  static BM_Blob* x = new BM_Blob(i);
+  return *x;
+}
+
+// Type for NoDestrBlobFunc or OnHeapBlobFunc.
+typedef const BM_Blob& (*FuncType)();
+
+// ========================================================================= //
+// Simple benchmarks that read a single BM_Blob over and over, hence
+// all they touch fits into L1 CPU cache:
+
+// Direct non-POD global variable (style guide violation) as a baseline.
+static BM_Blob direct_blob(0);
+
+void BM_Direct(benchmark::State& state) {
+  for (auto s : state) {
+    direct_blob.Verify(0);
+  }
+}
+BENCHMARK(BM_Direct);
+
+void BM_NoDestr(benchmark::State& state) {
+  for (auto s : state) {
+    NoDestrBlobFunc<0>().Verify(0);
+  }
+}
+BENCHMARK(BM_NoDestr);
+
+void BM_OnHeap(benchmark::State& state) {
+  for (auto s : state) {
+    OnHeapBlobFunc<0>().Verify(0);
+  }
+}
+BENCHMARK(BM_OnHeap);
+
+// ========================================================================= //
+// Benchmarks that read kNumObjects of BM_Blob over and over, hence with
+// appropriate values of sizeof(BM_Blob) and kNumObjects their working set
+// can exceed a given layer of CPU cache.
+
+// Type of benchmark to select between NoDestrBlobFunc and OnHeapBlobFunc.
+enum BM_Type { kNoDestr, kOnHeap, kDirect };
+
+// BlobFunc<n>(t, i) returns the i-th function of type t.
+// n must be larger than i (we'll use kNumObjects for n).
+template<int n>
+FuncType BlobFunc(BM_Type t, int i) {
+  if (i == n) {
+    switch (t) {
+      case kNoDestr:  return &NoDestrBlobFunc<n>;
+      case kOnHeap:   return &OnHeapBlobFunc<n>;
+      case kDirect:   return nullptr;
+    }
+  }
+  return BlobFunc<n-1>(t, i);
+}
+
+template<>
+FuncType BlobFunc<0>(BM_Type t, int i) {
+  ABSL_INTERNAL_CHECK(i == 0, "");
+  switch (t) {
+    case kNoDestr:  return &NoDestrBlobFunc<0>;
+    case kOnHeap:   return &OnHeapBlobFunc<0>;
+    case kDirect:   return nullptr;
+  }
+  return nullptr;
+}
+
+// Direct non-POD global variables (style guide violation) as a baseline.
+static BM_Blob direct_blobs[kNumObjects];
+
+// Helper that cheaply maps benchmark iteration to randomish index in
+// [0, kNumObjects).
+int RandIdx(int i) {
+  // int64 is to avoid overflow and generating negative return values:
+  return (static_cast<int64_t>(i) * 13) % kNumObjects;
+}
+
+// Generic benchmark working with kNumObjects for any of the possible BM_Type.
+template <BM_Type t>
+void BM_Many(benchmark::State& state) {
+  FuncType funcs[kNumObjects];
+  for (int i = 0; i < kNumObjects; ++i) {
+    funcs[i] = BlobFunc<kNumObjects-1>(t, i);
+  }
+  if (t == kDirect) {
+    for (auto s : state) {
+      int idx = RandIdx(state.iterations());
+      direct_blobs[idx].Verify(-1);
+    }
+  } else {
+    for (auto s : state) {
+      int idx = RandIdx(state.iterations());
+      funcs[idx]().Verify(idx);
+    }
+  }
+}
+
+void BM_DirectMany(benchmark::State& state) { BM_Many<kDirect>(state); }
+void BM_NoDestrMany(benchmark::State& state) { BM_Many<kNoDestr>(state); }
+void BM_OnHeapMany(benchmark::State& state) { BM_Many<kOnHeap>(state); }
+
+BENCHMARK(BM_DirectMany);
+BENCHMARK(BM_NoDestrMany);
+BENCHMARK(BM_OnHeapMany);
+
+}  // namespace
diff --git a/absl/base/no_destructor_test.cc b/absl/base/no_destructor_test.cc
new file mode 100644
index 0000000..71693c7
--- /dev/null
+++ b/absl/base/no_destructor_test.cc
@@ -0,0 +1,209 @@
+// 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/base/no_destructor.h"
+
+#include <array>
+#include <initializer_list>
+#include <string>
+#include <type_traits>
+#include <vector>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "absl/base/config.h"
+#include "absl/base/internal/raw_logging.h"
+
+namespace {
+
+struct Blob {
+  Blob() : val(42) {}
+  Blob(int x, int y) : val(x + y) {}
+  Blob(std::initializer_list<int> xs) {
+    val = 0;
+    for (auto& x : xs) val += x;
+  }
+
+  Blob(const Blob& /*b*/) = delete;
+  Blob(Blob&& b) noexcept : val(b.val) {
+    b.moved_out = true;
+  }  // moving is fine
+
+  // no crash: NoDestructor indeed does not destruct (the moved-out Blob
+  // temporaries do get destroyed though)
+  ~Blob() { ABSL_INTERNAL_CHECK(moved_out, "~Blob"); }
+
+  int val;
+  bool moved_out = false;
+};
+
+struct TypeWithDeletedDestructor {
+  ~TypeWithDeletedDestructor() = delete;
+};
+
+TEST(NoDestructorTest, DestructorNeverCalled) {
+  absl::NoDestructor<TypeWithDeletedDestructor> a;
+  (void)a;
+}
+
+TEST(NoDestructorTest, Noncopyable) {
+  using T = absl::NoDestructor<int>;
+
+  EXPECT_FALSE((std::is_constructible<T, T>::value));
+  EXPECT_FALSE((std::is_constructible<T, const T>::value));
+  EXPECT_FALSE((std::is_constructible<T, T&>::value));
+  EXPECT_FALSE((std::is_constructible<T, const T&>::value));
+
+  EXPECT_FALSE((std::is_assignable<T&, T>::value));
+  EXPECT_FALSE((std::is_assignable<T&, const T>::value));
+  EXPECT_FALSE((std::is_assignable<T&, T&>::value));
+  EXPECT_FALSE((std::is_assignable<T&, const T&>::value));
+}
+
+TEST(NoDestructorTest, Interface) {
+  EXPECT_TRUE(std::is_trivially_destructible<absl::NoDestructor<Blob>>::value);
+  EXPECT_TRUE(
+      std::is_trivially_destructible<absl::NoDestructor<const Blob>>::value);
+  {
+    absl::NoDestructor<Blob> b;  // default c-tor
+    // access: *, ->, get()
+    EXPECT_EQ(42, (*b).val);
+    (*b).val = 55;
+    EXPECT_EQ(55, b->val);
+    b->val = 66;
+    EXPECT_EQ(66, b.get()->val);
+    b.get()->val = 42;  // NOLINT
+    EXPECT_EQ(42, (*b).val);
+  }
+  {
+    absl::NoDestructor<const Blob> b(70, 7);  // regular c-tor, const
+    EXPECT_EQ(77, (*b).val);
+    EXPECT_EQ(77, b->val);
+    EXPECT_EQ(77, b.get()->val);
+  }
+  {
+    const absl::NoDestructor<Blob> b{
+        {20, 28, 40}};  // init-list c-tor, deep const
+    // This only works in clang, not in gcc:
+    // const absl::NoDestructor<Blob> b({20, 28, 40});
+    EXPECT_EQ(88, (*b).val);
+    EXPECT_EQ(88, b->val);
+    EXPECT_EQ(88, b.get()->val);
+  }
+}
+
+TEST(NoDestructorTest, SfinaeRegressionAbstractArg) {
+  struct Abstract {
+    virtual ~Abstract() = default;
+    virtual int foo() const = 0;
+  };
+
+  struct Concrete : Abstract {
+    int foo() const override { return 17; }
+  };
+
+  struct UsesAbstractInConstructor {
+    explicit UsesAbstractInConstructor(const Abstract& abstract)
+        : i(abstract.foo()) {}
+    int i;
+  };
+
+  Concrete input;
+  absl::NoDestructor<UsesAbstractInConstructor> foo1(input);
+  EXPECT_EQ(foo1->i, 17);
+  absl::NoDestructor<UsesAbstractInConstructor> foo2(
+      static_cast<const Abstract&>(input));
+  EXPECT_EQ(foo2->i, 17);
+}
+
+// ========================================================================= //
+
+std::string* Str0() {
+  static absl::NoDestructor<std::string> x;
+  return x.get();
+}
+
+extern const std::string& Str2();
+
+const char* Str1() {
+  static absl::NoDestructor<std::string> x(Str2() + "_Str1");
+  return x->c_str();
+}
+
+const std::string& Str2() {
+  static absl::NoDestructor<std::string> x("Str2");
+  return *x;
+}
+
+const std::string& Str2Copy() {
+  // Exercise copy construction
+  static absl::NoDestructor<std::string> x(Str2());
+  return *x;
+}
+
+typedef std::array<std::string, 3> MyArray;
+const MyArray& Array() {
+  static absl::NoDestructor<MyArray> x{{{"foo", "bar", "baz"}}};
+  // This only works in clang, not in gcc:
+  // static absl::NoDestructor<MyArray> x({{"foo", "bar", "baz"}});
+  return *x;
+}
+
+typedef std::vector<int> MyVector;
+const MyVector& Vector() {
+  static absl::NoDestructor<MyVector> x{{1, 2, 3}};
+  return *x;
+}
+
+const int& Int() {
+  static absl::NoDestructor<int> x;
+  return *x;
+}
+
+TEST(NoDestructorTest, StaticPattern) {
+  EXPECT_TRUE(
+      std::is_trivially_destructible<absl::NoDestructor<std::string>>::value);
+  EXPECT_TRUE(
+      std::is_trivially_destructible<absl::NoDestructor<MyArray>>::value);
+  EXPECT_TRUE(
+      std::is_trivially_destructible<absl::NoDestructor<MyVector>>::value);
+  EXPECT_TRUE(std::is_trivially_destructible<absl::NoDestructor<int>>::value);
+
+  EXPECT_EQ(*Str0(), "");
+  Str0()->append("foo");
+  EXPECT_EQ(*Str0(), "foo");
+
+  EXPECT_EQ(std::string(Str1()), "Str2_Str1");
+
+  EXPECT_EQ(Str2(), "Str2");
+  EXPECT_EQ(Str2Copy(), "Str2");
+
+  EXPECT_THAT(Array(), testing::ElementsAre("foo", "bar", "baz"));
+
+  EXPECT_THAT(Vector(), testing::ElementsAre(1, 2, 3));
+
+  EXPECT_EQ(0, Int());  // should get zero-initialized
+}
+
+#ifdef ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION
+// This would fail to compile if Class Template Argument Deduction was not
+// provided for absl::NoDestructor.
+TEST(NoDestructorTest, ClassTemplateArgumentDeduction) {
+  absl::NoDestructor i(1);
+  static_assert(std::is_same<decltype(i), absl::NoDestructor<int>>::value,
+                "Expected deduced type to be int.");
+}
+#endif
+
+}  // namespace
diff --git a/absl/base/optimization.h b/absl/base/optimization.h
index ad0121a..f985995 100644
--- a/absl/base/optimization.h
+++ b/absl/base/optimization.h
@@ -25,6 +25,7 @@
 #include <assert.h>
 
 #include "absl/base/config.h"
+#include "absl/base/options.h"
 
 // ABSL_BLOCK_TAIL_CALL_OPTIMIZATION
 //
diff --git a/absl/base/options.h b/absl/base/options.h
index 09b98ee..67cbf45 100644
--- a/absl/base/options.h
+++ b/absl/base/options.h
@@ -176,6 +176,32 @@
 
 #define ABSL_OPTION_USE_STD_VARIANT 1
 
+// 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
@@ -200,7 +226,7 @@
 // allowed.
 
 #define ABSL_OPTION_USE_INLINE_NAMESPACE 1
-#define ABSL_OPTION_INLINE_NAMESPACE_NAME lts_20230802
+#define ABSL_OPTION_INLINE_NAMESPACE_NAME lts_20240116
 
 // ABSL_OPTION_HARDENED
 //
diff --git a/absl/base/prefetch.h b/absl/base/prefetch.h
index de7a180..eb40a44 100644
--- a/absl/base/prefetch.h
+++ b/absl/base/prefetch.h
@@ -24,17 +24,19 @@
 #ifndef ABSL_BASE_PREFETCH_H_
 #define ABSL_BASE_PREFETCH_H_
 
+#include "absl/base/attributes.h"
 #include "absl/base/config.h"
 
 #if defined(ABSL_INTERNAL_HAVE_SSE)
 #include <xmmintrin.h>
 #endif
 
-#if defined(_MSC_VER) && _MSC_VER >= 1900 && \
-    (defined(_M_X64) || defined(_M_IX86))
+#if defined(_MSC_VER)
 #include <intrin.h>
+#if defined(ABSL_INTERNAL_HAVE_SSE)
 #pragma intrinsic(_mm_prefetch)
 #endif
+#endif
 
 namespace absl {
 ABSL_NAMESPACE_BEGIN
@@ -140,21 +142,24 @@
 // See __builtin_prefetch:
 // https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html.
 //
-inline void PrefetchToLocalCache(const void* addr) {
+ABSL_ATTRIBUTE_ALWAYS_INLINE inline void PrefetchToLocalCache(
+    const void* addr) {
   __builtin_prefetch(addr, 0, 3);
 }
 
-inline void PrefetchToLocalCacheNta(const void* addr) {
+ABSL_ATTRIBUTE_ALWAYS_INLINE inline void PrefetchToLocalCacheNta(
+    const void* addr) {
   __builtin_prefetch(addr, 0, 0);
 }
 
-inline void PrefetchToLocalCacheForWrite(const void* addr) {
+ABSL_ATTRIBUTE_ALWAYS_INLINE inline void PrefetchToLocalCacheForWrite(
+    const void* addr) {
   // [x86] gcc/clang don't generate PREFETCHW for __builtin_prefetch(.., 1)
   // unless -march=broadwell or newer; this is not generally the default, so we
   // manually emit prefetchw. PREFETCHW is recognized as a no-op on older Intel
   // processors and has been present on AMD processors since the K6-2.
-#if defined(__x86_64__)
-  asm("prefetchw (%0)" : : "r"(addr));
+#if defined(__x86_64__) && !defined(__PRFCHW__)
+  asm("prefetchw %0" : : "m"(*reinterpret_cast<const char*>(addr)));
 #else
   __builtin_prefetch(addr, 1, 3);
 #endif
@@ -164,15 +169,18 @@
 
 #define ABSL_HAVE_PREFETCH 1
 
-inline void PrefetchToLocalCache(const void* addr) {
+ABSL_ATTRIBUTE_ALWAYS_INLINE inline void PrefetchToLocalCache(
+    const void* addr) {
   _mm_prefetch(reinterpret_cast<const char*>(addr), _MM_HINT_T0);
 }
 
-inline void PrefetchToLocalCacheNta(const void* addr) {
+ABSL_ATTRIBUTE_ALWAYS_INLINE inline void PrefetchToLocalCacheNta(
+    const void* addr) {
   _mm_prefetch(reinterpret_cast<const char*>(addr), _MM_HINT_NTA);
 }
 
-inline void PrefetchToLocalCacheForWrite(const void* addr) {
+ABSL_ATTRIBUTE_ALWAYS_INLINE inline void PrefetchToLocalCacheForWrite(
+    const void* addr) {
 #if defined(_MM_HINT_ET0)
   _mm_prefetch(reinterpret_cast<const char*>(addr), _MM_HINT_ET0);
 #elif !defined(_MSC_VER) && defined(__x86_64__)
@@ -180,15 +188,18 @@
   // up, PREFETCHW is recognized as a no-op on older Intel processors
   // and has been present on AMD processors since the K6-2. We have this
   // disabled for MSVC compilers as this miscompiles on older MSVC compilers.
-  asm("prefetchw (%0)" : : "r"(addr));
+  asm("prefetchw %0" : : "m"(*reinterpret_cast<const char*>(addr)));
 #endif
 }
 
 #else
 
-inline void PrefetchToLocalCache(const void* addr) {}
-inline void PrefetchToLocalCacheNta(const void* addr) {}
-inline void PrefetchToLocalCacheForWrite(const void* addr) {}
+ABSL_ATTRIBUTE_ALWAYS_INLINE inline void PrefetchToLocalCache(
+    const void* addr) {}
+ABSL_ATTRIBUTE_ALWAYS_INLINE inline void PrefetchToLocalCacheNta(
+    const void* addr) {}
+ABSL_ATTRIBUTE_ALWAYS_INLINE inline void PrefetchToLocalCacheForWrite(
+    const void* addr) {}
 
 #endif
 
diff --git a/absl/base/spinlock_test_common.cc b/absl/base/spinlock_test_common.cc
index 52ecf58..e904715 100644
--- a/absl/base/spinlock_test_common.cc
+++ b/absl/base/spinlock_test_common.cc
@@ -51,6 +51,8 @@
   static int64_t DecodeWaitCycles(uint32_t lock_value) {
     return SpinLock::DecodeWaitCycles(lock_value);
   }
+
+  static bool IsCooperative(const SpinLock& l) { return l.IsCooperative(); }
 };
 
 namespace {
@@ -266,6 +268,17 @@
                        base_internal::NumCPUs() * 2);
 }
 
+TEST(SpinLockTest, IsCooperative) {
+  SpinLock default_constructor;
+  EXPECT_TRUE(SpinLockTest::IsCooperative(default_constructor));
+
+  SpinLock cooperative(base_internal::SCHEDULE_COOPERATIVE_AND_KERNEL);
+  EXPECT_TRUE(SpinLockTest::IsCooperative(cooperative));
+
+  SpinLock kernel_only(base_internal::SCHEDULE_KERNEL_ONLY);
+  EXPECT_FALSE(SpinLockTest::IsCooperative(kernel_only));
+}
+
 }  // namespace
 }  // namespace base_internal
 ABSL_NAMESPACE_END
diff --git a/absl/base/thread_annotations.h b/absl/base/thread_annotations.h
index bc8a620..4a3f3e3 100644
--- a/absl/base/thread_annotations.h
+++ b/absl/base/thread_annotations.h
@@ -36,8 +36,6 @@
 
 #include "absl/base/attributes.h"
 #include "absl/base/config.h"
-// TODO(mbonadei): Remove after the backward compatibility period.
-#include "absl/base/internal/thread_annotations.h"  // IWYU pragma: export
 
 // ABSL_GUARDED_BY()
 //
diff --git a/absl/cleanup/BUILD.bazel b/absl/cleanup/BUILD.bazel
index 2154d9f..984d571 100644
--- a/absl/cleanup/BUILD.bazel
+++ b/absl/cleanup/BUILD.bazel
@@ -19,7 +19,14 @@
     "ABSL_TEST_COPTS",
 )
 
-package(default_visibility = ["//visibility:public"])
+package(
+    default_visibility = ["//visibility:public"],
+    features = [
+        "header_modules",
+        "layering_check",
+        "parse_headers",
+    ],
+)
 
 licenses(["notice"])
 
@@ -60,6 +67,7 @@
         ":cleanup",
         "//absl/base:config",
         "//absl/utility",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
diff --git a/absl/container/BUILD.bazel b/absl/container/BUILD.bazel
index f22da59..0ba2fa7 100644
--- a/absl/container/BUILD.bazel
+++ b/absl/container/BUILD.bazel
@@ -21,7 +21,14 @@
     "ABSL_TEST_COPTS",
 )
 
-package(default_visibility = ["//visibility:public"])
+package(
+    default_visibility = ["//visibility:public"],
+    features = [
+        "header_modules",
+        "layering_check",
+        "parse_headers",
+    ],
+)
 
 licenses(["notice"])
 
@@ -47,6 +54,7 @@
         "//absl/types:any",
         "//absl/types:optional",
         "//absl/utility",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -73,12 +81,13 @@
     copts = ABSL_TEST_COPTS,
     linkopts = ABSL_DEFAULT_LINKOPTS,
     deps = [
-        ":counting_allocator",
         ":fixed_array",
+        ":test_allocator",
         "//absl/base:config",
         "//absl/base:exception_testing",
         "//absl/hash:hash_testing",
         "//absl/memory",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -92,6 +101,7 @@
         ":fixed_array",
         "//absl/base:config",
         "//absl/base:exception_safety_testing",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -116,6 +126,7 @@
     linkopts = ABSL_DEFAULT_LINKOPTS,
     deps = [
         ":compressed_tuple",
+        "//absl/base:config",
         "//absl/base:core_headers",
         "//absl/memory",
         "//absl/meta:type_traits",
@@ -139,13 +150,12 @@
 )
 
 cc_library(
-    name = "counting_allocator",
+    name = "test_allocator",
     testonly = 1,
-    hdrs = ["internal/counting_allocator.h"],
-    copts = ABSL_DEFAULT_COPTS,
+    copts = ABSL_TEST_COPTS,
     linkopts = ABSL_DEFAULT_LINKOPTS,
+    textual_hdrs = ["internal/test_allocator.h"],
     visibility = ["//visibility:private"],
-    deps = ["//absl/base:config"],
 )
 
 cc_test(
@@ -154,8 +164,8 @@
     copts = ABSL_TEST_COPTS,
     linkopts = ABSL_DEFAULT_LINKOPTS,
     deps = [
-        ":counting_allocator",
         ":inlined_vector",
+        ":test_allocator",
         ":test_instance_tracker",
         "//absl/base:config",
         "//absl/base:core_headers",
@@ -164,6 +174,7 @@
         "//absl/log:check",
         "//absl/memory",
         "//absl/strings",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -192,6 +203,7 @@
         ":inlined_vector",
         "//absl/base:config",
         "//absl/base:exception_safety_testing",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -216,6 +228,7 @@
     linkopts = ABSL_DEFAULT_LINKOPTS,
     deps = [
         ":test_instance_tracker",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -256,7 +269,9 @@
         ":unordered_map_members_test",
         ":unordered_map_modifiers_test",
         "//absl/log:check",
+        "//absl/meta:type_traits",
         "//absl/types:any",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -283,15 +298,18 @@
     linkopts = ABSL_DEFAULT_LINKOPTS,
     tags = ["no_test_loonix"],
     deps = [
+        ":container_memory",
         ":flat_hash_set",
         ":hash_generator_testing",
         ":unordered_set_constructor_test",
         ":unordered_set_lookup_test",
         ":unordered_set_members_test",
         ":unordered_set_modifiers_test",
+        "//absl/base:config",
         "//absl/log:check",
         "//absl/memory",
         "//absl/strings",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -326,6 +344,7 @@
         ":unordered_map_lookup_test",
         ":unordered_map_members_test",
         ":unordered_map_modifiers_test",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -357,6 +376,7 @@
         ":unordered_set_lookup_test",
         ":unordered_set_members_test",
         ":unordered_set_modifiers_test",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -383,7 +403,10 @@
     deps = [
         ":container_memory",
         ":test_instance_tracker",
+        "//absl/base:no_destructor",
+        "//absl/meta:type_traits",
         "//absl/strings",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -417,6 +440,7 @@
         "//absl/strings",
         "//absl/strings:cord",
         "//absl/strings:cord_test_helpers",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -430,6 +454,7 @@
     linkopts = ABSL_DEFAULT_LINKOPTS,
     deps = [
         ":hash_policy_testing",
+        "//absl/base:no_destructor",
         "//absl/memory",
         "//absl/meta:type_traits",
         "//absl/strings",
@@ -455,6 +480,7 @@
     linkopts = ABSL_DEFAULT_LINKOPTS,
     deps = [
         ":hash_policy_testing",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -477,6 +503,7 @@
     linkopts = ABSL_DEFAULT_LINKOPTS,
     deps = [
         ":hash_policy_traits",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -497,6 +524,8 @@
     linkopts = ABSL_DEFAULT_LINKOPTS,
     deps = [
         ":common_policy_traits",
+        "//absl/base:config",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -560,6 +589,7 @@
         "//absl/synchronization",
         "//absl/synchronization:thread_pool",
         "//absl/time",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -580,6 +610,8 @@
     deps = [
         ":hash_policy_traits",
         ":node_slot_policy",
+        "//absl/base:config",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -592,6 +624,8 @@
     deps = [
         ":container_memory",
         ":raw_hash_set",
+        "//absl/base:config",
+        "//absl/base:core_headers",
         "//absl/base:throw_delegate",
     ],
 )
@@ -651,13 +685,19 @@
         ":hash_function_defaults",
         ":hash_policy_testing",
         ":hashtable_debug",
+        ":hashtablez_sampler",
         ":raw_hash_set",
+        ":test_allocator",
         "//absl/base",
         "//absl/base:config",
         "//absl/base:core_headers",
         "//absl/base:prefetch",
+        "//absl/hash",
         "//absl/log",
+        "//absl/memory",
+        "//absl/meta:type_traits",
         "//absl/strings",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -694,10 +734,12 @@
         ":hash_function_defaults",
         ":hashtable_debug",
         ":raw_hash_set",
+        "//absl/base:no_destructor",
         "//absl/random",
         "//absl/random:distributions",
         "//absl/strings",
         "//absl/strings:str_format",
+        "//absl/types:optional",
     ],
 )
 
@@ -710,7 +752,8 @@
     deps = [
         ":raw_hash_set",
         ":tracked",
-        "//absl/base:core_headers",
+        "//absl/base:config",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -723,6 +766,7 @@
     deps = [
         "//absl/base:config",
         "//absl/base:core_headers",
+        "//absl/debugging:demangle_internal",
         "//absl/meta:type_traits",
         "//absl/strings",
         "//absl/types:span",
@@ -741,9 +785,10 @@
     deps = [
         ":layout",
         "//absl/base:config",
-        "//absl/base:core_headers",
         "//absl/log:check",
         "//absl/types:span",
+        "//absl/utility",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -889,6 +934,7 @@
         ":unordered_set_lookup_test",
         ":unordered_set_members_test",
         ":unordered_set_modifiers_test",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -904,6 +950,7 @@
         ":unordered_map_lookup_test",
         ":unordered_map_members_test",
         ":unordered_map_modifiers_test",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -920,6 +967,7 @@
         ":flat_hash_set",
         ":node_hash_map",
         ":node_hash_set",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -989,7 +1037,7 @@
     deps = [
         ":btree",
         ":btree_test_common",
-        ":counting_allocator",
+        ":test_allocator",
         ":test_instance_tracker",
         "//absl/algorithm:container",
         "//absl/base:core_headers",
@@ -1001,6 +1049,7 @@
         "//absl/strings",
         "//absl/types:compare",
         "//absl/types:optional",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -1031,5 +1080,6 @@
         "//absl/strings:str_format",
         "//absl/time",
         "@com_github_google_benchmark//:benchmark_main",
+        "@com_google_googletest//:gtest",
     ],
 )
diff --git a/absl/container/CMakeLists.txt b/absl/container/CMakeLists.txt
index 39d95e0..128cc0e 100644
--- a/absl/container/CMakeLists.txt
+++ b/absl/container/CMakeLists.txt
@@ -77,13 +77,13 @@
     absl::btree_test_common
     absl::compare
     absl::core_headers
-    absl::counting_allocator
     absl::flags
     absl::hash_testing
     absl::optional
     absl::random_random
     absl::raw_logging_internal
     absl::strings
+    absl::test_allocator
     absl::test_instance_tracker
     GTest::gmock_main
 )
@@ -145,11 +145,11 @@
     ${ABSL_TEST_COPTS}
   DEPS
     absl::fixed_array
-    absl::counting_allocator
     absl::config
     absl::exception_testing
     absl::hash_testing
     absl::memory
+    absl::test_allocator
     GTest::gmock_main
 )
 
@@ -177,6 +177,7 @@
     ${ABSL_DEFAULT_COPTS}
   DEPS
     absl::compressed_tuple
+    absl::config
     absl::core_headers
     absl::memory
     absl::span
@@ -204,13 +205,14 @@
 # Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
-    counting_allocator
+    test_allocator
   HDRS
-    "internal/counting_allocator.h"
+    "internal/test_allocator.h"
   COPTS
     ${ABSL_DEFAULT_COPTS}
   DEPS
     absl::config
+    GTest::gmock
 )
 
 absl_cc_test(
@@ -224,12 +226,12 @@
     absl::check
     absl::config
     absl::core_headers
-    absl::counting_allocator
     absl::exception_testing
     absl::hash_testing
     absl::inlined_vector
     absl::memory
     absl::strings
+    absl::test_allocator
     absl::test_instance_tracker
     GTest::gmock_main
 )
@@ -304,6 +306,7 @@
     absl::check
     absl::flat_hash_map
     absl::hash_generator_testing
+    absl::type_traits
     absl::unordered_map_constructor_test
     absl::unordered_map_lookup_test
     absl::unordered_map_members_test
@@ -338,6 +341,8 @@
     "-DUNORDERED_SET_CXX17"
   DEPS
     absl::check
+    absl::config
+    absl::container_memory
     absl::flat_hash_set
     absl::hash_generator_testing
     absl::memory
@@ -445,8 +450,10 @@
     ${ABSL_TEST_COPTS}
   DEPS
     absl::container_memory
+    absl::no_destructor
     absl::strings
     absl::test_instance_tracker
+    absl::type_traits
     GTest::gmock_main
 )
 
@@ -497,6 +504,7 @@
     absl::hash_policy_testing
     absl::memory
     absl::meta
+    absl::no_destructor
     absl::strings
   TESTONLY
 )
@@ -575,6 +583,7 @@
     ${ABSL_TEST_COPTS}
   DEPS
     absl::common_policy_traits
+    absl::config
     GTest::gmock_main
 )
 
@@ -658,6 +667,7 @@
   COPTS
     ${ABSL_TEST_COPTS}
   DEPS
+    absl::config
     absl::hash_policy_traits
     absl::node_slot_policy
     GTest::gmock_main
@@ -672,7 +682,9 @@
   COPTS
     ${ABSL_DEFAULT_COPTS}
   DEPS
+    absl::config
     absl::container_memory
+    absl::core_headers
     absl::raw_hash_set
     absl::throw_delegate
   PUBLIC
@@ -736,13 +748,18 @@
     absl::core_headers
     absl::flat_hash_map
     absl::flat_hash_set
+    absl::hash
     absl::hash_function_defaults
     absl::hash_policy_testing
     absl::hashtable_debug
+    absl::hashtablez_sampler
     absl::log
+    absl::memory
     absl::prefetch
     absl::raw_hash_set
     absl::strings
+    absl::test_allocator
+    absl::type_traits
     GTest::gmock_main
 )
 
@@ -754,9 +771,9 @@
   COPTS
     ${ABSL_TEST_COPTS}
   DEPS
+    absl::config
     absl::raw_hash_set
     absl::tracked
-    absl::core_headers
     GTest::gmock_main
 )
 
@@ -771,6 +788,7 @@
   DEPS
     absl::config
     absl::core_headers
+    absl::debugging_internal
     absl::meta
     absl::strings
     absl::span
@@ -789,8 +807,8 @@
     absl::layout
     absl::check
     absl::config
-    absl::core_headers
     absl::span
+    absl::utility
     GTest::gmock_main
 )
 
diff --git a/absl/container/btree_map.h b/absl/container/btree_map.h
index cd3ee2b..0f62f0b 100644
--- a/absl/container/btree_map.h
+++ b/absl/container/btree_map.h
@@ -53,6 +53,7 @@
 #ifndef ABSL_CONTAINER_BTREE_MAP_H_
 #define ABSL_CONTAINER_BTREE_MAP_H_
 
+#include "absl/base/attributes.h"
 #include "absl/container/internal/btree.h"  // IWYU pragma: export
 #include "absl/container/internal/btree_container.h"  // IWYU pragma: export
 
@@ -864,7 +865,8 @@
   using init_type = typename super_type::init_type;
 
   template <typename V>
-  static auto key(const V &value) -> decltype(value.first) {
+  static auto key(const V &value ABSL_ATTRIBUTE_LIFETIME_BOUND)
+      -> decltype((value.first)) {
     return value.first;
   }
   static const Key &key(const slot_type *s) { return slot_policy::key(s); }
diff --git a/absl/container/btree_test.cc b/absl/container/btree_test.cc
index 72f446b..d7102fe 100644
--- a/absl/container/btree_test.cc
+++ b/absl/container/btree_test.cc
@@ -37,7 +37,7 @@
 #include "absl/base/macros.h"
 #include "absl/container/btree_map.h"
 #include "absl/container/btree_set.h"
-#include "absl/container/internal/counting_allocator.h"
+#include "absl/container/internal/test_allocator.h"
 #include "absl/container/internal/test_instance_tracker.h"
 #include "absl/flags/flag.h"
 #include "absl/hash/hash_testing.h"
@@ -76,16 +76,6 @@
   CheckPairEquals(x.first, y.first);
   CheckPairEquals(x.second, y.second);
 }
-
-bool IsAssertEnabled() {
-  // Use an assert with side-effects to figure out if they are actually enabled.
-  bool assert_enabled = false;
-  assert([&]() {  // NOLINT
-    assert_enabled = true;
-    return true;
-  }());
-  return assert_enabled;
-}
 }  // namespace
 
 // The base class for a sorted associative container checker. TreeType is the
@@ -668,111 +658,6 @@
 }
 
 template <typename T>
-struct PropagatingCountingAlloc : public CountingAllocator<T> {
-  using propagate_on_container_copy_assignment = std::true_type;
-  using propagate_on_container_move_assignment = std::true_type;
-  using propagate_on_container_swap = std::true_type;
-
-  using Base = CountingAllocator<T>;
-  using Base::Base;
-
-  template <typename U>
-  explicit PropagatingCountingAlloc(const PropagatingCountingAlloc<U> &other)
-      : Base(other.bytes_used_) {}
-
-  template <typename U>
-  struct rebind {
-    using other = PropagatingCountingAlloc<U>;
-  };
-};
-
-template <typename T>
-void BtreeAllocatorTest() {
-  using value_type = typename T::value_type;
-
-  int64_t bytes1 = 0, bytes2 = 0;
-  PropagatingCountingAlloc<T> allocator1(&bytes1);
-  PropagatingCountingAlloc<T> allocator2(&bytes2);
-  Generator<value_type> generator(1000);
-
-  // Test that we allocate properly aligned memory. If we don't, then Layout
-  // will assert fail.
-  auto unused1 = allocator1.allocate(1);
-  auto unused2 = allocator2.allocate(1);
-
-  // Test copy assignment
-  {
-    T b1(typename T::key_compare(), allocator1);
-    T b2(typename T::key_compare(), allocator2);
-
-    int64_t original_bytes1 = bytes1;
-    b1.insert(generator(0));
-    EXPECT_GT(bytes1, original_bytes1);
-
-    // This should propagate the allocator.
-    b1 = b2;
-    EXPECT_EQ(b1.size(), 0);
-    EXPECT_EQ(b2.size(), 0);
-    EXPECT_EQ(bytes1, original_bytes1);
-
-    for (int i = 1; i < 1000; i++) {
-      b1.insert(generator(i));
-    }
-
-    // We should have allocated out of allocator2.
-    EXPECT_GT(bytes2, bytes1);
-  }
-
-  // Test move assignment
-  {
-    T b1(typename T::key_compare(), allocator1);
-    T b2(typename T::key_compare(), allocator2);
-
-    int64_t original_bytes1 = bytes1;
-    b1.insert(generator(0));
-    EXPECT_GT(bytes1, original_bytes1);
-
-    // This should propagate the allocator.
-    b1 = std::move(b2);
-    EXPECT_EQ(b1.size(), 0);
-    EXPECT_EQ(bytes1, original_bytes1);
-
-    for (int i = 1; i < 1000; i++) {
-      b1.insert(generator(i));
-    }
-
-    // We should have allocated out of allocator2.
-    EXPECT_GT(bytes2, bytes1);
-  }
-
-  // Test swap
-  {
-    T b1(typename T::key_compare(), allocator1);
-    T b2(typename T::key_compare(), allocator2);
-
-    int64_t original_bytes1 = bytes1;
-    b1.insert(generator(0));
-    EXPECT_GT(bytes1, original_bytes1);
-
-    // This should swap the allocators.
-    swap(b1, b2);
-    EXPECT_EQ(b1.size(), 0);
-    EXPECT_EQ(b2.size(), 1);
-    EXPECT_GT(bytes1, original_bytes1);
-
-    for (int i = 1; i < 1000; i++) {
-      b1.insert(generator(i));
-    }
-
-    // We should have allocated out of allocator2.
-    EXPECT_GT(bytes2, bytes1);
-  }
-
-  allocator1.deallocate(unused1, 1);
-  allocator2.deallocate(unused2, 1);
-}
-
-template <typename T>
 void BtreeMapTest() {
   using value_type = typename T::value_type;
   using mapped_type = typename T::mapped_type;
@@ -811,10 +696,7 @@
       sizeof(absl::btree_set<K>),
       2 * sizeof(void *) + sizeof(typename absl::btree_set<K>::size_type));
   using BtreeSet = absl::btree_set<K>;
-  using CountingBtreeSet =
-      absl::btree_set<K, std::less<K>, PropagatingCountingAlloc<K>>;
   BtreeTest<BtreeSet, std::set<K>>();
-  BtreeAllocatorTest<CountingBtreeSet>();
 }
 
 template <typename K, int N = 256>
@@ -823,24 +705,16 @@
       sizeof(absl::btree_map<K, K>),
       2 * sizeof(void *) + sizeof(typename absl::btree_map<K, K>::size_type));
   using BtreeMap = absl::btree_map<K, K>;
-  using CountingBtreeMap =
-      absl::btree_map<K, K, std::less<K>,
-                      PropagatingCountingAlloc<std::pair<const K, K>>>;
   BtreeTest<BtreeMap, std::map<K, K>>();
-  BtreeAllocatorTest<CountingBtreeMap>();
   BtreeMapTest<BtreeMap>();
 }
 
 TEST(Btree, set_int32) { SetTest<int32_t>(); }
-TEST(Btree, set_int64) { SetTest<int64_t>(); }
 TEST(Btree, set_string) { SetTest<std::string>(); }
 TEST(Btree, set_cord) { SetTest<absl::Cord>(); }
-TEST(Btree, set_pair) { SetTest<std::pair<int, int>>(); }
 TEST(Btree, map_int32) { MapTest<int32_t>(); }
-TEST(Btree, map_int64) { MapTest<int64_t>(); }
 TEST(Btree, map_string) { MapTest<std::string>(); }
 TEST(Btree, map_cord) { MapTest<absl::Cord>(); }
-TEST(Btree, map_pair) { MapTest<std::pair<int, int>>(); }
 
 template <typename K, int N = 256>
 void MultiSetTest() {
@@ -848,10 +722,7 @@
       sizeof(absl::btree_multiset<K>),
       2 * sizeof(void *) + sizeof(typename absl::btree_multiset<K>::size_type));
   using BtreeMSet = absl::btree_multiset<K>;
-  using CountingBtreeMSet =
-      absl::btree_multiset<K, std::less<K>, PropagatingCountingAlloc<K>>;
   BtreeMultiTest<BtreeMSet, std::multiset<K>>();
-  BtreeAllocatorTest<CountingBtreeMSet>();
 }
 
 template <typename K, int N = 256>
@@ -860,24 +731,16 @@
             2 * sizeof(void *) +
                 sizeof(typename absl::btree_multimap<K, K>::size_type));
   using BtreeMMap = absl::btree_multimap<K, K>;
-  using CountingBtreeMMap =
-      absl::btree_multimap<K, K, std::less<K>,
-                           PropagatingCountingAlloc<std::pair<const K, K>>>;
   BtreeMultiTest<BtreeMMap, std::multimap<K, K>>();
   BtreeMultiMapTest<BtreeMMap>();
-  BtreeAllocatorTest<CountingBtreeMMap>();
 }
 
 TEST(Btree, multiset_int32) { MultiSetTest<int32_t>(); }
-TEST(Btree, multiset_int64) { MultiSetTest<int64_t>(); }
 TEST(Btree, multiset_string) { MultiSetTest<std::string>(); }
 TEST(Btree, multiset_cord) { MultiSetTest<absl::Cord>(); }
-TEST(Btree, multiset_pair) { MultiSetTest<std::pair<int, int>>(); }
 TEST(Btree, multimap_int32) { MultiMapTest<int32_t>(); }
-TEST(Btree, multimap_int64) { MultiMapTest<int64_t>(); }
 TEST(Btree, multimap_string) { MultiMapTest<std::string>(); }
 TEST(Btree, multimap_cord) { MultiMapTest<absl::Cord>(); }
-TEST(Btree, multimap_pair) { MultiMapTest<std::pair<int, int>>(); }
 
 struct CompareIntToString {
   bool operator()(const std::string &a, const std::string &b) const {
@@ -1483,7 +1346,8 @@
   tracker->ResetCopiesMovesSwaps();
 }
 
-#ifdef ABSL_HAVE_ADDRESS_SANITIZER
+#if defined(ABSL_HAVE_ADDRESS_SANITIZER) || \
+    defined(ABSL_HAVE_HWADDRESS_SANITIZER)
 constexpr bool kAsan = true;
 #else
 constexpr bool kAsan = false;
@@ -2526,50 +2390,23 @@
   EXPECT_EQ(std::string(10, 'a'), m[1]);
 }
 
-TEST(Btree, MoveAssignmentAllocatorPropagation) {
-  InstanceTracker tracker;
+template <typename Alloc>
+using BtreeSetAlloc = absl::btree_set<int, std::less<int>, Alloc>;
 
-  int64_t bytes1 = 0, bytes2 = 0;
-  PropagatingCountingAlloc<MovableOnlyInstance> allocator1(&bytes1);
-  PropagatingCountingAlloc<MovableOnlyInstance> allocator2(&bytes2);
-  std::less<MovableOnlyInstance> cmp;
+TEST(Btree, AllocatorPropagation) {
+  TestAllocPropagation<BtreeSetAlloc>();
+}
 
-  // Test propagating allocator_type.
-  {
-    absl::btree_set<MovableOnlyInstance, std::less<MovableOnlyInstance>,
-                    PropagatingCountingAlloc<MovableOnlyInstance>>
-        set1(cmp, allocator1), set2(cmp, allocator2);
+TEST(Btree, MinimumAlignmentAllocator) {
+  absl::btree_set<int8_t, std::less<int8_t>, MinimumAlignmentAlloc<int8_t>> set;
 
-    for (int i = 0; i < 100; ++i) set1.insert(MovableOnlyInstance(i));
+  // Do some basic operations. Test that everything is fine when allocator uses
+  // minimal alignment.
+  for (int8_t i = 0; i < 100; ++i) set.insert(i);
+  set.erase(set.find(50), set.end());
+  for (int8_t i = 51; i < 101; ++i) set.insert(i);
 
-    tracker.ResetCopiesMovesSwaps();
-    set2 = std::move(set1);
-    EXPECT_EQ(tracker.moves(), 0);
-  }
-  // Test non-propagating allocator_type with equal allocators.
-  {
-    absl::btree_set<MovableOnlyInstance, std::less<MovableOnlyInstance>,
-                    CountingAllocator<MovableOnlyInstance>>
-        set1(cmp, allocator1), set2(cmp, allocator1);
-
-    for (int i = 0; i < 100; ++i) set1.insert(MovableOnlyInstance(i));
-
-    tracker.ResetCopiesMovesSwaps();
-    set2 = std::move(set1);
-    EXPECT_EQ(tracker.moves(), 0);
-  }
-  // Test non-propagating allocator_type with different allocators.
-  {
-    absl::btree_set<MovableOnlyInstance, std::less<MovableOnlyInstance>,
-                    CountingAllocator<MovableOnlyInstance>>
-        set1(cmp, allocator1), set2(cmp, allocator2);
-
-    for (int i = 0; i < 100; ++i) set1.insert(MovableOnlyInstance(i));
-
-    tracker.ResetCopiesMovesSwaps();
-    set2 = std::move(set1);
-    EXPECT_GE(tracker.moves(), 100);
-  }
+  EXPECT_EQ(set.size(), 100);
 }
 
 TEST(Btree, EmptyTree) {
@@ -2965,6 +2802,20 @@
   EXPECT_EQ(set.count(2), 2);
 }
 
+TEST(Btree, SetIteratorsAreConst) {
+  using Set = absl::btree_set<int>;
+  EXPECT_TRUE(
+      (std::is_same<typename Set::iterator::reference, const int &>::value));
+  EXPECT_TRUE(
+      (std::is_same<typename Set::iterator::pointer, const int *>::value));
+
+  using MSet = absl::btree_multiset<int>;
+  EXPECT_TRUE(
+      (std::is_same<typename MSet::iterator::reference, const int &>::value));
+  EXPECT_TRUE(
+      (std::is_same<typename MSet::iterator::pointer, const int *>::value));
+}
+
 TEST(Btree, AllocConstructor) {
   using Alloc = CountingAllocator<int>;
   using Set = absl::btree_set<int, std::less<int>, Alloc>;
@@ -3229,10 +3080,10 @@
   if (!BtreeGenerationsEnabled())
     GTEST_SKIP() << "Generation validation for iterators is disabled.";
 
-  // Invalid memory use can trigger heap-use-after-free in ASan or invalidated
-  // iterator assertions.
+  // Invalid memory use can trigger use-after-free in ASan, HWASAN or
+  // invalidated iterator assertions.
   constexpr const char *kInvalidMemoryDeathMessage =
-      "heap-use-after-free|invalidated iterator";
+      "use-after-free|invalidated iterator";
 
   {
     absl::btree_set<int> set;
@@ -3561,12 +3412,12 @@
   set.insert(0);
   const int *ptr = &*set.begin();
   set.insert(1);
-  EXPECT_DEATH(std::cout << *ptr, "heap-use-after-free");
+  EXPECT_DEATH(std::cout << *ptr, "use-after-free");
   size_t slots_per_node = BtreeNodePeer::GetNumSlotsPerNode<decltype(set)>();
   for (int i = 2; i < slots_per_node - 1; ++i) set.insert(i);
   ptr = &*set.begin();
   set.insert(static_cast<int>(slots_per_node));
-  EXPECT_DEATH(std::cout << *ptr, "heap-use-after-free");
+  EXPECT_DEATH(std::cout << *ptr, "use-after-free");
 }
 
 template<typename Set>
diff --git a/absl/container/fixed_array_test.cc b/absl/container/fixed_array_test.cc
index 9dbf2a8..2421b5f 100644
--- a/absl/container/fixed_array_test.cc
+++ b/absl/container/fixed_array_test.cc
@@ -30,7 +30,7 @@
 #include "absl/base/config.h"
 #include "absl/base/internal/exception_testing.h"
 #include "absl/base/options.h"
-#include "absl/container/internal/counting_allocator.h"
+#include "absl/container/internal/test_allocator.h"
 #include "absl/hash/hash_testing.h"
 #include "absl/memory/memory.h"
 
diff --git a/absl/container/flat_hash_map.h b/absl/container/flat_hash_map.h
index 8f4d993..acd013b 100644
--- a/absl/container/flat_hash_map.h
+++ b/absl/container/flat_hash_map.h
@@ -64,7 +64,7 @@
 //   `insert()`, provided that the map is provided a compatible heterogeneous
 //   hashing function and equality operator.
 // * Invalidates any references and pointers to elements within the table after
-//   `rehash()`.
+//   `rehash()` and when the table is moved.
 // * Contains a `capacity()` member function indicating the number of element
 //   slots (open, deleted, and empty) within the hash map.
 // * Returns `void` from the `erase(iterator)` overload.
@@ -579,9 +579,9 @@
   }
 
   template <class Allocator>
-  static void transfer(Allocator* alloc, slot_type* new_slot,
+  static auto transfer(Allocator* alloc, slot_type* new_slot,
                        slot_type* old_slot) {
-    slot_policy::transfer(alloc, new_slot, old_slot);
+    return slot_policy::transfer(alloc, new_slot, old_slot);
   }
 
   template <class F, class... Args>
diff --git a/absl/container/flat_hash_map_test.cc b/absl/container/flat_hash_map_test.cc
index e6acbea..d90fe9d 100644
--- a/absl/container/flat_hash_map_test.cc
+++ b/absl/container/flat_hash_map_test.cc
@@ -14,14 +14,20 @@
 
 #include "absl/container/flat_hash_map.h"
 
+#include <cstddef>
 #include <memory>
+#include <type_traits>
+#include <utility>
+#include <vector>
 
+#include "gtest/gtest.h"
 #include "absl/container/internal/hash_generator_testing.h"
 #include "absl/container/internal/unordered_map_constructor_test.h"
 #include "absl/container/internal/unordered_map_lookup_test.h"
 #include "absl/container/internal/unordered_map_members_test.h"
 #include "absl/container/internal/unordered_map_modifiers_test.h"
 #include "absl/log/check.h"
+#include "absl/meta/type_traits.h"
 #include "absl/types/any.h"
 
 namespace absl {
@@ -102,6 +108,34 @@
   }
 }
 
+TEST(FlatHashMap, Relocatability) {
+  static_assert(absl::is_trivially_relocatable<int>::value, "");
+  static_assert(
+      absl::is_trivially_relocatable<std::pair<const int, int>>::value, "");
+  static_assert(
+      std::is_same<decltype(absl::container_internal::FlatHashMapPolicy<
+                            int, int>::transfer<std::allocator<char>>(nullptr,
+                                                                      nullptr,
+                                                                      nullptr)),
+                   std::true_type>::value,
+      "");
+
+    struct NonRelocatable {
+      NonRelocatable() = default;
+      NonRelocatable(NonRelocatable&&) {}
+      NonRelocatable& operator=(NonRelocatable&&) { return *this; }
+      void* self = nullptr;
+    };
+
+  EXPECT_FALSE(absl::is_trivially_relocatable<NonRelocatable>::value);
+  EXPECT_TRUE(
+      (std::is_same<decltype(absl::container_internal::FlatHashMapPolicy<
+                            int, NonRelocatable>::
+                                transfer<std::allocator<char>>(nullptr, nullptr,
+                                                               nullptr)),
+                   std::false_type>::value));
+}
+
 // gcc becomes unhappy if this is inside the method, so pull it out here.
 struct balast {};
 
@@ -150,9 +184,7 @@
 
 struct Eq {
   using is_transparent = void;
-  bool operator()(size_t lhs, size_t rhs) const {
-    return lhs == rhs;
-  }
+  bool operator()(size_t lhs, size_t rhs) const { return lhs == rhs; }
   bool operator()(size_t lhs, const LazyInt& rhs) const {
     return lhs == rhs.value;
   }
diff --git a/absl/container/flat_hash_set.h b/absl/container/flat_hash_set.h
index c789c7e..a94a82a 100644
--- a/absl/container/flat_hash_set.h
+++ b/absl/container/flat_hash_set.h
@@ -60,7 +60,7 @@
 //   that the set is provided a compatible heterogeneous hashing function and
 //   equality operator.
 // * Invalidates any references and pointers to elements within the table after
-//   `rehash()`.
+//   `rehash()` and when the table is moved.
 // * Contains a `capacity()` member function indicating the number of element
 //   slots (open, deleted, and empty) within the hash set.
 // * Returns `void` from the `erase(iterator)` overload.
diff --git a/absl/container/flat_hash_set_test.cc b/absl/container/flat_hash_set_test.cc
index 20130f9..a60b4bf 100644
--- a/absl/container/flat_hash_set_test.cc
+++ b/absl/container/flat_hash_set_test.cc
@@ -14,8 +14,15 @@
 
 #include "absl/container/flat_hash_set.h"
 
+#include <cstdint>
+#include <memory>
+#include <utility>
 #include <vector>
 
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "absl/base/config.h"
+#include "absl/container/internal/container_memory.h"
 #include "absl/container/internal/hash_generator_testing.h"
 #include "absl/container/internal/unordered_set_constructor_test.h"
 #include "absl/container/internal/unordered_set_lookup_test.h"
@@ -172,6 +179,64 @@
   }
 }
 
+class PoisonInline {
+  int64_t data_;
+
+ public:
+  explicit PoisonInline(int64_t d) : data_(d) {
+    SanitizerPoisonObject(&data_);
+  }
+  PoisonInline(const PoisonInline& that) : PoisonInline(*that) {}
+  ~PoisonInline() { SanitizerUnpoisonObject(&data_); }
+
+  int64_t operator*() const {
+    SanitizerUnpoisonObject(&data_);
+    const int64_t ret = data_;
+    SanitizerPoisonObject(&data_);
+    return ret;
+  }
+  template <typename H>
+  friend H AbslHashValue(H h, const PoisonInline& pi) {
+    return H::combine(std::move(h), *pi);
+  }
+  bool operator==(const PoisonInline& rhs) const { return **this == *rhs; }
+};
+
+// Tests that we don't touch the poison_ member of PoisonInline.
+TEST(FlatHashSet, PoisonInline) {
+  PoisonInline a(0), b(1);
+  {  // basic usage
+    flat_hash_set<PoisonInline> set;
+    set.insert(a);
+    EXPECT_THAT(set, UnorderedElementsAre(a));
+    set.insert(b);
+    EXPECT_THAT(set, UnorderedElementsAre(a, b));
+    set.erase(a);
+    EXPECT_THAT(set, UnorderedElementsAre(b));
+    set.rehash(0);  // shrink to inline
+    EXPECT_THAT(set, UnorderedElementsAre(b));
+  }
+  {  // test move constructor from inline to inline
+    flat_hash_set<PoisonInline> set;
+    set.insert(a);
+    flat_hash_set<PoisonInline> set2(std::move(set));
+    EXPECT_THAT(set2, UnorderedElementsAre(a));
+  }
+  {  // test move assignment from inline to inline
+    flat_hash_set<PoisonInline> set, set2;
+    set.insert(a);
+    set2 = std::move(set);
+    EXPECT_THAT(set2, UnorderedElementsAre(a));
+  }
+  {  // test alloc move constructor from inline to inline
+    flat_hash_set<PoisonInline> set;
+    set.insert(a);
+    flat_hash_set<PoisonInline> set2(std::move(set),
+                                     std::allocator<PoisonInline>());
+    EXPECT_THAT(set2, UnorderedElementsAre(a));
+  }
+}
+
 }  // namespace
 }  // namespace container_internal
 ABSL_NAMESPACE_END
diff --git a/absl/container/inlined_vector_test.cc b/absl/container/inlined_vector_test.cc
index b9a79f5..241389a 100644
--- a/absl/container/inlined_vector_test.cc
+++ b/absl/container/inlined_vector_test.cc
@@ -33,7 +33,7 @@
 #include "absl/base/internal/exception_testing.h"
 #include "absl/base/macros.h"
 #include "absl/base/options.h"
-#include "absl/container/internal/counting_allocator.h"
+#include "absl/container/internal/test_allocator.h"
 #include "absl/container/internal/test_instance_tracker.h"
 #include "absl/hash/hash_testing.h"
 #include "absl/log/check.h"
diff --git a/absl/container/internal/btree.h b/absl/container/internal/btree.h
index 569faa0..91df57a 100644
--- a/absl/container/internal/btree.h
+++ b/absl/container/internal/btree.h
@@ -79,6 +79,7 @@
 #ifdef ABSL_BTREE_ENABLE_GENERATIONS
 #error ABSL_BTREE_ENABLE_GENERATIONS cannot be directly set
 #elif defined(ABSL_HAVE_ADDRESS_SANITIZER) || \
+    defined(ABSL_HAVE_HWADDRESS_SANITIZER) || \
     defined(ABSL_HAVE_MEMORY_SANITIZER)
 // When compiled in sanitizer mode, we add generation integers to the nodes and
 // iterators. When iterators are used, we validate that the container has not
@@ -572,13 +573,6 @@
   btree_node(btree_node const &) = delete;
   btree_node &operator=(btree_node const &) = delete;
 
-  // Public for EmptyNodeType.
-  constexpr static size_type Alignment() {
-    static_assert(LeafLayout(1).Alignment() == InternalLayout().Alignment(),
-                  "Alignment of all nodes must be equal.");
-    return InternalLayout().Alignment();
-  }
-
  protected:
   btree_node() = default;
 
@@ -653,6 +647,12 @@
     return InternalLayout().AllocSize();
   }
 
+  constexpr static size_type Alignment() {
+    static_assert(LeafLayout(1).Alignment() == InternalLayout().Alignment(),
+                  "Alignment of all nodes must be equal.");
+    return InternalLayout().Alignment();
+  }
+
   // N is the index of the type in the Layout definition.
   // ElementType<N> is the Nth type in the Layout definition.
   template <size_type N>
@@ -1122,8 +1122,11 @@
   using const_reference = typename params_type::const_reference;
   using slot_type = typename params_type::slot_type;
 
-  using iterator =
-     btree_iterator<normal_node, normal_reference, normal_pointer>;
+  // In sets, all iterators are const.
+  using iterator = absl::conditional_t<
+      is_map_container::value,
+      btree_iterator<normal_node, normal_reference, normal_pointer>,
+      btree_iterator<normal_node, const_reference, const_pointer>>;
   using const_iterator =
       btree_iterator<const_node, const_reference, const_pointer>;
 
@@ -1318,7 +1321,7 @@
 
   // We use a static empty node for the root/leftmost/rightmost of empty btrees
   // in order to avoid branching in begin()/end().
-  struct alignas(node_type::Alignment()) EmptyNodeType : node_type {
+  struct EmptyNodeType : node_type {
     using field_type = typename node_type::field_type;
     node_type *parent;
 #ifdef ABSL_BTREE_ENABLE_GENERATIONS
@@ -1331,25 +1334,12 @@
     // as a leaf node). max_count() is never called when the tree is empty.
     field_type max_count = node_type::kInternalNodeMaxCount + 1;
 
-#ifdef _MSC_VER
-    // MSVC has constexpr code generations bugs here.
-    EmptyNodeType() : parent(this) {}
-#else
-    explicit constexpr EmptyNodeType(node_type *p) : parent(p) {}
-#endif
+    constexpr EmptyNodeType() : parent(this) {}
   };
 
   static node_type *EmptyNode() {
-#ifdef _MSC_VER
-    static EmptyNodeType *empty_node = new EmptyNodeType;
-    // This assert fails on some other construction methods.
-    assert(empty_node->parent == empty_node);
-    return empty_node;
-#else
-    static constexpr EmptyNodeType empty_node(
-        const_cast<EmptyNodeType *>(&empty_node));
+    alignas(node_type::Alignment()) static constexpr EmptyNodeType empty_node;
     return const_cast<EmptyNodeType *>(&empty_node);
-#endif
   }
 
   enum : uint32_t {
@@ -2420,7 +2410,7 @@
 
     using std::swap;
     if (absl::allocator_traits<
-            allocator_type>::propagate_on_container_copy_assignment::value) {
+            allocator_type>::propagate_on_container_move_assignment::value) {
       swap(root_, other.root_);
       // Note: `rightmost_` also contains the allocator and the key comparator.
       swap(rightmost_, other.rightmost_);
@@ -2534,6 +2524,10 @@
   return res;
 }
 
+// Note: we tried implementing this more efficiently by erasing all of the
+// elements in [begin, end) at once and then doing rebalancing once at the end
+// (rather than interleaving deletion and rebalancing), but that adds a lot of
+// complexity, which seems to outweigh the performance win.
 template <typename P>
 auto btree<P>::erase_range(iterator begin, iterator end)
     -> std::pair<size_type, iterator> {
@@ -2863,7 +2857,8 @@
     }
   }
   (void)replaced_node;
-#ifdef ABSL_HAVE_ADDRESS_SANITIZER
+#if defined(ABSL_HAVE_ADDRESS_SANITIZER) || \
+    defined(ABSL_HAVE_HWADDRESS_SANITIZER)
   if (!replaced_node) {
     assert(iter.node_->is_leaf());
     if (iter.node_->is_root()) {
diff --git a/absl/container/internal/common_policy_traits.h b/absl/container/internal/common_policy_traits.h
index 3558a54..57eac67 100644
--- a/absl/container/internal/common_policy_traits.h
+++ b/absl/container/internal/common_policy_traits.h
@@ -93,11 +93,13 @@
   struct Rank0 : Rank1 {};
 
   // Use auto -> decltype as an enabler.
+  // P::transfer returns std::true_type if transfer uses memcpy (e.g. in
+  // node_slot_policy).
   template <class Alloc, class P = Policy>
   static auto transfer_impl(Alloc* alloc, slot_type* new_slot,
                             slot_type* old_slot, Rank0)
-      -> decltype((void)P::transfer(alloc, new_slot, old_slot)) {
-    P::transfer(alloc, new_slot, old_slot);
+      -> decltype(P::transfer(alloc, new_slot, old_slot)) {
+    return P::transfer(alloc, new_slot, old_slot);
   }
 #if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606
   // This overload returns true_type for the trait below.
diff --git a/absl/container/internal/common_policy_traits_test.cc b/absl/container/internal/common_policy_traits_test.cc
index 5eaa4aa..faee3e7 100644
--- a/absl/container/internal/common_policy_traits_test.cc
+++ b/absl/container/internal/common_policy_traits_test.cc
@@ -16,10 +16,12 @@
 
 #include <functional>
 #include <memory>
-#include <new>
+#include <type_traits>
+#include <utility>
 
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
+#include "absl/base/config.h"
 
 namespace absl {
 ABSL_NAMESPACE_BEGIN
@@ -51,9 +53,14 @@
 struct PolicyWithOptionalOps : PolicyWithoutOptionalOps {
   static std::function<void(void*, Slot*, Slot*)> transfer;
 };
-
 std::function<void(void*, Slot*, Slot*)> PolicyWithOptionalOps::transfer;
 
+struct PolicyWithMemcpyTransfer : PolicyWithoutOptionalOps {
+  static std::function<std::true_type(void*, Slot*, Slot*)> transfer;
+};
+std::function<std::true_type(void*, Slot*, Slot*)>
+    PolicyWithMemcpyTransfer::transfer;
+
 struct Test : ::testing::Test {
   Test() {
     PolicyWithoutOptionalOps::construct = [&](void* a1, Slot* a2, Slot a3) {
@@ -114,6 +121,13 @@
   common_policy_traits<PolicyWithOptionalOps>::transfer(&alloc, &a, &b);
 }
 
+TEST(TransferUsesMemcpy, Basic) {
+  EXPECT_FALSE(
+      common_policy_traits<PolicyWithOptionalOps>::transfer_uses_memcpy());
+  EXPECT_TRUE(
+      common_policy_traits<PolicyWithMemcpyTransfer>::transfer_uses_memcpy());
+}
+
 }  // namespace
 }  // namespace container_internal
 ABSL_NAMESPACE_END
diff --git a/absl/container/internal/container_memory.h b/absl/container/internal/container_memory.h
index f59ca4e..3262d4e 100644
--- a/absl/container/internal/container_memory.h
+++ b/absl/container/internal/container_memory.h
@@ -122,10 +122,10 @@
 // Returns a tuple of references to the elements of the input tuple. T must be a
 // tuple.
 template <class T>
-auto TupleRef(T&& t) -> decltype(
-    TupleRefImpl(std::forward<T>(t),
-                 absl::make_index_sequence<
-                     std::tuple_size<typename std::decay<T>::type>::value>())) {
+auto TupleRef(T&& t) -> decltype(TupleRefImpl(
+    std::forward<T>(t),
+    absl::make_index_sequence<
+        std::tuple_size<typename std::decay<T>::type>::value>())) {
   return TupleRefImpl(
       std::forward<T>(t),
       absl::make_index_sequence<
@@ -156,8 +156,8 @@
 // Constructs T using the args specified in the tuple and calls F with the
 // constructed value.
 template <class T, class Tuple, class F>
-decltype(std::declval<F>()(std::declval<T>())) WithConstructed(
-    Tuple&& t, F&& f) {
+decltype(std::declval<F>()(std::declval<T>())) WithConstructed(Tuple&& t,
+                                                               F&& f) {
   return memory_internal::WithConstructedImpl<T>(
       std::forward<Tuple>(t),
       absl::make_index_sequence<
@@ -423,16 +423,19 @@
   }
 
   template <class Allocator>
-  static void transfer(Allocator* alloc, slot_type* new_slot,
+  static auto transfer(Allocator* alloc, slot_type* new_slot,
                        slot_type* old_slot) {
+    auto is_relocatable =
+        typename absl::is_trivially_relocatable<value_type>::type();
+
     emplace(new_slot);
 #if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606
-    if (absl::is_trivially_relocatable<value_type>()) {
+    if (is_relocatable) {
       // TODO(b/247130232,b/251814870): remove casts after fixing warnings.
       std::memcpy(static_cast<void*>(std::launder(&new_slot->value)),
                   static_cast<const void*>(&old_slot->value),
                   sizeof(value_type));
-      return;
+      return is_relocatable;
     }
 #endif
 
@@ -444,6 +447,7 @@
                                                    std::move(old_slot->value));
     }
     destroy(alloc, old_slot);
+    return is_relocatable;
   }
 };
 
diff --git a/absl/container/internal/container_memory_test.cc b/absl/container/internal/container_memory_test.cc
index fb9c4dd..90d64bf 100644
--- a/absl/container/internal/container_memory_test.cc
+++ b/absl/container/internal/container_memory_test.cc
@@ -14,15 +14,20 @@
 
 #include "absl/container/internal/container_memory.h"
 
+#include <cstddef>
 #include <cstdint>
+#include <memory>
 #include <tuple>
+#include <type_traits>
 #include <typeindex>
 #include <typeinfo>
 #include <utility>
 
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
+#include "absl/base/no_destructor.h"
 #include "absl/container/internal/test_instance_tracker.h"
+#include "absl/meta/type_traits.h"
 #include "absl/strings/string_view.h"
 
 namespace absl {
@@ -54,7 +59,7 @@
 }
 
 std::map<std::type_index, int>& AllocationMap() {
-  static auto* map = new std::map<std::type_index, int>;
+  static absl::NoDestructor<std::map<std::type_index, int>> map;
   return *map;
 }
 
@@ -219,8 +224,7 @@
     ADD_FAILURE() << "Must not be called";
     return 'A';
   };
-  EXPECT_STREQ("not decomposable",
-               TryDecomposePair(f));
+  EXPECT_STREQ("not decomposable", TryDecomposePair(f));
   EXPECT_STREQ("not decomposable",
                TryDecomposePair(f, std::piecewise_construct, std::make_tuple(),
                                 std::make_tuple(0.5)));
@@ -251,6 +255,31 @@
   EXPECT_EQ(tracker.copies(), 0);
 }
 
+TEST(MapSlotPolicy, TransferReturnsTrue) {
+  {
+    using slot_policy = map_slot_policy<int, float>;
+    EXPECT_TRUE(
+        (std::is_same<decltype(slot_policy::transfer<std::allocator<char>>(
+                          nullptr, nullptr, nullptr)),
+                      std::true_type>::value));
+  }
+  {
+    struct NonRelocatable {
+      NonRelocatable() = default;
+      NonRelocatable(NonRelocatable&&) {}
+      NonRelocatable& operator=(NonRelocatable&&) { return *this; }
+      void* self = nullptr;
+    };
+
+    EXPECT_FALSE(absl::is_trivially_relocatable<NonRelocatable>::value);
+    using slot_policy = map_slot_policy<int, NonRelocatable>;
+    EXPECT_TRUE(
+        (std::is_same<decltype(slot_policy::transfer<std::allocator<char>>(
+                          nullptr, nullptr, nullptr)),
+                      std::false_type>::value));
+  }
+}
+
 }  // namespace
 }  // namespace container_internal
 ABSL_NAMESPACE_END
diff --git a/absl/container/internal/counting_allocator.h b/absl/container/internal/counting_allocator.h
deleted file mode 100644
index 66068a5..0000000
--- a/absl/container/internal/counting_allocator.h
+++ /dev/null
@@ -1,122 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_CONTAINER_INTERNAL_COUNTING_ALLOCATOR_H_
-#define ABSL_CONTAINER_INTERNAL_COUNTING_ALLOCATOR_H_
-
-#include <cstdint>
-#include <memory>
-
-#include "absl/base/config.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace container_internal {
-
-// This is a stateful allocator, but the state lives outside of the
-// allocator (in whatever test is using the allocator). This is odd
-// but helps in tests where the allocator is propagated into nested
-// containers - that chain of allocators uses the same state and is
-// thus easier to query for aggregate allocation information.
-template <typename T>
-class CountingAllocator {
- public:
-  using Allocator = std::allocator<T>;
-  using AllocatorTraits = std::allocator_traits<Allocator>;
-  using value_type = typename AllocatorTraits::value_type;
-  using pointer = typename AllocatorTraits::pointer;
-  using const_pointer = typename AllocatorTraits::const_pointer;
-  using size_type = typename AllocatorTraits::size_type;
-  using difference_type = typename AllocatorTraits::difference_type;
-
-  CountingAllocator() = default;
-  explicit CountingAllocator(int64_t* bytes_used) : bytes_used_(bytes_used) {}
-  CountingAllocator(int64_t* bytes_used, int64_t* instance_count)
-      : bytes_used_(bytes_used), instance_count_(instance_count) {}
-
-  template <typename U>
-  CountingAllocator(const CountingAllocator<U>& x)
-      : bytes_used_(x.bytes_used_), instance_count_(x.instance_count_) {}
-
-  pointer allocate(
-      size_type n,
-      typename AllocatorTraits::const_void_pointer hint = nullptr) {
-    Allocator allocator;
-    pointer ptr = AllocatorTraits::allocate(allocator, n, hint);
-    if (bytes_used_ != nullptr) {
-      *bytes_used_ += n * sizeof(T);
-    }
-    return ptr;
-  }
-
-  void deallocate(pointer p, size_type n) {
-    Allocator allocator;
-    AllocatorTraits::deallocate(allocator, p, n);
-    if (bytes_used_ != nullptr) {
-      *bytes_used_ -= n * sizeof(T);
-    }
-  }
-
-  template <typename U, typename... Args>
-  void construct(U* p, Args&&... args) {
-    Allocator allocator;
-    AllocatorTraits::construct(allocator, p, std::forward<Args>(args)...);
-    if (instance_count_ != nullptr) {
-      *instance_count_ += 1;
-    }
-  }
-
-  template <typename U>
-  void destroy(U* p) {
-    Allocator allocator;
-    // Ignore GCC warning bug.
-#if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(12, 0)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wuse-after-free"
-#endif
-    AllocatorTraits::destroy(allocator, p);
-#if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(12, 0)
-#pragma GCC diagnostic pop
-#endif
-    if (instance_count_ != nullptr) {
-      *instance_count_ -= 1;
-    }
-  }
-
-  template <typename U>
-  class rebind {
-   public:
-    using other = CountingAllocator<U>;
-  };
-
-  friend bool operator==(const CountingAllocator& a,
-                         const CountingAllocator& b) {
-    return a.bytes_used_ == b.bytes_used_ &&
-           a.instance_count_ == b.instance_count_;
-  }
-
-  friend bool operator!=(const CountingAllocator& a,
-                         const CountingAllocator& b) {
-    return !(a == b);
-  }
-
-  int64_t* bytes_used_ = nullptr;
-  int64_t* instance_count_ = nullptr;
-};
-
-}  // namespace container_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_CONTAINER_INTERNAL_COUNTING_ALLOCATOR_H_
diff --git a/absl/container/internal/hash_generator_testing.cc b/absl/container/internal/hash_generator_testing.cc
index 59cc5aa..e89dfdb 100644
--- a/absl/container/internal/hash_generator_testing.cc
+++ b/absl/container/internal/hash_generator_testing.cc
@@ -16,6 +16,8 @@
 
 #include <deque>
 
+#include "absl/base/no_destructor.h"
+
 namespace absl {
 ABSL_NAMESPACE_BEGIN
 namespace container_internal {
@@ -41,11 +43,11 @@
 }  // namespace
 
 std::mt19937_64* GetSharedRng() {
-  static auto* rng = [] {
+  static absl::NoDestructor<std::mt19937_64> rng([] {
     RandomDeviceSeedSeq seed_seq;
-    return new std::mt19937_64(seed_seq);
-  }();
-  return rng;
+    return std::mt19937_64(seed_seq);
+  }());
+  return rng.get();
 }
 
 std::string Generator<std::string>::operator()() const {
@@ -59,7 +61,7 @@
 }
 
 absl::string_view Generator<absl::string_view>::operator()() const {
-  static auto* arena = new std::deque<std::string>();
+  static absl::NoDestructor<std::deque<std::string>> arena;
   // NOLINTNEXTLINE(runtime/int)
   std::uniform_int_distribution<short> chars(0x20, 0x7E);
   arena->emplace_back();
diff --git a/absl/container/internal/hashtable_debug.h b/absl/container/internal/hashtable_debug.h
index 19d5212..c79c1a9 100644
--- a/absl/container/internal/hashtable_debug.h
+++ b/absl/container/internal/hashtable_debug.h
@@ -95,14 +95,6 @@
       HashtableDebugAccess<C>::AllocatedByteSize(c);
 }
 
-// Returns a tight lower bound for AllocatedByteSize(c) where `c` is of type `C`
-// and `c.size()` is equal to `num_elements`.
-template <typename C>
-size_t LowerBoundAllocatedByteSize(size_t num_elements) {
-  return absl::container_internal::hashtable_debug_internal::
-      HashtableDebugAccess<C>::LowerBoundAllocatedByteSize(num_elements);
-}
-
 }  // namespace container_internal
 ABSL_NAMESPACE_END
 }  // namespace absl
diff --git a/absl/container/internal/hashtablez_sampler.h b/absl/container/internal/hashtablez_sampler.h
index d8fd8f3..e41ee2d 100644
--- a/absl/container/internal/hashtablez_sampler.h
+++ b/absl/container/internal/hashtablez_sampler.h
@@ -137,18 +137,7 @@
     UnsampleSlow(info_);
   }
 
-  HashtablezInfoHandle(const HashtablezInfoHandle&) = delete;
-  HashtablezInfoHandle& operator=(const HashtablezInfoHandle&) = delete;
-
-  HashtablezInfoHandle(HashtablezInfoHandle&& o) noexcept
-      : info_(absl::exchange(o.info_, nullptr)) {}
-  HashtablezInfoHandle& operator=(HashtablezInfoHandle&& o) noexcept {
-    if (ABSL_PREDICT_FALSE(info_ != nullptr)) {
-      UnsampleSlow(info_);
-    }
-    info_ = absl::exchange(o.info_, nullptr);
-    return *this;
-  }
+  inline bool IsSampled() const { return ABSL_PREDICT_FALSE(info_ != nullptr); }
 
   inline void RecordStorageChanged(size_t size, size_t capacity) {
     if (ABSL_PREDICT_TRUE(info_ == nullptr)) return;
@@ -198,6 +187,7 @@
   explicit HashtablezInfoHandle(std::nullptr_t) {}
 
   inline void Unregister() {}
+  inline bool IsSampled() const { return false; }
   inline void RecordStorageChanged(size_t /*size*/, size_t /*capacity*/) {}
   inline void RecordRehash(size_t /*total_probe_length*/) {}
   inline void RecordReservation(size_t /*target_capacity*/) {}
diff --git a/absl/container/internal/hashtablez_sampler_test.cc b/absl/container/internal/hashtablez_sampler_test.cc
index 665d518..8ebb08d 100644
--- a/absl/container/internal/hashtablez_sampler_test.cc
+++ b/absl/container/internal/hashtablez_sampler_test.cc
@@ -42,16 +42,11 @@
 #if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
 class HashtablezInfoHandlePeer {
  public:
-  static bool IsSampled(const HashtablezInfoHandle& h) {
-    return h.info_ != nullptr;
-  }
-
   static HashtablezInfo* GetInfo(HashtablezInfoHandle* h) { return h->info_; }
 };
 #else
 class HashtablezInfoHandlePeer {
  public:
-  static bool IsSampled(const HashtablezInfoHandle&) { return false; }
   static HashtablezInfo* GetInfo(HashtablezInfoHandle*) { return nullptr; }
 };
 #endif  // defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
@@ -267,7 +262,7 @@
   for (int i = 0; i < 1000000; ++i) {
     HashtablezInfoHandle h = Sample(test_element_size);
     ++total;
-    if (HashtablezInfoHandlePeer::IsSampled(h)) {
+    if (h.IsSampled()) {
       ++num_sampled;
     }
     sample_rate = static_cast<double>(num_sampled) / total;
@@ -294,6 +289,7 @@
   });
   EXPECT_TRUE(found);
 
+  h.Unregister();
   h = HashtablezInfoHandle();
   found = false;
   sampler.Iterate([&](const HashtablezInfo& h) {
diff --git a/absl/container/internal/inlined_vector.h b/absl/container/internal/inlined_vector.h
index b2a602d..0eb9c34 100644
--- a/absl/container/internal/inlined_vector.h
+++ b/absl/container/internal/inlined_vector.h
@@ -26,6 +26,7 @@
 #include <utility>
 
 #include "absl/base/attributes.h"
+#include "absl/base/config.h"
 #include "absl/base/macros.h"
 #include "absl/container/internal/compressed_tuple.h"
 #include "absl/memory/memory.h"
@@ -384,7 +385,17 @@
 
   bool GetIsAllocated() const { return GetSizeAndIsAllocated() & 1; }
 
-  Pointer<A> GetAllocatedData() { return data_.allocated.allocated_data; }
+  Pointer<A> GetAllocatedData() {
+    // GCC 12 has a false-positive -Wmaybe-uninitialized warning here.
+#if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(12, 0)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
+#endif
+    return data_.allocated.allocated_data;
+#if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(12, 0)
+#pragma GCC diagnostic pop
+#endif
+  }
 
   ConstPointer<A> GetAllocatedData() const {
     return data_.allocated.allocated_data;
diff --git a/absl/container/internal/layout.h b/absl/container/internal/layout.h
index a59a243..a4ba610 100644
--- a/absl/container/internal/layout.h
+++ b/absl/container/internal/layout.h
@@ -55,7 +55,7 @@
 // `Partial()` comes in handy when the array sizes are embedded into the
 // allocation.
 //
-//   // size_t[1] containing N, size_t[1] containing M, double[N], int[M].
+//   // size_t[0] containing N, size_t[1] containing M, double[N], int[M].
 //   using L = Layout<size_t, size_t, double, int>;
 //
 //   unsigned char* Allocate(size_t n, size_t m) {
@@ -172,6 +172,7 @@
 #include <utility>
 
 #include "absl/base/config.h"
+#include "absl/debugging/internal/demangle.h"
 #include "absl/meta/type_traits.h"
 #include "absl/strings/str_cat.h"
 #include "absl/types/span.h"
@@ -181,14 +182,6 @@
 #include <sanitizer/asan_interface.h>
 #endif
 
-#if defined(__GXX_RTTI)
-#define ABSL_INTERNAL_HAS_CXA_DEMANGLE
-#endif
-
-#ifdef ABSL_INTERNAL_HAS_CXA_DEMANGLE
-#include <cxxabi.h>
-#endif
-
 namespace absl {
 ABSL_NAMESPACE_BEGIN
 namespace container_internal {
@@ -294,19 +287,11 @@
 template <class T>
 std::string TypeName() {
   std::string out;
-  int status = 0;
-  char* demangled = nullptr;
-#ifdef ABSL_INTERNAL_HAS_CXA_DEMANGLE
-  demangled = abi::__cxa_demangle(typeid(T).name(), nullptr, nullptr, &status);
+#if ABSL_INTERNAL_HAS_RTTI
+  absl::StrAppend(&out, "<",
+                  absl::debugging_internal::DemangleString(typeid(T).name()),
+                  ">");
 #endif
-  if (status == 0 && demangled != nullptr) {  // Demangling succeeded.
-    absl::StrAppend(&out, "<", demangled, ">");
-    free(demangled);
-  } else {
-#if defined(__GXX_RTTI) || defined(_CPPRTTI)
-    absl::StrAppend(&out, "<", typeid(T).name(), ">");
-#endif
-  }
   return out;
 }
 
diff --git a/absl/container/internal/layout_test.cc b/absl/container/internal/layout_test.cc
index ce599ce..ae55cf7 100644
--- a/absl/container/internal/layout_test.cc
+++ b/absl/container/internal/layout_test.cc
@@ -19,8 +19,12 @@
 #include <stddef.h>
 
 #include <cstdint>
+#include <cstring>
+#include <initializer_list>
 #include <memory>
-#include <sstream>
+#include <ostream>
+#include <string>
+#include <tuple>
 #include <type_traits>
 
 #include "gmock/gmock.h"
@@ -28,6 +32,7 @@
 #include "absl/base/config.h"
 #include "absl/log/check.h"
 #include "absl/types/span.h"
+#include "absl/utility/utility.h"
 
 namespace absl {
 ABSL_NAMESPACE_BEGIN
diff --git a/absl/container/internal/node_slot_policy.h b/absl/container/internal/node_slot_policy.h
index baba574..3f1874d 100644
--- a/absl/container/internal/node_slot_policy.h
+++ b/absl/container/internal/node_slot_policy.h
@@ -62,9 +62,12 @@
     Policy::delete_element(alloc, *slot);
   }
 
+  // Returns true_type to indicate that transfer can use memcpy.
   template <class Alloc>
-  static void transfer(Alloc*, slot_type* new_slot, slot_type* old_slot) {
+  static std::true_type transfer(Alloc*, slot_type* new_slot,
+                                 slot_type* old_slot) {
     *new_slot = *old_slot;
+    return {};
   }
 
   static size_t space_used(const slot_type* slot) {
diff --git a/absl/container/internal/node_slot_policy_test.cc b/absl/container/internal/node_slot_policy_test.cc
index 51b7467..d4ea919 100644
--- a/absl/container/internal/node_slot_policy_test.cc
+++ b/absl/container/internal/node_slot_policy_test.cc
@@ -18,6 +18,7 @@
 
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
+#include "absl/base/config.h"
 #include "absl/container/internal/hash_policy_traits.h"
 
 namespace absl {
@@ -61,6 +62,7 @@
   int* b = &s;
   NodePolicy::transfer(&alloc, &a, &b);
   EXPECT_EQ(&s, a);
+  EXPECT_TRUE(NodePolicy::transfer_uses_memcpy());
 }
 
 }  // namespace
diff --git a/absl/container/internal/raw_hash_map.h b/absl/container/internal/raw_hash_map.h
index 2d5a871..97182bc 100644
--- a/absl/container/internal/raw_hash_map.h
+++ b/absl/container/internal/raw_hash_map.h
@@ -19,6 +19,8 @@
 #include <type_traits>
 #include <utility>
 
+#include "absl/base/attributes.h"
+#include "absl/base/config.h"
 #include "absl/base/internal/throw_delegate.h"
 #include "absl/container/internal/container_memory.h"
 #include "absl/container/internal/raw_hash_set.h"  // IWYU pragma: export
@@ -175,13 +177,20 @@
   template <class K = key_type, class P = Policy, K* = nullptr>
   MappedReference<P> operator[](key_arg<K>&& key)
       ABSL_ATTRIBUTE_LIFETIME_BOUND {
-    return Policy::value(&*try_emplace(std::forward<K>(key)).first);
+    // It is safe to use unchecked_deref here because try_emplace
+    // will always return an iterator pointing to a valid item in the table,
+    // since it inserts if nothing is found for the given key.
+    return Policy::value(
+        &this->unchecked_deref(try_emplace(std::forward<K>(key)).first));
   }
 
   template <class K = key_type, class P = Policy>
   MappedReference<P> operator[](const key_arg<K>& key)
       ABSL_ATTRIBUTE_LIFETIME_BOUND {
-    return Policy::value(&*try_emplace(key).first);
+    // It is safe to use unchecked_deref here because try_emplace
+    // will always return an iterator pointing to a valid item in the table,
+    // since it inserts if nothing is found for the given key.
+    return Policy::value(&this->unchecked_deref(try_emplace(key).first));
   }
 
  private:
diff --git a/absl/container/internal/raw_hash_set.cc b/absl/container/internal/raw_hash_set.cc
index 2ff95b6..9f8ea51 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 {
@@ -67,6 +69,16 @@
   return value ^ static_cast<size_t>(reinterpret_cast<uintptr_t>(&counter));
 }
 
+bool ShouldRehashForBugDetection(const ctrl_t* ctrl, size_t capacity) {
+  // Note: we can't use the abseil-random library because abseil-random
+  // depends on swisstable. We want to return true with probability
+  // `min(1, RehashProbabilityConstant() / capacity())`. In order to do this,
+  // we probe based on a random hash and see if the offset is less than
+  // RehashProbabilityConstant().
+  return probe(ctrl, capacity, absl::HashOf(RandomSeed())).offset() <
+         RehashProbabilityConstant();
+}
+
 }  // namespace
 
 GenerationType* EmptyGeneration() {
@@ -84,13 +96,12 @@
                                               size_t capacity) const {
   if (reserved_growth_ == kReservedGrowthJustRanOut) return true;
   if (reserved_growth_ > 0) return false;
-  // Note: we can't use the abseil-random library because abseil-random
-  // depends on swisstable. We want to return true with probability
-  // `min(1, RehashProbabilityConstant() / capacity())`. In order to do this,
-  // we probe based on a random hash and see if the offset is less than
-  // RehashProbabilityConstant().
-  return probe(ctrl, capacity, absl::HashOf(RandomSeed())).offset() <
-         RehashProbabilityConstant();
+  return ShouldRehashForBugDetection(ctrl, capacity);
+}
+
+bool CommonFieldsGenerationInfoEnabled::should_rehash_for_bug_detection_on_move(
+    const ctrl_t* ctrl, size_t capacity) const {
+  return ShouldRehashForBugDetection(ctrl, capacity);
 }
 
 bool ShouldInsertBackwards(size_t hash, const ctrl_t* ctrl) {
@@ -117,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) {
@@ -218,26 +221,35 @@
   common.infoz().RecordRehash(total_probe_length);
 }
 
-void EraseMetaOnly(CommonFields& c, ctrl_t* it, size_t slot_size) {
-  assert(IsFull(*it) && "erasing a dangling iterator");
-  c.set_size(c.size() - 1);
-  const auto index = static_cast<size_t>(it - c.control());
+static bool WasNeverFull(CommonFields& c, size_t index) {
+  if (is_single_group(c.capacity())) {
+    return true;
+  }
   const size_t index_before = (index - Group::kWidth) & c.capacity();
-  const auto empty_after = Group(it).MaskEmpty();
+  const auto empty_after = Group(c.control() + index).MaskEmpty();
   const auto empty_before = Group(c.control() + index_before).MaskEmpty();
 
   // We count how many consecutive non empties we have to the right and to the
   // left of `it`. If the sum is >= kWidth then there is at least one probe
   // window that might have seen a full group.
-  bool was_never_full = empty_before && empty_after &&
-                        static_cast<size_t>(empty_after.TrailingZeros()) +
-                                empty_before.LeadingZeros() <
-                            Group::kWidth;
+  return empty_before && empty_after &&
+         static_cast<size_t>(empty_after.TrailingZeros()) +
+                 empty_before.LeadingZeros() <
+             Group::kWidth;
+}
 
-  SetCtrl(c, index, was_never_full ? ctrl_t::kEmpty : ctrl_t::kDeleted,
-          slot_size);
-  c.set_growth_left(c.growth_left() + (was_never_full ? 1 : 0));
+void EraseMetaOnly(CommonFields& c, size_t index, size_t slot_size) {
+  assert(IsFull(c.control()[index]) && "erasing a dangling iterator");
+  c.decrement_size();
   c.infoz().RecordErase();
+
+  if (WasNeverFull(c, index)) {
+    SetCtrl(c, index, ctrl_t::kEmpty, slot_size);
+    c.set_growth_left(c.growth_left() + 1);
+    return;
+  }
+
+  SetCtrl(c, index, ctrl_t::kDeleted, slot_size);
 }
 
 void ClearBackingArray(CommonFields& c, const PolicyFunctions& policy,
@@ -245,19 +257,124 @@
   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
+    // infoz.
+    c.infoz().RecordClearedReservation();
+    c.infoz().RecordStorageChanged(0, 0);
     (*policy.dealloc)(c, policy);
     c.set_control(EmptyGroup());
     c.set_generation_ptr(EmptyGeneration());
     c.set_slots(nullptr);
     c.set_capacity(0);
-    c.infoz().RecordClearedReservation();
-    assert(c.size() == 0);
-    c.infoz().RecordStorageChanged(0, 0);
   }
 }
 
+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 5f89d8e..3518bc3 100644
--- a/absl/container/internal/raw_hash_set.h
+++ b/absl/container/internal/raw_hash_set.h
@@ -62,6 +62,9 @@
 // pseudo-struct:
 //
 //   struct BackingArray {
+//     // Sampling handler. This field isn't present when the sampling is
+//     // disabled or this allocation hasn't been selected for sampling.
+//     HashtablezInfoHandle infoz_;
 //     // The number of elements we can insert before growing the capacity.
 //     size_t growth_left;
 //     // Control bytes for the "real" slots.
@@ -175,25 +178,29 @@
 #define ABSL_CONTAINER_INTERNAL_RAW_HASH_SET_H_
 
 #include <algorithm>
+#include <cassert>
 #include <cmath>
 #include <cstddef>
 #include <cstdint>
 #include <cstring>
+#include <initializer_list>
 #include <iterator>
 #include <limits>
 #include <memory>
-#include <string>
 #include <tuple>
 #include <type_traits>
 #include <utility>
 
+#include "absl/base/attributes.h"
 #include "absl/base/config.h"
 #include "absl/base/internal/endian.h"
 #include "absl/base/internal/raw_logging.h"
+#include "absl/base/macros.h"
 #include "absl/base/optimization.h"
+#include "absl/base/options.h"
 #include "absl/base/port.h"
 #include "absl/base/prefetch.h"
-#include "absl/container/internal/common.h"
+#include "absl/container/internal/common.h"  // IWYU pragma: export // for node_handle
 #include "absl/container/internal/compressed_tuple.h"
 #include "absl/container/internal/container_memory.h"
 #include "absl/container/internal/hash_policy_traits.h"
@@ -227,6 +234,7 @@
 #ifdef ABSL_SWISSTABLE_ENABLE_GENERATIONS
 #error ABSL_SWISSTABLE_ENABLE_GENERATIONS cannot be directly set
 #elif defined(ABSL_HAVE_ADDRESS_SANITIZER) || \
+    defined(ABSL_HAVE_HWADDRESS_SANITIZER) || \
     defined(ABSL_HAVE_MEMORY_SANITIZER)
 // When compiled in sanitizer mode, we add generation integers to the backing
 // array and iterators. In the backing array, we store the generation between
@@ -262,8 +270,21 @@
   swap(lhs, rhs);
 }
 template <typename AllocType>
-void SwapAlloc(AllocType& /*lhs*/, AllocType& /*rhs*/,
-               std::false_type /* propagate_on_container_swap */) {}
+void SwapAlloc(AllocType& lhs, AllocType& rhs,
+               std::false_type /* propagate_on_container_swap */) {
+  (void)lhs;
+  (void)rhs;
+  assert(lhs == rhs &&
+         "It's UB to call swap with unequal non-propagating allocators.");
+}
+
+template <typename AllocType>
+void CopyAlloc(AllocType& lhs, AllocType& rhs,
+               std::true_type /* propagate_alloc */) {
+  lhs = rhs;
+}
+template <typename AllocType>
+void CopyAlloc(AllocType&, AllocType&, std::false_type /* propagate_alloc */) {}
 
 // The state for a probe sequence.
 //
@@ -361,7 +382,7 @@
 // width of an abstract bit in the representation.
 // This mask provides operations for any number of real bits set in an abstract
 // bit. To add iteration on top of that, implementation must guarantee no more
-// than one real bit is set in an abstract bit.
+// than the most significant real bit is set in a set abstract bit.
 template <class T, int SignificantBits, int Shift = 0>
 class NonIterableBitMask {
  public:
@@ -388,7 +409,9 @@
   uint32_t LeadingZeros() const {
     constexpr int total_significant_bits = SignificantBits << Shift;
     constexpr int extra_bits = sizeof(T) * 8 - total_significant_bits;
-    return static_cast<uint32_t>(countl_zero(mask_ << extra_bits)) >> Shift;
+    return static_cast<uint32_t>(
+               countl_zero(static_cast<T>(mask_ << extra_bits))) >>
+           Shift;
   }
 
   T mask_;
@@ -418,6 +441,10 @@
   using const_iterator = BitMask;
 
   BitMask& operator++() {
+    if (Shift == 3) {
+      constexpr uint64_t msbs = 0x8080808080808080ULL;
+      this->mask_ &= msbs;
+    }
     this->mask_ &= (this->mask_ - 1);
     return *this;
   }
@@ -590,29 +617,39 @@
   }
 
   // Returns a bitmask representing the positions of slots that match hash.
-  BitMask<uint32_t, kWidth> Match(h2_t hash) const {
+  BitMask<uint16_t, kWidth> Match(h2_t hash) const {
     auto match = _mm_set1_epi8(static_cast<char>(hash));
-    return BitMask<uint32_t, kWidth>(
-        static_cast<uint32_t>(_mm_movemask_epi8(_mm_cmpeq_epi8(match, ctrl))));
+    BitMask<uint16_t, kWidth> result = BitMask<uint16_t, kWidth>(0);
+    result = BitMask<uint16_t, kWidth>(
+        static_cast<uint16_t>(_mm_movemask_epi8(_mm_cmpeq_epi8(match, ctrl))));
+    return result;
   }
 
   // Returns a bitmask representing the positions of empty slots.
-  NonIterableBitMask<uint32_t, kWidth> MaskEmpty() const {
+  NonIterableBitMask<uint16_t, kWidth> MaskEmpty() const {
 #ifdef ABSL_INTERNAL_HAVE_SSSE3
     // This only works because ctrl_t::kEmpty is -128.
-    return NonIterableBitMask<uint32_t, kWidth>(
-        static_cast<uint32_t>(_mm_movemask_epi8(_mm_sign_epi8(ctrl, ctrl))));
+    return NonIterableBitMask<uint16_t, kWidth>(
+        static_cast<uint16_t>(_mm_movemask_epi8(_mm_sign_epi8(ctrl, ctrl))));
 #else
     auto match = _mm_set1_epi8(static_cast<char>(ctrl_t::kEmpty));
-    return NonIterableBitMask<uint32_t, kWidth>(
-        static_cast<uint32_t>(_mm_movemask_epi8(_mm_cmpeq_epi8(match, ctrl))));
+    return NonIterableBitMask<uint16_t, kWidth>(
+        static_cast<uint16_t>(_mm_movemask_epi8(_mm_cmpeq_epi8(match, ctrl))));
 #endif
   }
 
+  // Returns a bitmask representing the positions of full slots.
+  // Note: for `is_small()` tables group may contain the "same" slot twice:
+  // original and mirrored.
+  BitMask<uint16_t, kWidth> MaskFull() const {
+    return BitMask<uint16_t, kWidth>(
+        static_cast<uint16_t>(_mm_movemask_epi8(ctrl) ^ 0xffff));
+  }
+
   // Returns a bitmask representing the positions of empty or deleted slots.
-  NonIterableBitMask<uint32_t, kWidth> MaskEmptyOrDeleted() const {
+  NonIterableBitMask<uint16_t, kWidth> MaskEmptyOrDeleted() const {
     auto special = _mm_set1_epi8(static_cast<char>(ctrl_t::kSentinel));
-    return NonIterableBitMask<uint32_t, kWidth>(static_cast<uint32_t>(
+    return NonIterableBitMask<uint16_t, kWidth>(static_cast<uint16_t>(
         _mm_movemask_epi8(_mm_cmpgt_epi8_fixed(special, ctrl))));
   }
 
@@ -651,9 +688,8 @@
   BitMask<uint64_t, kWidth, 3> Match(h2_t hash) const {
     uint8x8_t dup = vdup_n_u8(hash);
     auto mask = vceq_u8(ctrl, dup);
-    constexpr uint64_t msbs = 0x8080808080808080ULL;
     return BitMask<uint64_t, kWidth, 3>(
-        vget_lane_u64(vreinterpret_u64_u8(mask), 0) & msbs);
+        vget_lane_u64(vreinterpret_u64_u8(mask), 0));
   }
 
   NonIterableBitMask<uint64_t, kWidth, 3> MaskEmpty() const {
@@ -665,6 +701,17 @@
     return NonIterableBitMask<uint64_t, kWidth, 3>(mask);
   }
 
+  // Returns a bitmask representing the positions of full slots.
+  // Note: for `is_small()` tables group may contain the "same" slot twice:
+  // original and mirrored.
+  BitMask<uint64_t, kWidth, 3> MaskFull() const {
+    uint64_t mask = vget_lane_u64(
+        vreinterpret_u64_u8(vcge_s8(vreinterpret_s8_u8(ctrl),
+                                    vdup_n_s8(static_cast<int8_t>(0)))),
+        0);
+    return BitMask<uint64_t, kWidth, 3>(mask);
+  }
+
   NonIterableBitMask<uint64_t, kWidth, 3> MaskEmptyOrDeleted() const {
     uint64_t mask =
         vget_lane_u64(vreinterpret_u64_u8(vcgt_s8(
@@ -729,13 +776,21 @@
 
   NonIterableBitMask<uint64_t, kWidth, 3> MaskEmpty() const {
     constexpr uint64_t msbs = 0x8080808080808080ULL;
-    return NonIterableBitMask<uint64_t, kWidth, 3>((ctrl & (~ctrl << 6)) &
+    return NonIterableBitMask<uint64_t, kWidth, 3>((ctrl & ~(ctrl << 6)) &
                                                    msbs);
   }
 
+  // Returns a bitmask representing the positions of full slots.
+  // Note: for `is_small()` tables group may contain the "same" slot twice:
+  // original and mirrored.
+  BitMask<uint64_t, kWidth, 3> MaskFull() const {
+    constexpr uint64_t msbs = 0x8080808080808080ULL;
+    return BitMask<uint64_t, kWidth, 3>((ctrl ^ msbs) & msbs);
+  }
+
   NonIterableBitMask<uint64_t, kWidth, 3> MaskEmptyOrDeleted() const {
     constexpr uint64_t msbs = 0x8080808080808080ULL;
-    return NonIterableBitMask<uint64_t, kWidth, 3>((ctrl & (~ctrl << 7)) &
+    return NonIterableBitMask<uint64_t, kWidth, 3>((ctrl & ~(ctrl << 7)) &
                                                    msbs);
   }
 
@@ -760,10 +815,21 @@
 
 #ifdef ABSL_INTERNAL_HAVE_SSE2
 using Group = GroupSse2Impl;
+using GroupEmptyOrDeleted = GroupSse2Impl;
 #elif defined(ABSL_INTERNAL_HAVE_ARM_NEON) && defined(ABSL_IS_LITTLE_ENDIAN)
 using Group = GroupAArch64Impl;
+// For Aarch64, we use the portable implementation for counting and masking
+// empty or deleted group elements. This is to avoid the latency of moving
+// between data GPRs and Neon registers when it does not provide a benefit.
+// Using Neon is profitable when we call Match(), but is not when we don't,
+// which is the case when we do *EmptyOrDeleted operations. It is difficult to
+// make a similar approach beneficial on other architectures such as x86 since
+// they have much lower GPR <-> vector register transfer latency and 16-wide
+// Groups.
+using GroupEmptyOrDeleted = GroupPortableImpl;
 #else
 using Group = GroupPortableImpl;
+using GroupEmptyOrDeleted = GroupPortableImpl;
 #endif
 
 // When there is an insertion with no reserved growth, we rehash with
@@ -802,15 +868,19 @@
   // whenever reserved_growth_ is zero.
   bool should_rehash_for_bug_detection_on_insert(const ctrl_t* ctrl,
                                                  size_t capacity) const;
+  // Similar to above, except that we don't depend on reserved_growth_.
+  bool should_rehash_for_bug_detection_on_move(const ctrl_t* ctrl,
+                                               size_t capacity) const;
   void maybe_increment_generation_on_insert() {
     if (reserved_growth_ == kReservedGrowthJustRanOut) reserved_growth_ = 0;
 
     if (reserved_growth_ > 0) {
       if (--reserved_growth_ == 0) reserved_growth_ = kReservedGrowthJustRanOut;
     } else {
-      *generation_ = NextGeneration(*generation_);
+      increment_generation();
     }
   }
+  void increment_generation() { *generation_ = NextGeneration(*generation_); }
   void reset_reserved_growth(size_t reservation, size_t size) {
     reserved_growth_ = reservation - size;
   }
@@ -856,7 +926,11 @@
   bool should_rehash_for_bug_detection_on_insert(const ctrl_t*, size_t) const {
     return false;
   }
+  bool should_rehash_for_bug_detection_on_move(const ctrl_t*, size_t) const {
+    return false;
+  }
   void maybe_increment_generation_on_insert() {}
+  void increment_generation() {}
   void reset_reserved_growth(size_t, size_t) {}
   size_t reserved_growth() const { return 0; }
   void set_reserved_growth(size_t) {}
@@ -909,9 +983,11 @@
 // A valid capacity is a non-zero integer `2^m - 1`.
 inline bool IsValidCapacity(size_t n) { return ((n + 1) & n) == 0 && n > 0; }
 
-// Computes the offset from the start of the backing allocation of the control
-// bytes. growth_left is stored at the beginning of the backing array.
-inline size_t ControlOffset() { return sizeof(size_t); }
+// Computes the offset from the start of the backing allocation of control.
+// infoz and growth_left are stored at the beginning of the backing array.
+inline size_t ControlOffset(bool has_infoz) {
+  return (has_infoz ? sizeof(HashtablezInfoHandle) : 0) + sizeof(size_t);
+}
 
 // Returns the number of "cloned control bytes".
 //
@@ -922,24 +998,26 @@
 
 // Given the capacity of a table, computes the offset (from the start of the
 // backing allocation) of the generation counter (if it exists).
-inline size_t GenerationOffset(size_t capacity) {
+inline size_t GenerationOffset(size_t capacity, bool has_infoz) {
   assert(IsValidCapacity(capacity));
   const size_t num_control_bytes = capacity + 1 + NumClonedBytes();
-  return ControlOffset() + num_control_bytes;
+  return ControlOffset(has_infoz) + num_control_bytes;
 }
 
 // Given the capacity of a table, computes the offset (from the start of the
 // backing allocation) at which the slots begin.
-inline size_t SlotOffset(size_t capacity, size_t slot_align) {
+inline size_t SlotOffset(size_t capacity, size_t slot_align, bool has_infoz) {
   assert(IsValidCapacity(capacity));
-  return (GenerationOffset(capacity) + NumGenerationBytes() + slot_align - 1) &
+  return (GenerationOffset(capacity, has_infoz) + NumGenerationBytes() +
+          slot_align - 1) &
          (~slot_align + 1);
 }
 
 // Given the capacity of a table, computes the total size of the backing
 // array.
-inline size_t AllocSize(size_t capacity, size_t slot_size, size_t slot_align) {
-  return SlotOffset(capacity, slot_align) + capacity * slot_size;
+inline size_t AllocSize(size_t capacity, size_t slot_size, size_t slot_align,
+                        bool has_infoz) {
+  return SlotOffset(capacity, slot_align, has_infoz) + capacity * slot_size;
 }
 
 // CommonFields hold the fields in raw_hash_set that do not depend
@@ -954,28 +1032,15 @@
   CommonFields& operator=(const CommonFields&) = delete;
 
   // Movable
-  CommonFields(CommonFields&& that)
-      : CommonFieldsGenerationInfo(
-            std::move(static_cast<CommonFieldsGenerationInfo&&>(that))),
-        // Explicitly copying fields into "this" and then resetting "that"
-        // fields generates less code then calling absl::exchange per field.
-        control_(that.control()),
-        slots_(that.slot_array()),
-        capacity_(that.capacity()),
-        compressed_tuple_(that.size(), std::move(that.infoz())) {
-    that.set_control(EmptyGroup());
-    that.set_slots(nullptr);
-    that.set_capacity(0);
-    that.set_size(0);
-  }
+  CommonFields(CommonFields&& that) = default;
   CommonFields& operator=(CommonFields&&) = default;
 
   ctrl_t* control() const { return control_; }
   void set_control(ctrl_t* c) { control_ = c; }
   void* backing_array_start() const {
-    // growth_left is stored before control bytes.
+    // growth_left (and maybe infoz) is stored before control bytes.
     assert(reinterpret_cast<uintptr_t>(control()) % alignof(size_t) == 0);
-    return control() - sizeof(size_t);
+    return control() - ControlOffset(has_infoz());
   }
 
   // Note: we can't use slots() because Qt defines "slots" as a macro.
@@ -983,8 +1048,18 @@
   void set_slots(void* s) { slots_ = s; }
 
   // The number of filled slots.
-  size_t size() const { return compressed_tuple_.template get<0>(); }
-  void set_size(size_t s) { compressed_tuple_.template get<0>() = s; }
+  size_t size() const { return size_ >> HasInfozShift(); }
+  void set_size(size_t s) {
+    size_ = (s << HasInfozShift()) | (size_ & HasInfozMask());
+  }
+  void increment_size() {
+    assert(size() < capacity());
+    size_ += size_t{1} << HasInfozShift();
+  }
+  void decrement_size() {
+    assert(size() > 0);
+    size_ -= size_t{1} << HasInfozShift();
+  }
 
   // The total number of available slots.
   size_t capacity() const { return capacity_; }
@@ -996,28 +1071,52 @@
   // The number of slots we can still fill without needing to rehash.
   // This is stored in the heap allocation before the control bytes.
   size_t growth_left() const {
-    return *reinterpret_cast<size_t*>(backing_array_start());
+    const size_t* gl_ptr = reinterpret_cast<size_t*>(control()) - 1;
+    assert(reinterpret_cast<uintptr_t>(gl_ptr) % alignof(size_t) == 0);
+    return *gl_ptr;
   }
   void set_growth_left(size_t gl) {
-    *reinterpret_cast<size_t*>(backing_array_start()) = gl;
+    size_t* gl_ptr = reinterpret_cast<size_t*>(control()) - 1;
+    assert(reinterpret_cast<uintptr_t>(gl_ptr) % alignof(size_t) == 0);
+    *gl_ptr = gl;
   }
 
-  HashtablezInfoHandle& infoz() { return compressed_tuple_.template get<1>(); }
-  const HashtablezInfoHandle& infoz() const {
-    return compressed_tuple_.template get<1>();
+  bool has_infoz() const {
+    return ABSL_PREDICT_FALSE((size_ & HasInfozMask()) != 0);
+  }
+  void set_has_infoz(bool has_infoz) {
+    size_ = (size() << HasInfozShift()) | static_cast<size_t>(has_infoz);
+  }
+
+  HashtablezInfoHandle infoz() {
+    return has_infoz()
+               ? *reinterpret_cast<HashtablezInfoHandle*>(backing_array_start())
+               : HashtablezInfoHandle();
+  }
+  void set_infoz(HashtablezInfoHandle infoz) {
+    assert(has_infoz());
+    *reinterpret_cast<HashtablezInfoHandle*>(backing_array_start()) = infoz;
   }
 
   bool should_rehash_for_bug_detection_on_insert() const {
     return CommonFieldsGenerationInfo::
         should_rehash_for_bug_detection_on_insert(control(), capacity());
   }
+  bool should_rehash_for_bug_detection_on_move() const {
+    return CommonFieldsGenerationInfo::
+        should_rehash_for_bug_detection_on_move(control(), capacity());
+  }
+  void maybe_increment_generation_on_move() {
+    if (capacity() == 0) return;
+    increment_generation();
+  }
   void reset_reserved_growth(size_t reservation) {
     CommonFieldsGenerationInfo::reset_reserved_growth(reservation, size());
   }
 
   // The size of the backing array allocation.
   size_t alloc_size(size_t slot_size, size_t slot_align) const {
-    return AllocSize(capacity(), slot_size, slot_align);
+    return AllocSize(capacity(), slot_size, slot_align, has_infoz());
   }
 
   // Returns the number of control bytes set to kDeleted. For testing only.
@@ -1027,9 +1126,14 @@
   }
 
  private:
-  // TODO(b/259599413): Investigate removing some of these fields:
+  // We store the has_infoz bit in the lowest bit of size_.
+  static constexpr size_t HasInfozShift() { return 1; }
+  static constexpr size_t HasInfozMask() {
+    return (size_t{1} << HasInfozShift()) - 1;
+  }
+
+  // TODO(b/182800944): Investigate removing some of these fields:
   // - control/slots can be derived from each other
-  // - we can use 6 bits for capacity since it's always a power of two minus 1
 
   // The control bytes (and, also, a pointer near to the base of the backing
   // array).
@@ -1044,12 +1148,16 @@
   // `control`. May be null for empty tables.
   void* slots_ = nullptr;
 
+  // The number of slots in the backing array. This is always 2^N-1 for an
+  // integer N. NOTE: we tried experimenting with compressing the capacity and
+  // storing it together with size_: (a) using 6 bits to store the corresponding
+  // power (N in 2^N-1), and (b) storing 2^N as the most significant bit of
+  // size_ and storing size in the low bits. Both of these experiments were
+  // regressions, presumably because we need capacity to do find operations.
   size_t capacity_ = 0;
 
-  // Bundle together size and HashtablezInfoHandle to ensure EBO for
-  // HashtablezInfoHandle when sampling is turned off.
-  absl::container_internal::CompressedTuple<size_t, HashtablezInfoHandle>
-      compressed_tuple_{0u, HashtablezInfoHandle{}};
+  // The size and also has one bit that stores whether we have infoz.
+  size_t size_ = 0;
 };
 
 template <class Policy, class Hash, class Eq, class Alloc>
@@ -1139,35 +1247,39 @@
                          const GenerationType* generation_ptr,
                          const char* operation) {
   if (!SwisstableDebugEnabled()) return;
-  if (ctrl == nullptr) {
-    ABSL_INTERNAL_LOG(FATAL,
-                      std::string(operation) + " called on end() iterator.");
+  // `SwisstableDebugEnabled()` is also true for release builds with hardening
+  // enabled. To minimize their impact in those builds:
+  // - use `ABSL_PREDICT_FALSE()` to provide a compiler hint for code layout
+  // - use `ABSL_RAW_LOG()` with a format string to reduce code size and improve
+  //   the chances that the hot paths will be inlined.
+  if (ABSL_PREDICT_FALSE(ctrl == nullptr)) {
+    ABSL_RAW_LOG(FATAL, "%s called on end() iterator.", operation);
   }
-  if (ctrl == EmptyGroup()) {
-    ABSL_INTERNAL_LOG(FATAL, std::string(operation) +
-                                 " called on default-constructed iterator.");
+  if (ABSL_PREDICT_FALSE(ctrl == EmptyGroup())) {
+    ABSL_RAW_LOG(FATAL, "%s called on default-constructed iterator.",
+                 operation);
   }
   if (SwisstableGenerationsEnabled()) {
-    if (generation != *generation_ptr) {
-      ABSL_INTERNAL_LOG(FATAL,
-                        std::string(operation) +
-                            " called on invalid iterator. The table could have "
-                            "rehashed since this iterator was initialized.");
+    if (ABSL_PREDICT_FALSE(generation != *generation_ptr)) {
+      ABSL_RAW_LOG(FATAL,
+                   "%s called on invalid iterator. The table could have "
+                   "rehashed or moved since this iterator was initialized.",
+                   operation);
     }
-    if (!IsFull(*ctrl)) {
-      ABSL_INTERNAL_LOG(
+    if (ABSL_PREDICT_FALSE(!IsFull(*ctrl))) {
+      ABSL_RAW_LOG(
           FATAL,
-          std::string(operation) +
-              " called on invalid iterator. The element was likely erased.");
+          "%s called on invalid iterator. The element was likely erased.",
+          operation);
     }
   } else {
-    if (!IsFull(*ctrl)) {
-      ABSL_INTERNAL_LOG(
+    if (ABSL_PREDICT_FALSE(!IsFull(*ctrl))) {
+      ABSL_RAW_LOG(
           FATAL,
-          std::string(operation) +
-              " called on invalid iterator. The element might have been erased "
-              "or the table might have rehashed. Consider running with "
-              "--config=asan to diagnose rehashing issues.");
+          "%s called on invalid iterator. The element might have been erased "
+          "or the table might have rehashed. Consider running with "
+          "--config=asan to diagnose rehashing issues.",
+          operation);
     }
   }
 }
@@ -1180,13 +1292,13 @@
   const bool ctrl_is_valid_for_comparison =
       ctrl == nullptr || ctrl == EmptyGroup() || IsFull(*ctrl);
   if (SwisstableGenerationsEnabled()) {
-    if (generation != *generation_ptr) {
-      ABSL_INTERNAL_LOG(FATAL,
-                        "Invalid iterator comparison. The table could have "
-                        "rehashed since this iterator was initialized.");
+    if (ABSL_PREDICT_FALSE(generation != *generation_ptr)) {
+      ABSL_RAW_LOG(FATAL,
+                   "Invalid iterator comparison. The table could have rehashed "
+                   "or moved since this iterator was initialized.");
     }
-    if (!ctrl_is_valid_for_comparison) {
-      ABSL_INTERNAL_LOG(
+    if (ABSL_PREDICT_FALSE(!ctrl_is_valid_for_comparison)) {
+      ABSL_RAW_LOG(
           FATAL, "Invalid iterator comparison. The element was likely erased.");
     }
   } else {
@@ -1226,10 +1338,15 @@
                                 const GenerationType* generation_ptr_a,
                                 const GenerationType* generation_ptr_b) {
   if (!SwisstableDebugEnabled()) return;
+  // `SwisstableDebugEnabled()` is also true for release builds with hardening
+  // enabled. To minimize their impact in those builds:
+  // - use `ABSL_PREDICT_FALSE()` to provide a compiler hint for code layout
+  // - use `ABSL_RAW_LOG()` with a format string to reduce code size and improve
+  //   the chances that the hot paths will be inlined.
   const bool a_is_default = ctrl_a == EmptyGroup();
   const bool b_is_default = ctrl_b == EmptyGroup();
-  if (a_is_default != b_is_default) {
-    ABSL_INTERNAL_LOG(
+  if (ABSL_PREDICT_FALSE(a_is_default != b_is_default)) {
+    ABSL_RAW_LOG(
         FATAL,
         "Invalid iterator comparison. Comparing default-constructed iterator "
         "with non-default-constructed iterator.");
@@ -1237,36 +1354,36 @@
   if (a_is_default && b_is_default) return;
 
   if (SwisstableGenerationsEnabled()) {
-    if (generation_ptr_a == generation_ptr_b) return;
+    if (ABSL_PREDICT_TRUE(generation_ptr_a == generation_ptr_b)) return;
     const bool a_is_empty = IsEmptyGeneration(generation_ptr_a);
     const bool b_is_empty = IsEmptyGeneration(generation_ptr_b);
     if (a_is_empty != b_is_empty) {
-      ABSL_INTERNAL_LOG(FATAL,
-                        "Invalid iterator comparison. Comparing iterator from "
-                        "a non-empty hashtable with an iterator from an empty "
-                        "hashtable.");
+      ABSL_RAW_LOG(FATAL,
+                   "Invalid iterator comparison. Comparing iterator from a "
+                   "non-empty hashtable with an iterator from an empty "
+                   "hashtable.");
     }
     if (a_is_empty && b_is_empty) {
-      ABSL_INTERNAL_LOG(FATAL,
-                        "Invalid iterator comparison. Comparing iterators from "
-                        "different empty hashtables.");
+      ABSL_RAW_LOG(FATAL,
+                   "Invalid iterator comparison. Comparing iterators from "
+                   "different empty hashtables.");
     }
     const bool a_is_end = ctrl_a == nullptr;
     const bool b_is_end = ctrl_b == nullptr;
     if (a_is_end || b_is_end) {
-      ABSL_INTERNAL_LOG(FATAL,
-                        "Invalid iterator comparison. Comparing iterator with "
-                        "an end() iterator from a different hashtable.");
+      ABSL_RAW_LOG(FATAL,
+                   "Invalid iterator comparison. Comparing iterator with an "
+                   "end() iterator from a different hashtable.");
     }
-    ABSL_INTERNAL_LOG(FATAL,
-                      "Invalid iterator comparison. Comparing non-end() "
-                      "iterators from different hashtables.");
+    ABSL_RAW_LOG(FATAL,
+                 "Invalid iterator comparison. Comparing non-end() iterators "
+                 "from different hashtables.");
   } else {
     ABSL_HARDENING_ASSERT(
         AreItersFromSameContainer(ctrl_a, ctrl_b, slot_a, slot_b) &&
         "Invalid iterator comparison. The iterators may be from different "
-        "containers or the container might have rehashed. Consider running "
-        "with --config=asan to diagnose rehashing issues.");
+        "containers or the container might have rehashed or moved. Consider "
+        "running with --config=asan to diagnose issues.");
   }
 }
 
@@ -1289,6 +1406,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) {
@@ -1310,7 +1433,7 @@
   auto seq = probe(common, hash);
   const ctrl_t* ctrl = common.control();
   while (true) {
-    Group g{ctrl + seq.offset()};
+    GroupEmptyOrDeleted g{ctrl + seq.offset()};
     auto mask = g.MaskEmptyOrDeleted();
     if (mask) {
 #if !defined(NDEBUG)
@@ -1351,7 +1474,6 @@
               capacity + 1 + NumClonedBytes());
   ctrl[capacity] = ctrl_t::kSentinel;
   SanitizerPoisonMemoryRegion(common.slot_array(), slot_size * capacity);
-  ResetGrowthLeft(common);
 }
 
 // Sets `ctrl[i]` to `h`.
@@ -1386,38 +1508,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;
-
-  const size_t cap = c.capacity();
-  const size_t alloc_size = AllocSize(cap, SizeOfSlot, AlignOfSlot);
-  // growth_left (which is a size_t) is stored with the backing array.
-  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)));
-  c.set_generation(NextGeneration(old_generation));
-  c.set_control(reinterpret_cast<ctrl_t*>(mem + ControlOffset()));
-  c.set_slots(mem + SlotOffset(cap, AlignOfSlot));
-  ResetCtrl(c, SizeOfSlot);
-  if (sample_size) {
-    c.infoz() = Sample(sample_size);
-  }
-  c.infoz().RecordStorageChanged(c.size(), cap);
+// 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
@@ -1442,7 +1789,7 @@
                        bool reuse);
 
 // Type-erased version of raw_hash_set::erase_meta_only.
-void EraseMetaOnly(CommonFields& c, ctrl_t* it, size_t slot_size);
+void EraseMetaOnly(CommonFields& c, size_t index, size_t slot_size);
 
 // Function to place in PolicyFunctions::dealloc for raw_hash_sets
 // that are using std::allocator. This allows us to share the same
@@ -1456,6 +1803,7 @@
                                 policy.slot_size * common.capacity());
 
   std::allocator<char> alloc;
+  common.infoz().Unregister();
   Deallocate<BackingArrayAlignment(AlignOfSlot)>(
       &alloc, common.backing_array_start(),
       common.alloc_size(policy.slot_size, AlignOfSlot));
@@ -1534,6 +1882,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>;
 
@@ -1590,7 +1943,7 @@
     // PRECONDITION: not an end() iterator.
     reference operator*() const {
       AssertIsFull(ctrl_, generation(), generation_ptr(), "operator*()");
-      return PolicyTraits::element(slot_);
+      return unchecked_deref();
     }
 
     // PRECONDITION: not an end() iterator.
@@ -1645,13 +1998,17 @@
     // If a sentinel is reached, we null `ctrl_` out instead.
     void skip_empty_or_deleted() {
       while (IsEmptyOrDeleted(*ctrl_)) {
-        uint32_t shift = Group{ctrl_}.CountLeadingEmptyOrDeleted();
+        uint32_t shift =
+            GroupEmptyOrDeleted{ctrl_}.CountLeadingEmptyOrDeleted();
         ctrl_ += shift;
         slot_ += shift;
       }
       if (ABSL_PREDICT_FALSE(*ctrl_ == ctrl_t::kSentinel)) ctrl_ = nullptr;
     }
 
+    ctrl_t* control() const { return ctrl_; }
+    slot_type* slot() const { return slot_; }
+
     // We use EmptyGroup() for default-constructed iterators so that they can
     // be distinguished from end iterators, which have nullptr ctrl_.
     ctrl_t* ctrl_ = EmptyGroup();
@@ -1660,10 +2017,23 @@
     union {
       slot_type* slot_;
     };
+
+    // An equality check which skips ABSL Hardening iterator invalidation
+    // checks.
+    // Should be used when the lifetimes of the iterators are well-enough
+    // understood to prove that they cannot be invalid.
+    bool unchecked_equals(const iterator& b) { return ctrl_ == b.control(); }
+
+    // Dereferences the iterator without ABSL Hardening iterator invalidation
+    // checks.
+    reference unchecked_deref() const { return PolicyTraits::element(slot_); }
   };
 
   class const_iterator {
     friend class raw_hash_set;
+    template <class Container, typename Enabler>
+    friend struct absl::container_internal::hashtable_debug_internal::
+        HashtableDebugAccess;
 
    public:
     using iterator_category = typename iterator::iterator_category;
@@ -1697,8 +2067,14 @@
                    const GenerationType* gen)
         : inner_(const_cast<ctrl_t*>(ctrl), const_cast<slot_type*>(slot), gen) {
     }
+    ctrl_t* control() const { return inner_.control(); }
+    slot_type* slot() const { return inner_.slot(); }
 
     iterator inner_;
+
+    bool unchecked_equals(const const_iterator& b) {
+      return inner_.unchecked_equals(b.inner_);
+    }
   };
 
   using node_type = node_handle<Policy, hash_policy_traits<Policy>, Alloc>;
@@ -1717,8 +2093,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));
     }
   }
 
@@ -1843,28 +2218,35 @@
       :  // Hash, equality and allocator are copied instead of moved because
          // `that` must be left valid. If Hash is std::function<Key>, moving it
          // would create a nullptr functor that cannot be called.
-        settings_(absl::exchange(that.common(), CommonFields{}),
-                  that.hash_ref(), that.eq_ref(), that.alloc_ref()) {}
+         // TODO(b/296061262): move instead of copying hash/eq/alloc.
+         // Note: we avoid using exchange for better generated code.
+        settings_(std::move(that.common()), that.hash_ref(), that.eq_ref(),
+                  that.alloc_ref()) {
+    that.common() = CommonFields{};
+    maybe_increment_generation_or_rehash_on_move();
+  }
 
   raw_hash_set(raw_hash_set&& that, const allocator_type& a)
       : settings_(CommonFields{}, that.hash_ref(), that.eq_ref(), a) {
     if (a == that.alloc_ref()) {
       std::swap(common(), that.common());
+      maybe_increment_generation_or_rehash_on_move();
     } else {
-      reserve(that.size());
-      // Note: this will copy elements of dense_set and unordered_set instead of
-      // moving them. This can be fixed if it ever becomes an issue.
-      for (auto& elem : that) insert(std::move(elem));
+      move_elements_allocs_unequal(std::move(that));
     }
   }
 
   raw_hash_set& operator=(const raw_hash_set& that) {
-    raw_hash_set tmp(that,
-                     AllocTraits::propagate_on_container_copy_assignment::value
-                         ? that.alloc_ref()
-                         : alloc_ref());
-    swap(tmp);
-    return *this;
+    if (ABSL_PREDICT_FALSE(this == &that)) return *this;
+    constexpr bool propagate_alloc =
+        AllocTraits::propagate_on_container_copy_assignment::value;
+    // TODO(ezb): maybe avoid allocating a new backing array if this->capacity()
+    // is an exact match for that.size(). If this->capacity() is too big, then
+    // it would make iteration very slow to reuse the allocation. Maybe we can
+    // do the same heuristic as clear() and reuse if it's small enough.
+    raw_hash_set tmp(that, propagate_alloc ? that.alloc_ref() : alloc_ref());
+    // NOLINTNEXTLINE: not returning *this for performance.
+    return assign_impl<propagate_alloc>(std::move(tmp));
   }
 
   raw_hash_set& operator=(raw_hash_set&& that) noexcept(
@@ -1879,19 +2261,7 @@
         typename AllocTraits::propagate_on_container_move_assignment());
   }
 
-  ~raw_hash_set() {
-    const size_t cap = capacity();
-    if (!cap) return;
-    destroy_slots();
-
-    // Unpoison before returning the memory to the allocator.
-    SanitizerUnpoisonMemoryRegion(slot_array(), sizeof(slot_type) * cap);
-    Deallocate<BackingArrayAlignment(alignof(slot_type))>(
-        &alloc_ref(), common().backing_array_start(),
-        AllocSize(cap, sizeof(slot_type), alignof(slot_type)));
-
-    infoz().Unregister();
-  }
+  ~raw_hash_set() { destructor_impl(); }
 
   iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND {
     auto it = iterator_at(0);
@@ -1937,17 +2307,6 @@
     common().set_reservation_size(0);
   }
 
-  inline void destroy_slots() {
-    const size_t cap = capacity();
-    const ctrl_t* ctrl = control();
-    slot_type* slot = slot_array();
-    for (size_t i = 0; i != cap; ++i) {
-      if (IsFull(ctrl[i])) {
-        PolicyTraits::destroy(&alloc_ref(), slot + i);
-      }
-    }
-  }
-
   // This overload kicks in when the argument is an rvalue of insertable and
   // decomposable type other than init_type.
   //
@@ -2075,7 +2434,7 @@
     alignas(slot_type) unsigned char raw[sizeof(slot_type)];
     slot_type* slot = reinterpret_cast<slot_type*>(&raw);
 
-    PolicyTraits::construct(&alloc_ref(), slot, std::forward<Args>(args)...);
+    construct(slot, std::forward<Args>(args)...);
     const auto& elem = PolicyTraits::element(slot);
     return PolicyTraits::apply(InsertSlot<true>{*this, std::move(*slot)}, elem);
   }
@@ -2179,8 +2538,8 @@
   // This overload is necessary because otherwise erase<K>(const K&) would be
   // a better match if non-const iterator is passed as an argument.
   void erase(iterator it) {
-    AssertIsFull(it.ctrl_, it.generation(), it.generation_ptr(), "erase()");
-    PolicyTraits::destroy(&alloc_ref(), it.slot_);
+    AssertIsFull(it.control(), it.generation(), it.generation_ptr(), "erase()");
+    destroy(it.slot());
     erase_meta_only(it);
   }
 
@@ -2211,8 +2570,8 @@
     assert(this != &src);
     for (auto it = src.begin(), e = src.end(); it != e;) {
       auto next = std::next(it);
-      if (PolicyTraits::apply(InsertSlot<false>{*this, std::move(*it.slot_)},
-                              PolicyTraits::element(it.slot_))
+      if (PolicyTraits::apply(InsertSlot<false>{*this, std::move(*it.slot())},
+                              PolicyTraits::element(it.slot()))
               .second) {
         src.erase_meta_only(it);
       }
@@ -2226,10 +2585,9 @@
   }
 
   node_type extract(const_iterator position) {
-    AssertIsFull(position.inner_.ctrl_, position.inner_.generation(),
+    AssertIsFull(position.control(), position.inner_.generation(),
                  position.inner_.generation_ptr(), "extract()");
-    auto node =
-        CommonAccess::Transfer<node_type>(alloc_ref(), position.inner_.slot_);
+    auto node = CommonAccess::Transfer<node_type>(alloc_ref(), position.slot());
     erase_meta_only(position);
     return node;
   }
@@ -2364,7 +2722,11 @@
 
   template <class K = key_type>
   bool contains(const key_arg<K>& key) const {
-    return find(key) != end();
+    // Here neither the iterator returned by `find()` nor `end()` can be invalid
+    // outside of potential thread-safety issues.
+    // `find()`'s return value is constructed, used, and then destructed
+    // all in this context.
+    return !find(key).unchecked_equals(end());
   }
 
   template <class K = key_type>
@@ -2400,8 +2762,10 @@
     const raw_hash_set* outer = &a;
     const raw_hash_set* inner = &b;
     if (outer->capacity() > inner->capacity()) std::swap(outer, inner);
-    for (const value_type& elem : *outer)
-      if (!inner->has_element(elem)) return false;
+    for (const value_type& elem : *outer) {
+      auto it = PolicyTraits::apply(FindElement{*inner}, elem);
+      if (it == inner->end() || !(*it == elem)) return false;
+    }
     return true;
   }
 
@@ -2471,10 +2835,9 @@
     std::pair<iterator, bool> operator()(const K& key, Args&&...) && {
       auto res = s.find_or_prepare_insert(key);
       if (res.second) {
-        PolicyTraits::transfer(&s.alloc_ref(), s.slot_array() + res.first,
-                               &slot);
+        s.transfer(s.slot_array() + res.first, &slot);
       } else if (do_destroy) {
-        PolicyTraits::destroy(&s.alloc_ref(), &slot);
+        s.destroy(&slot);
       }
       return {s.iterator_at(res.first), res.second};
     }
@@ -2483,58 +2846,111 @@
     slot_type&& slot;
   };
 
+  // TODO(b/303305702): re-enable reentrant validation.
+  template <typename... Args>
+  inline void construct(slot_type* slot, Args&&... args) {
+    PolicyTraits::construct(&alloc_ref(), slot, std::forward<Args>(args)...);
+  }
+  inline void destroy(slot_type* slot) {
+    PolicyTraits::destroy(&alloc_ref(), slot);
+  }
+  inline void transfer(slot_type* to, slot_type* from) {
+    PolicyTraits::transfer(&alloc_ref(), to, from);
+  }
+
+  inline void destroy_slots() {
+    const size_t cap = capacity();
+    const ctrl_t* ctrl = control();
+    slot_type* slot = slot_array();
+    for (size_t i = 0; i != cap; ++i) {
+      if (IsFull(ctrl[i])) {
+        destroy(slot + i);
+      }
+    }
+  }
+
+  inline void dealloc() {
+    assert(capacity() != 0);
+    // Unpoison before returning the memory to the allocator.
+    SanitizerUnpoisonMemoryRegion(slot_array(), sizeof(slot_type) * capacity());
+    infoz().Unregister();
+    Deallocate<BackingArrayAlignment(alignof(slot_type))>(
+        &alloc_ref(), common().backing_array_start(),
+        common().alloc_size(sizeof(slot_type), alignof(slot_type)));
+  }
+
+  inline void destructor_impl() {
+    if (capacity() == 0) return;
+    destroy_slots();
+    dealloc();
+  }
+
   // Erases, but does not destroy, the value pointed to by `it`.
   //
   // This merely updates the pertinent control byte. This can be used in
   // conjunction with Policy::transfer to move the object to another place.
   void erase_meta_only(const_iterator it) {
-    EraseMetaOnly(common(), it.inner_.ctrl_, sizeof(slot_type));
+    EraseMetaOnly(common(), static_cast<size_t>(it.control() - 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 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));
-        PolicyTraits::transfer(&alloc_ref(), 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(),
-          AllocSize(old_capacity, sizeof(slot_type), alignof(slot_type)));
-    }
-    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.
@@ -2604,36 +3020,64 @@
     }
   }
 
-  bool has_element(const value_type& elem) const {
-    size_t hash = PolicyTraits::apply(HashElement{hash_ref()}, elem);
-    auto seq = probe(common(), hash);
-    const ctrl_t* ctrl = control();
-    while (true) {
-      Group g{ctrl + seq.offset()};
-      for (uint32_t i : g.Match(H2(hash))) {
-        if (ABSL_PREDICT_TRUE(
-                PolicyTraits::element(slot_array() + seq.offset(i)) == elem))
-          return true;
-      }
-      if (ABSL_PREDICT_TRUE(g.MaskEmpty())) return false;
-      seq.next();
-      assert(seq.index() <= capacity() && "full table!");
+  void maybe_increment_generation_or_rehash_on_move() {
+    common().maybe_increment_generation_on_move();
+    if (!empty() && common().should_rehash_for_bug_detection_on_move()) {
+      resize(capacity());
     }
-    return false;
   }
 
-  // TODO(alkis): Optimize this assuming *this and that don't overlap.
-  raw_hash_set& move_assign(raw_hash_set&& that, std::true_type) {
-    raw_hash_set tmp(std::move(that));
-    swap(tmp);
+  template<bool propagate_alloc>
+  raw_hash_set& assign_impl(raw_hash_set&& that) {
+    // We don't bother checking for this/that aliasing. We just need to avoid
+    // breaking the invariants in that case.
+    destructor_impl();
+    common() = std::move(that.common());
+    // TODO(b/296061262): move instead of copying hash/eq/alloc.
+    hash_ref() = that.hash_ref();
+    eq_ref() = that.eq_ref();
+    CopyAlloc(alloc_ref(), that.alloc_ref(),
+              std::integral_constant<bool, propagate_alloc>());
+    that.common() = CommonFields{};
+    maybe_increment_generation_or_rehash_on_move();
     return *this;
   }
-  raw_hash_set& move_assign(raw_hash_set&& that, std::false_type) {
-    raw_hash_set tmp(std::move(that), alloc_ref());
-    swap(tmp);
+
+  raw_hash_set& move_elements_allocs_unequal(raw_hash_set&& that) {
+    const size_t size = that.size();
+    if (size == 0) return *this;
+    reserve(size);
+    for (iterator it = that.begin(); it != that.end(); ++it) {
+      insert(std::move(PolicyTraits::element(it.slot())));
+      that.destroy(it.slot());
+    }
+    that.dealloc();
+    that.common() = CommonFields{};
+    maybe_increment_generation_or_rehash_on_move();
     return *this;
   }
 
+  raw_hash_set& move_assign(raw_hash_set&& that,
+                            std::true_type /*propagate_alloc*/) {
+    return assign_impl<true>(std::move(that));
+  }
+  raw_hash_set& move_assign(raw_hash_set&& that,
+                            std::false_type /*propagate_alloc*/) {
+    if (alloc_ref() == that.alloc_ref()) {
+      return assign_impl<false>(std::move(that));
+    }
+    // Aliasing can't happen here because allocs would compare equal above.
+    assert(this != &that);
+    destructor_impl();
+    // We can't take over that's memory so we need to move each element.
+    // While moving elements, this should have that's hash/eq so copy hash/eq
+    // before moving elements.
+    // TODO(b/296061262): move instead of copying hash/eq.
+    hash_ref() = that.hash_ref();
+    eq_ref() = that.eq_ref();
+    return move_elements_allocs_unequal(std::move(that));
+  }
+
  protected:
   // Attempts to find `key` in the table; if it isn't found, returns a slot that
   // the value can be inserted into, with the control byte already set to
@@ -2675,10 +3119,19 @@
     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().set_size(common().size() + 1);
+    common().increment_size();
     set_growth_left(growth_left() - IsEmpty(control()[target.offset]));
     SetCtrl(common(), target.offset, H2(hash), sizeof(slot_type));
     common().maybe_increment_generation_on_insert();
@@ -2696,8 +3149,7 @@
   // POSTCONDITION: *m.iterator_at(i) == value_type(forward<Args>(args)...).
   template <class... Args>
   void emplace_at(size_t i, Args&&... args) {
-    PolicyTraits::construct(&alloc_ref(), slot_array() + i,
-                            std::forward<Args>(args)...);
+    construct(slot_array() + i, std::forward<Args>(args)...);
 
     assert(PolicyTraits::apply(FindElement{*this}, *iterator_at(i)) ==
                iterator_at(i) &&
@@ -2711,6 +3163,8 @@
     return {control() + i, slot_array() + i, common().generation_ptr()};
   }
 
+  reference unchecked_deref(iterator it) { return it.unchecked_deref(); }
+
  private:
   friend struct RawHashSetTestOnlyAccess;
 
@@ -2743,7 +3197,7 @@
   slot_type* slot_array() const {
     return static_cast<slot_type*>(common().slot_array());
   }
-  HashtablezInfoHandle& infoz() { return common().infoz(); }
+  HashtablezInfoHandle infoz() { return common().infoz(); }
 
   hasher& hash_ref() { return settings_.template get<1>(); }
   const hasher& hash_ref() const { return settings_.template get<1>(); }
@@ -2763,8 +3217,7 @@
   }
   static void transfer_slot_fn(void* set, void* dst, void* src) {
     auto* h = static_cast<raw_hash_set*>(set);
-    PolicyTraits::transfer(&h->alloc_ref(), static_cast<slot_type*>(dst),
-                           static_cast<slot_type*>(src));
+    h->transfer(static_cast<slot_type*>(dst), static_cast<slot_type*>(src));
   }
   // Note: dealloc_fn will only be used if we have a non-standard allocator.
   static void dealloc_fn(CommonFields& common, const PolicyFunctions&) {
@@ -2774,6 +3227,7 @@
     SanitizerUnpoisonMemoryRegion(common.slot_array(),
                                   sizeof(slot_type) * common.capacity());
 
+    common.infoz().Unregister();
     Deallocate<BackingArrayAlignment(alignof(slot_type))>(
         &set->alloc_ref(), common.backing_array_start(),
         common.alloc_size(sizeof(slot_type), alignof(slot_type)));
@@ -2847,33 +3301,18 @@
   static size_t AllocatedByteSize(const Set& c) {
     size_t capacity = c.capacity();
     if (capacity == 0) return 0;
-    size_t m = AllocSize(capacity, sizeof(Slot), alignof(Slot));
+    size_t m = c.common().alloc_size(sizeof(Slot), alignof(Slot));
 
     size_t per_slot = Traits::space_used(static_cast<const Slot*>(nullptr));
     if (per_slot != ~size_t{}) {
       m += per_slot * c.size();
     } else {
-      const ctrl_t* ctrl = c.control();
-      for (size_t i = 0; i != capacity; ++i) {
-        if (container_internal::IsFull(ctrl[i])) {
-          m += Traits::space_used(c.slot_array() + i);
-        }
+      for (auto it = c.begin(); it != c.end(); ++it) {
+        m += Traits::space_used(it.slot());
       }
     }
     return m;
   }
-
-  static size_t LowerBoundAllocatedByteSize(size_t size) {
-    size_t capacity = GrowthToLowerboundCapacity(size);
-    if (capacity == 0) return 0;
-    size_t m =
-        AllocSize(NormalizeCapacity(capacity), sizeof(Slot), alignof(Slot));
-    size_t per_slot = Traits::space_used(static_cast<const Slot*>(nullptr));
-    if (per_slot != ~size_t{}) {
-      m += per_slot * size;
-    }
-    return m;
-  }
 };
 
 }  // namespace hashtable_debug_internal
diff --git a/absl/container/internal/raw_hash_set_allocator_test.cc b/absl/container/internal/raw_hash_set_allocator_test.cc
index e73f53f..05dcfaa 100644
--- a/absl/container/internal/raw_hash_set_allocator_test.cc
+++ b/absl/container/internal/raw_hash_set_allocator_test.cc
@@ -12,10 +12,19 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include <cstddef>
+#include <cstdint>
+#include <functional>
 #include <limits>
-#include <scoped_allocator>
+#include <memory>
+#include <ostream>
+#include <set>
+#include <type_traits>
+#include <utility>
 
+#include "gmock/gmock.h"
 #include "gtest/gtest.h"
+#include "absl/base/config.h"
 #include "absl/container/internal/raw_hash_set.h"
 #include "absl/container/internal/tracked.h"
 
@@ -23,6 +32,7 @@
 ABSL_NAMESPACE_BEGIN
 namespace container_internal {
 namespace {
+using ::testing::AnyOf;
 
 enum AllocSpec {
   kPropagateOnCopy = 1,
@@ -276,34 +286,38 @@
 }
 
 TEST_F(PropagateOnAll, MoveConstructor) {
-  auto it = t1.insert(0).first;
+  t1.insert(0);
   Table u(std::move(t1));
-  EXPECT_EQ(1, a1.num_allocs());
-  EXPECT_EQ(0, it->num_moves());
+  auto it = u.begin();
+  EXPECT_THAT(a1.num_allocs(), AnyOf(1, 2));
+  EXPECT_THAT(it->num_moves(), AnyOf(0, 1));
   EXPECT_EQ(0, it->num_copies());
 }
 
 TEST_F(NoPropagateOnMove, MoveConstructor) {
-  auto it = t1.insert(0).first;
+  t1.insert(0);
   Table u(std::move(t1));
-  EXPECT_EQ(1, a1.num_allocs());
-  EXPECT_EQ(0, it->num_moves());
+  auto it = u.begin();
+  EXPECT_THAT(a1.num_allocs(), AnyOf(1, 2));
+  EXPECT_THAT(it->num_moves(), AnyOf(0, 1));
   EXPECT_EQ(0, it->num_copies());
 }
 
 TEST_F(PropagateOnAll, MoveConstructorWithSameAlloc) {
-  auto it = t1.insert(0).first;
+  t1.insert(0);
   Table u(std::move(t1), a1);
-  EXPECT_EQ(1, a1.num_allocs());
-  EXPECT_EQ(0, it->num_moves());
+  auto it = u.begin();
+  EXPECT_THAT(a1.num_allocs(), AnyOf(1, 2));
+  EXPECT_THAT(it->num_moves(), AnyOf(0, 1));
   EXPECT_EQ(0, it->num_copies());
 }
 
 TEST_F(NoPropagateOnMove, MoveConstructorWithSameAlloc) {
-  auto it = t1.insert(0).first;
+  t1.insert(0);
   Table u(std::move(t1), a1);
-  EXPECT_EQ(1, a1.num_allocs());
-  EXPECT_EQ(0, it->num_moves());
+  auto it = u.begin();
+  EXPECT_THAT(a1.num_allocs(), AnyOf(1, 2));
+  EXPECT_THAT(it->num_moves(), AnyOf(0, 1));
   EXPECT_EQ(0, it->num_copies());
 }
 
@@ -313,8 +327,8 @@
   it = u.find(0);
   EXPECT_EQ(a2, u.get_allocator());
   EXPECT_EQ(1, a1.num_allocs());
-  EXPECT_EQ(1, a2.num_allocs());
-  EXPECT_EQ(1, it->num_moves());
+  EXPECT_THAT(a2.num_allocs(), AnyOf(1, 2));
+  EXPECT_THAT(it->num_moves(), AnyOf(1, 2));
   EXPECT_EQ(0, it->num_copies());
 }
 
@@ -324,8 +338,8 @@
   it = u.find(0);
   EXPECT_EQ(a2, u.get_allocator());
   EXPECT_EQ(1, a1.num_allocs());
-  EXPECT_EQ(1, a2.num_allocs());
-  EXPECT_EQ(1, it->num_moves());
+  EXPECT_THAT(a2.num_allocs(), AnyOf(1, 2));
+  EXPECT_THAT(it->num_moves(), AnyOf(1, 2));
   EXPECT_EQ(0, it->num_copies());
 }
 
@@ -333,8 +347,8 @@
   auto it = t1.insert(0).first;
   Table u(0, a1);
   u = t1;
-  EXPECT_EQ(2, a1.num_allocs());
-  EXPECT_EQ(0, it->num_moves());
+  EXPECT_THAT(a1.num_allocs(), AnyOf(2, 3));
+  EXPECT_THAT(it->num_moves(), AnyOf(0, 1));
   EXPECT_EQ(1, it->num_copies());
 }
 
@@ -342,8 +356,8 @@
   auto it = t1.insert(0).first;
   Table u(0, a1);
   u = t1;
-  EXPECT_EQ(2, a1.num_allocs());
-  EXPECT_EQ(0, it->num_moves());
+  EXPECT_THAT(a1.num_allocs(), AnyOf(2, 3));
+  EXPECT_THAT(it->num_moves(), AnyOf(0, 1));
   EXPECT_EQ(1, it->num_copies());
 }
 
@@ -352,9 +366,9 @@
   Table u(0, a2);
   u = t1;
   EXPECT_EQ(a1, u.get_allocator());
-  EXPECT_EQ(2, a1.num_allocs());
+  EXPECT_THAT(a1.num_allocs(), AnyOf(2, 3));
   EXPECT_EQ(0, a2.num_allocs());
-  EXPECT_EQ(0, it->num_moves());
+  EXPECT_THAT(it->num_moves(), AnyOf(0, 1));
   EXPECT_EQ(1, it->num_copies());
 }
 
@@ -364,51 +378,54 @@
   u = t1;
   EXPECT_EQ(a2, u.get_allocator());
   EXPECT_EQ(1, a1.num_allocs());
-  EXPECT_EQ(1, a2.num_allocs());
-  EXPECT_EQ(0, it->num_moves());
+  EXPECT_THAT(a2.num_allocs(), AnyOf(1, 2));
+  EXPECT_THAT(it->num_moves(), AnyOf(0, 1));
   EXPECT_EQ(1, it->num_copies());
 }
 
 TEST_F(PropagateOnAll, MoveAssignmentWithSameAlloc) {
-  auto it = t1.insert(0).first;
+  t1.insert(0);
   Table u(0, a1);
   u = std::move(t1);
+  auto it = u.begin();
   EXPECT_EQ(a1, u.get_allocator());
-  EXPECT_EQ(1, a1.num_allocs());
-  EXPECT_EQ(0, it->num_moves());
+  EXPECT_THAT(a1.num_allocs(), AnyOf(1, 2));
+  EXPECT_THAT(it->num_moves(), AnyOf(0, 1));
   EXPECT_EQ(0, it->num_copies());
 }
 
 TEST_F(NoPropagateOnMove, MoveAssignmentWithSameAlloc) {
-  auto it = t1.insert(0).first;
+  t1.insert(0);
   Table u(0, a1);
   u = std::move(t1);
+  auto it = u.begin();
   EXPECT_EQ(a1, u.get_allocator());
-  EXPECT_EQ(1, a1.num_allocs());
-  EXPECT_EQ(0, it->num_moves());
+  EXPECT_THAT(a1.num_allocs(), AnyOf(1, 2));
+  EXPECT_THAT(it->num_moves(), AnyOf(0, 1));
   EXPECT_EQ(0, it->num_copies());
 }
 
 TEST_F(PropagateOnAll, MoveAssignmentWithDifferentAlloc) {
-  auto it = t1.insert(0).first;
+  t1.insert(0);
   Table u(0, a2);
   u = std::move(t1);
+  auto it = u.begin();
   EXPECT_EQ(a1, u.get_allocator());
-  EXPECT_EQ(1, a1.num_allocs());
+  EXPECT_THAT(a1.num_allocs(), AnyOf(1, 2));
   EXPECT_EQ(0, a2.num_allocs());
-  EXPECT_EQ(0, it->num_moves());
+  EXPECT_THAT(it->num_moves(), AnyOf(0, 1));
   EXPECT_EQ(0, it->num_copies());
 }
 
 TEST_F(NoPropagateOnMove, MoveAssignmentWithDifferentAlloc) {
-  auto it = t1.insert(0).first;
+  t1.insert(0);
   Table u(0, a2);
   u = std::move(t1);
-  it = u.find(0);
+  auto it = u.find(0);
   EXPECT_EQ(a2, u.get_allocator());
   EXPECT_EQ(1, a1.num_allocs());
-  EXPECT_EQ(1, a2.num_allocs());
-  EXPECT_EQ(1, it->num_moves());
+  EXPECT_THAT(a2.num_allocs(), AnyOf(1, 2));
+  EXPECT_THAT(it->num_moves(), AnyOf(1, 2));
   EXPECT_EQ(0, it->num_copies());
 }
 
@@ -435,9 +452,6 @@
   // types
   using value_type = T;
 
-  // traits
-  using propagate_on_container_swap = std::false_type;
-
   PAlloc() noexcept = default;
   explicit PAlloc(size_t id) noexcept : id_(id) {}
   PAlloc(const PAlloc&) noexcept = default;
@@ -466,21 +480,7 @@
   size_t id_ = std::numeric_limits<size_t>::max();
 };
 
-// This doesn't compile with GCC 5.4 and 5.5 due to a bug in noexcept handing.
-#if !defined(__GNUC__) || __GNUC__ != 5 || (__GNUC_MINOR__ != 4 && \
-    __GNUC_MINOR__ != 5)
-TEST(NoPropagateOn, Swap) {
-  using PA = PAlloc<char>;
-  using Table = raw_hash_set<Policy, Identity, std::equal_to<int32_t>, PA>;
-
-  Table t1(PA{1}), t2(PA{2});
-  swap(t1, t2);
-  EXPECT_EQ(t1.get_allocator(), PA(1));
-  EXPECT_EQ(t2.get_allocator(), PA(2));
-}
-#endif
-
-TEST(NoPropagateOn, CopyConstruct) {
+TEST(NoPropagateDeletedAssignment, CopyConstruct) {
   using PA = PAlloc<char>;
   using Table = raw_hash_set<Policy, Identity, std::equal_to<int32_t>, PA>;
 
@@ -489,7 +489,7 @@
   EXPECT_EQ(t2.get_allocator(), PA());
 }
 
-TEST(NoPropagateOn, Assignment) {
+TEST(NoPropagateDeletedAssignment, CopyAssignment) {
   using PA = PAlloc<char>;
   using Table = raw_hash_set<Policy, Identity, std::equal_to<int32_t>, PA>;
 
@@ -499,6 +499,15 @@
   EXPECT_EQ(t2.get_allocator(), PA(2));
 }
 
+TEST(NoPropagateDeletedAssignment, MoveAssignment) {
+  using PA = PAlloc<char>;
+  using Table = raw_hash_set<Policy, Identity, std::equal_to<int32_t>, PA>;
+
+  Table t1(PA{1}), t2(PA{2});
+  t1 = std::move(t2);
+  EXPECT_EQ(t1.get_allocator(), PA(1));
+}
+
 }  // namespace
 }  // namespace container_internal
 ABSL_NAMESPACE_END
diff --git a/absl/container/internal/raw_hash_set_benchmark.cc b/absl/container/internal/raw_hash_set_benchmark.cc
index a364789..88b0737 100644
--- a/absl/container/internal/raw_hash_set_benchmark.cc
+++ b/absl/container/internal/raw_hash_set_benchmark.cc
@@ -14,6 +14,8 @@
 
 #include <array>
 #include <cmath>
+#include <cstddef>
+#include <cstdint>
 #include <numeric>
 #include <random>
 #include <tuple>
@@ -205,6 +207,22 @@
 }
 BENCHMARK(BM_CacheInSteadyState)->Apply(CacheInSteadyStateArgs);
 
+void BM_EraseEmplace(benchmark::State& state) {
+  IntTable t;
+  int64_t size = state.range(0);
+  for (int64_t i = 0; i < size; ++i) {
+    t.emplace(i);
+  }
+  while (state.KeepRunningBatch(size)) {
+    for (int64_t i = 0; i < size; ++i) {
+      benchmark::DoNotOptimize(t);
+      t.erase(i);
+      t.emplace(i);
+    }
+  }
+}
+BENCHMARK(BM_EraseEmplace)->Arg(1)->Arg(2)->Arg(4)->Arg(8)->Arg(16)->Arg(100);
+
 void BM_EndComparison(benchmark::State& state) {
   StringTable t = {{"a", "a"}, {"b", "b"}};
   auto it = t.begin();
@@ -369,28 +387,42 @@
 BENCHMARK(BM_NoOpReserveStringTable);
 
 void BM_ReserveIntTable(benchmark::State& state) {
-  int reserve_size = state.range(0);
-  for (auto _ : state) {
+  constexpr size_t kBatchSize = 1024;
+  size_t reserve_size = static_cast<size_t>(state.range(0));
+
+  std::vector<IntTable> tables;
+  while (state.KeepRunningBatch(kBatchSize)) {
     state.PauseTiming();
-    IntTable t;
+    tables.clear();
+    tables.resize(kBatchSize);
     state.ResumeTiming();
-    benchmark::DoNotOptimize(t);
-    t.reserve(reserve_size);
+    for (auto& t : tables) {
+      benchmark::DoNotOptimize(t);
+      t.reserve(reserve_size);
+      benchmark::DoNotOptimize(t);
+    }
   }
 }
-BENCHMARK(BM_ReserveIntTable)->Range(128, 4096);
+BENCHMARK(BM_ReserveIntTable)->Range(1, 64);
 
 void BM_ReserveStringTable(benchmark::State& state) {
-  int reserve_size = state.range(0);
-  for (auto _ : state) {
+  constexpr size_t kBatchSize = 1024;
+  size_t reserve_size = static_cast<size_t>(state.range(0));
+
+  std::vector<StringTable> tables;
+  while (state.KeepRunningBatch(kBatchSize)) {
     state.PauseTiming();
-    StringTable t;
+    tables.clear();
+    tables.resize(kBatchSize);
     state.ResumeTiming();
-    benchmark::DoNotOptimize(t);
-    t.reserve(reserve_size);
+    for (auto& t : tables) {
+      benchmark::DoNotOptimize(t);
+      t.reserve(reserve_size);
+      benchmark::DoNotOptimize(t);
+    }
   }
 }
-BENCHMARK(BM_ReserveStringTable)->Range(128, 4096);
+BENCHMARK(BM_ReserveStringTable)->Range(1, 64);
 
 // Like std::iota, except that ctrl_t doesn't support operator++.
 template <typename CtrlIter>
diff --git a/absl/container/internal/raw_hash_set_probe_benchmark.cc b/absl/container/internal/raw_hash_set_probe_benchmark.cc
index 7169a2e..5d4184b 100644
--- a/absl/container/internal/raw_hash_set_probe_benchmark.cc
+++ b/absl/container/internal/raw_hash_set_probe_benchmark.cc
@@ -19,6 +19,7 @@
 #include <regex>  // NOLINT
 #include <vector>
 
+#include "absl/base/no_destructor.h"
 #include "absl/container/flat_hash_map.h"
 #include "absl/container/internal/hash_function_defaults.h"
 #include "absl/container/internal/hashtable_debug.h"
@@ -29,6 +30,7 @@
 #include "absl/strings/str_format.h"
 #include "absl/strings/string_view.h"
 #include "absl/strings/strip.h"
+#include "absl/types/optional.h"
 
 namespace {
 
@@ -71,7 +73,7 @@
 };
 
 absl::BitGen& GlobalBitGen() {
-  static auto* value = new absl::BitGen;
+  static absl::NoDestructor<absl::BitGen> value;
   return *value;
 }
 
@@ -112,7 +114,7 @@
   static constexpr size_t kRandomPool = 20;
 
   static std::vector<T*>& GetPointers(size_t n) {
-    static auto* m = new absl::flat_hash_map<size_t, std::vector<T*>>();
+    static absl::NoDestructor<absl::flat_hash_map<size_t, std::vector<T*>>> m;
     return (*m)[n];
   }
 };
@@ -460,12 +462,12 @@
 constexpr int kDistWidth = 16;
 
 bool CanRunBenchmark(absl::string_view name) {
-  static std::regex* const filter = []() -> std::regex* {
+  static const absl::NoDestructor<absl::optional<std::regex>> filter([] {
     return benchmarks.empty() || benchmarks == "all"
-               ? nullptr
-               : new std::regex(std::string(benchmarks));
-  }();
-  return filter == nullptr || std::regex_search(std::string(name), *filter);
+               ? absl::nullopt
+               : absl::make_optional(std::regex(std::string(benchmarks)));
+  }());
+  return !filter->has_value() || std::regex_search(std::string(name), **filter);
 }
 
 struct Result {
diff --git a/absl/container/internal/raw_hash_set_test.cc b/absl/container/internal/raw_hash_set_test.cc
index 242a97c..f9797f5 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>
@@ -48,7 +49,12 @@
 #include "absl/container/internal/hash_function_defaults.h"
 #include "absl/container/internal/hash_policy_testing.h"
 #include "absl/container/internal/hashtable_debug.h"
+#include "absl/container/internal/hashtablez_sampler.h"
+#include "absl/container/internal/test_allocator.h"
+#include "absl/hash/hash.h"
 #include "absl/log/log.h"
+#include "absl/memory/memory.h"
+#include "absl/meta/type_traits.h"
 #include "absl/strings/string_view.h"
 
 namespace absl {
@@ -230,6 +236,25 @@
   }
 }
 
+TEST(Group, MaskFull) {
+  if (Group::kWidth == 16) {
+    ctrl_t group[] = {
+        ctrl_t::kEmpty, CtrlT(1),          ctrl_t::kDeleted,  CtrlT(3),
+        ctrl_t::kEmpty, CtrlT(5),          ctrl_t::kSentinel, CtrlT(7),
+        CtrlT(7),       CtrlT(5),          ctrl_t::kDeleted,  CtrlT(1),
+        CtrlT(1),       ctrl_t::kSentinel, ctrl_t::kEmpty,    CtrlT(1)};
+    EXPECT_THAT(Group{group}.MaskFull(),
+                ElementsAre(1, 3, 5, 7, 8, 9, 11, 12, 15));
+  } else if (Group::kWidth == 8) {
+    ctrl_t group[] = {ctrl_t::kEmpty,    CtrlT(1), ctrl_t::kEmpty,
+                      ctrl_t::kDeleted,  CtrlT(2), ctrl_t::kSentinel,
+                      ctrl_t::kSentinel, CtrlT(1)};
+    EXPECT_THAT(Group{group}.MaskFull(), ElementsAre(1, 4, 7));
+  } else {
+    FAIL() << "No test coverage for Group::kWidth==" << Group::kWidth;
+  }
+}
+
 TEST(Group, MaskEmptyOrDeleted) {
   if (Group::kWidth == 16) {
     ctrl_t group[] = {ctrl_t::kEmpty,   CtrlT(1), ctrl_t::kEmpty,    CtrlT(3),
@@ -292,13 +317,13 @@
       std::vector<ctrl_t> f(Group::kWidth, empty);
       f[Group::kWidth * 2 / 3] = full;
       f[Group::kWidth / 2] = full;
-      EXPECT_EQ(
-          Group::kWidth / 2, Group{f.data()}.CountLeadingEmptyOrDeleted());
+      EXPECT_EQ(Group::kWidth / 2,
+                Group{f.data()}.CountLeadingEmptyOrDeleted());
     }
   }
 }
 
-template <class T>
+template <class T, bool kTransferable = false>
 struct ValuePolicy {
   using slot_type = T;
   using key_type = T;
@@ -316,10 +341,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; }
@@ -336,6 +362,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<
@@ -408,19 +436,18 @@
   using Base::Base;
 };
 
-struct IntTable
-    : raw_hash_set<IntPolicy, container_internal::hash_default_hash<int64_t>,
-                   std::equal_to<int64_t>, std::allocator<int64_t>> {
-  using Base = typename IntTable::raw_hash_set;
+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;
 };
 
-struct Uint8Table
-    : raw_hash_set<Uint8Policy, container_internal::hash_default_hash<uint8_t>,
-                   std::equal_to<uint8_t>, std::allocator<uint8_t>> {
-  using Base = typename Uint8Table::raw_hash_set;
-  using Base::Base;
-};
+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> {
@@ -429,48 +456,21 @@
   template <typename U>
   explicit CustomAlloc(const CustomAlloc<U>& /*other*/) {}
 
-  template<class U> struct rebind {
+  template <class U>
+  struct rebind {
     using other = CustomAlloc<U>;
   };
 };
 
 struct CustomAllocIntTable
-    : raw_hash_set<IntPolicy, container_internal::hash_default_hash<int64_t>,
+    : raw_hash_set<IntPolicy, hash_default_hash<int64_t>,
                    std::equal_to<int64_t>, CustomAlloc<int64_t>> {
   using Base = typename CustomAllocIntTable::raw_hash_set;
   using Base::Base;
 };
 
-// Tries to allocate memory at the minimum alignment even when the default
-// allocator uses a higher alignment.
-template <typename T>
-struct MinimumAlignmentAlloc : std::allocator<T> {
-  MinimumAlignmentAlloc() = default;
-
-  template <typename U>
-  explicit MinimumAlignmentAlloc(const MinimumAlignmentAlloc<U>& /*other*/) {}
-
-  template <class U>
-  struct rebind {
-    using other = MinimumAlignmentAlloc<U>;
-  };
-
-  T* allocate(size_t n) {
-    T* ptr = std::allocator<T>::allocate(n + 1);
-    char* cptr = reinterpret_cast<char*>(ptr);
-    cptr += alignof(T);
-    return reinterpret_cast<T*>(cptr);
-  }
-
-  void deallocate(T* ptr, size_t n) {
-    char* cptr = reinterpret_cast<char*>(ptr);
-    cptr -= alignof(T);
-    std::allocator<T>::deallocate(reinterpret_cast<T*>(cptr), n + 1);
-  }
-};
-
 struct MinimumAlignmentUint8Table
-    : raw_hash_set<Uint8Policy, container_internal::hash_default_hash<uint8_t>,
+    : raw_hash_set<Uint8Policy, hash_default_hash<uint8_t>,
                    std::equal_to<uint8_t>, MinimumAlignmentAlloc<uint8_t>> {
   using Base = typename MinimumAlignmentUint8Table::raw_hash_set;
   using Base::Base;
@@ -524,13 +524,6 @@
   static_assert(std::is_empty<std::allocator<int>>::value, "");
 
   struct MockTable {
-    void* infoz;
-    void* ctrl;
-    void* slots;
-    size_t size;
-    size_t capacity;
-  };
-  struct MockTableInfozDisabled {
     void* ctrl;
     void* slots;
     size_t size;
@@ -555,9 +548,7 @@
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wunreachable-code"
 #endif
-  constexpr size_t mock_size = std::is_empty<HashtablezInfoHandle>()
-                                   ? sizeof(MockTableInfozDisabled)
-                                   : sizeof(MockTable);
+  constexpr size_t mock_size = sizeof(MockTable);
   constexpr size_t generation_size =
       SwisstableGenerationsEnabled() ? sizeof(GenerationData) : 0;
 #if defined(__clang__)
@@ -662,6 +653,23 @@
   EXPECT_TRUE(t.empty());
 }
 
+TEST(Table, EraseInSmallTables) {
+  for (int64_t size = 0; size < 64; ++size) {
+    IntTable t;
+    for (int64_t i = 0; i < size; ++i) {
+      t.insert(i);
+    }
+    for (int64_t i = 0; i < size; ++i) {
+      t.erase(i);
+      EXPECT_EQ(t.size(), size - i - 1);
+      for (int64_t j = i + 1; j < size; ++j) {
+        EXPECT_THAT(*t.find(j), j);
+      }
+    }
+    EXPECT_TRUE(t.empty());
+  }
+}
+
 TEST(Table, InsertWithinCapacity) {
   IntTable t;
   t.reserve(10);
@@ -693,6 +701,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;
@@ -1111,7 +1181,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);
@@ -1332,15 +1402,15 @@
   switch (container_internal::Group::kWidth) {
     case 8:
       if (kRandomizesInserts) {
-  return {0.05,
-          1.0,
-          {{0.95, 0.5}},
-          {{0.95, 0}, {0.99, 2}, {0.999, 4}, {0.9999, 10}}};
+        return {0.05,
+                1.0,
+                {{0.95, 0.5}},
+                {{0.95, 0}, {0.99, 2}, {0.999, 4}, {0.9999, 10}}};
       } else {
-  return {0.05,
-          2.0,
-          {{0.95, 0.1}},
-          {{0.95, 0}, {0.99, 2}, {0.999, 4}, {0.9999, 10}}};
+        return {0.05,
+                2.0,
+                {{0.95, 0.1}},
+                {{0.95, 0}, {0.99, 2}, {0.999, 4}, {0.9999, 10}}};
       }
     case 16:
       if (kRandomizesInserts) {
@@ -1386,7 +1456,7 @@
   std::random_device rd;
   std::mt19937 rng(rd());
   auto linear_transform = [](size_t x, size_t y) { return x * 17 + y * 13; };
-  std::uniform_int_distribution<size_t> dist(0, keys.size()-1);
+  std::uniform_int_distribution<size_t> dist(0, keys.size() - 1);
   while (num_iters--) {
     IntTable t1;
     size_t num_keys = keys.size() / 10;
@@ -1638,7 +1708,7 @@
 }
 
 struct ExplicitAllocIntTable
-    : raw_hash_set<IntPolicy, container_internal::hash_default_hash<int64_t>,
+    : raw_hash_set<IntPolicy, hash_default_hash<int64_t>,
                    std::equal_to<int64_t>, Alloc<int64_t>> {
   ExplicitAllocIntTable() = default;
 };
@@ -1716,6 +1786,14 @@
   EXPECT_THAT(*u.find("a"), Pair("a", "b"));
 }
 
+TEST(Table, MoveSelfAssign) {
+  StringTable t;
+  t.emplace("a", "b");
+  EXPECT_EQ(1, t.size());
+  t = std::move(*&t);
+  // As long as we don't crash, it's fine.
+}
+
 TEST(Table, Equality) {
   StringTable t;
   std::vector<std::pair<std::string, std::string>> v = {{"a", "b"},
@@ -1894,11 +1972,9 @@
   EXPECT_FALSE((VerifyResultOf<CallPrefetch, NonTransparentTable>()));
   EXPECT_FALSE((VerifyResultOf<CallCount, NonTransparentTable>()));
 
-  using TransparentTable = raw_hash_set<
-      StringPolicy,
-      absl::container_internal::hash_default_hash<absl::string_view>,
-      absl::container_internal::hash_default_eq<absl::string_view>,
-      std::allocator<int>>;
+  using TransparentTable =
+      raw_hash_set<StringPolicy, hash_default_hash<absl::string_view>,
+                   hash_default_eq<absl::string_view>, std::allocator<int>>;
 
   EXPECT_TRUE((VerifyResultOf<CallFind, TransparentTable>()));
   EXPECT_TRUE((VerifyResultOf<CallErase, TransparentTable>()));
@@ -2120,16 +2196,6 @@
   EXPECT_NE(old_ptr, addr(0));
 }
 
-bool IsAssertEnabled() {
-  // Use an assert with side-effects to figure out if they are actually enabled.
-  bool assert_enabled = false;
-  assert([&]() {  // NOLINT
-    assert_enabled = true;
-    return true;
-  }());
-  return assert_enabled;
-}
-
 TEST(TableDeathTest, InvalidIteratorAsserts) {
   if (!IsAssertEnabled() && !SwisstableGenerationsEnabled())
     GTEST_SKIP() << "Assertions not enabled.";
@@ -2152,10 +2218,10 @@
   EXPECT_DEATH_IF_SUPPORTED(++iter, kErasedDeathMessage);
 }
 
-// Invalid iterator use can trigger heap-use-after-free in asan,
+// Invalid iterator use can trigger use-after-free in asan/hwasan,
 // use-of-uninitialized-value in msan, or invalidated iterator assertions.
 constexpr const char* kInvalidIteratorDeathMessage =
-    "heap-use-after-free|use-of-uninitialized-value|invalidated "
+    "use-after-free|use-of-uninitialized-value|invalidated "
     "iterator|Invalid iterator|invalid iterator";
 
 // MSVC doesn't support | in regex.
@@ -2302,21 +2368,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;
@@ -2410,6 +2489,26 @@
 #endif
 }
 
+TEST(Iterator, InvalidUseWithMoveCrashesWithSanitizers) {
+  if (!SwisstableGenerationsEnabled()) GTEST_SKIP() << "Generations disabled.";
+  if (kMsvc) GTEST_SKIP() << "MSVC doesn't support | in regexp.";
+
+  IntTable t1, t2;
+  t1.insert(1);
+  auto it = t1.begin();
+  // ptr will become invalidated on rehash.
+  const int64_t* ptr = &*it;
+  (void)ptr;
+
+  t2 = std::move(t1);
+  EXPECT_DEATH_IF_SUPPORTED(*it, kInvalidIteratorDeathMessage);
+  EXPECT_DEATH_IF_SUPPORTED(void(it == t2.begin()),
+                            kInvalidIteratorDeathMessage);
+#ifdef ABSL_HAVE_ADDRESS_SANITIZER
+  EXPECT_DEATH_IF_SUPPORTED(std::cout << *ptr, "heap-use-after-free");
+#endif
+}
+
 TEST(Table, ReservedGrowthUpdatesWhenTableDoesntGrow) {
   IntTable t;
   for (int i = 0; i < 8; ++i) t.insert(i);
@@ -2480,7 +2579,7 @@
     // ptr will become invalidated on rehash.
     const int64_t* ptr = &*t.begin();
     t.insert(++i);
-    EXPECT_DEATH_IF_SUPPORTED(std::cout << *ptr, "heap-use-after-free") << i;
+    EXPECT_DEATH_IF_SUPPORTED(std::cout << *ptr, "use-after-free") << i;
   }
 }
 
@@ -2510,6 +2609,75 @@
                             "Invalid iterator comparison.*non-end");
 }
 
+template <typename Alloc>
+using RawHashSetAlloc = raw_hash_set<IntPolicy, hash_default_hash<int64_t>,
+                                     std::equal_to<int64_t>, Alloc>;
+
+TEST(Table, AllocatorPropagation) { TestAllocPropagation<RawHashSetAlloc>(); }
+
+struct CountedHash {
+  size_t operator()(int value) const {
+    ++count;
+    return static_cast<size_t>(value);
+  }
+  mutable int count = 0;
+};
+
+struct CountedHashIntTable
+    : raw_hash_set<IntPolicy, CountedHash, std::equal_to<int>,
+                   std::allocator<int>> {
+  using Base = typename CountedHashIntTable::raw_hash_set;
+  using Base::Base;
+};
+
+TEST(Table, CountedHash) {
+  // Verify that raw_hash_set does not compute redundant hashes.
+#ifdef NDEBUG
+  constexpr bool kExpectMinimumHashes = true;
+#else
+  constexpr bool kExpectMinimumHashes = false;
+#endif
+  if (!kExpectMinimumHashes) {
+    GTEST_SKIP() << "Only run under NDEBUG: `assert` statements may cause "
+                    "redundant hashing.";
+  }
+
+  using Table = CountedHashIntTable;
+  auto HashCount = [](const Table& t) { return t.hash_function().count; };
+  {
+    Table t;
+    EXPECT_EQ(HashCount(t), 0);
+  }
+  {
+    Table t;
+    t.insert(1);
+    EXPECT_EQ(HashCount(t), 1);
+    t.erase(1);
+    EXPECT_EQ(HashCount(t), 2);
+  }
+  {
+    Table t;
+    t.insert(3);
+    EXPECT_EQ(HashCount(t), 1);
+    auto node = t.extract(3);
+    EXPECT_EQ(HashCount(t), 2);
+    t.insert(std::move(node));
+    EXPECT_EQ(HashCount(t), 3);
+  }
+  {
+    Table t;
+    t.emplace(5);
+    EXPECT_EQ(HashCount(t), 1);
+  }
+  {
+    Table src;
+    src.insert(7);
+    Table dst;
+    dst.merge(src);
+    EXPECT_EQ(HashCount(dst), 1);
+  }
+}
+
 }  // namespace
 }  // namespace container_internal
 ABSL_NAMESPACE_END
diff --git a/absl/container/internal/test_allocator.h b/absl/container/internal/test_allocator.h
new file mode 100644
index 0000000..8e365a3
--- /dev/null
+++ b/absl/container/internal/test_allocator.h
@@ -0,0 +1,387 @@
+// Copyright 2018 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef ABSL_CONTAINER_INTERNAL_TEST_ALLOCATOR_H_
+#define ABSL_CONTAINER_INTERNAL_TEST_ALLOCATOR_H_
+
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+#include <memory>
+#include <type_traits>
+
+#include "gtest/gtest.h"
+#include "absl/base/config.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace container_internal {
+
+// This is a stateful allocator, but the state lives outside of the
+// allocator (in whatever test is using the allocator). This is odd
+// but helps in tests where the allocator is propagated into nested
+// containers - that chain of allocators uses the same state and is
+// thus easier to query for aggregate allocation information.
+template <typename T>
+class CountingAllocator {
+ public:
+  using Allocator = std::allocator<T>;
+  using AllocatorTraits = std::allocator_traits<Allocator>;
+  using value_type = typename AllocatorTraits::value_type;
+  using pointer = typename AllocatorTraits::pointer;
+  using const_pointer = typename AllocatorTraits::const_pointer;
+  using size_type = typename AllocatorTraits::size_type;
+  using difference_type = typename AllocatorTraits::difference_type;
+
+  CountingAllocator() = default;
+  explicit CountingAllocator(int64_t* bytes_used) : bytes_used_(bytes_used) {}
+  CountingAllocator(int64_t* bytes_used, int64_t* instance_count)
+      : bytes_used_(bytes_used), instance_count_(instance_count) {}
+
+  template <typename U>
+  CountingAllocator(const CountingAllocator<U>& x)
+      : bytes_used_(x.bytes_used_), instance_count_(x.instance_count_) {}
+
+  pointer allocate(
+      size_type n,
+      typename AllocatorTraits::const_void_pointer hint = nullptr) {
+    Allocator allocator;
+    pointer ptr = AllocatorTraits::allocate(allocator, n, hint);
+    if (bytes_used_ != nullptr) {
+      *bytes_used_ += n * sizeof(T);
+    }
+    return ptr;
+  }
+
+  void deallocate(pointer p, size_type n) {
+    Allocator allocator;
+    AllocatorTraits::deallocate(allocator, p, n);
+    if (bytes_used_ != nullptr) {
+      *bytes_used_ -= n * sizeof(T);
+    }
+  }
+
+  template <typename U, typename... Args>
+  void construct(U* p, Args&&... args) {
+    Allocator allocator;
+    AllocatorTraits::construct(allocator, p, std::forward<Args>(args)...);
+    if (instance_count_ != nullptr) {
+      *instance_count_ += 1;
+    }
+  }
+
+  template <typename U>
+  void destroy(U* p) {
+    Allocator allocator;
+    // Ignore GCC warning bug.
+#if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(12, 0)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wuse-after-free"
+#endif
+    AllocatorTraits::destroy(allocator, p);
+#if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(12, 0)
+#pragma GCC diagnostic pop
+#endif
+    if (instance_count_ != nullptr) {
+      *instance_count_ -= 1;
+    }
+  }
+
+  template <typename U>
+  class rebind {
+   public:
+    using other = CountingAllocator<U>;
+  };
+
+  friend bool operator==(const CountingAllocator& a,
+                         const CountingAllocator& b) {
+    return a.bytes_used_ == b.bytes_used_ &&
+           a.instance_count_ == b.instance_count_;
+  }
+
+  friend bool operator!=(const CountingAllocator& a,
+                         const CountingAllocator& b) {
+    return !(a == b);
+  }
+
+  int64_t* bytes_used_ = nullptr;
+  int64_t* instance_count_ = nullptr;
+};
+
+template <typename T>
+struct CopyAssignPropagatingCountingAlloc : public CountingAllocator<T> {
+  using propagate_on_container_copy_assignment = std::true_type;
+
+  using Base = CountingAllocator<T>;
+  using Base::Base;
+
+  template <typename U>
+  explicit CopyAssignPropagatingCountingAlloc(
+      const CopyAssignPropagatingCountingAlloc<U>& other)
+      : Base(other.bytes_used_, other.instance_count_) {}
+
+  template <typename U>
+  struct rebind {
+    using other = CopyAssignPropagatingCountingAlloc<U>;
+  };
+};
+
+template <typename T>
+struct MoveAssignPropagatingCountingAlloc : public CountingAllocator<T> {
+  using propagate_on_container_move_assignment = std::true_type;
+
+  using Base = CountingAllocator<T>;
+  using Base::Base;
+
+  template <typename U>
+  explicit MoveAssignPropagatingCountingAlloc(
+      const MoveAssignPropagatingCountingAlloc<U>& other)
+      : Base(other.bytes_used_, other.instance_count_) {}
+
+  template <typename U>
+  struct rebind {
+    using other = MoveAssignPropagatingCountingAlloc<U>;
+  };
+};
+
+template <typename T>
+struct SwapPropagatingCountingAlloc : public CountingAllocator<T> {
+  using propagate_on_container_swap = std::true_type;
+
+  using Base = CountingAllocator<T>;
+  using Base::Base;
+
+  template <typename U>
+  explicit SwapPropagatingCountingAlloc(
+      const SwapPropagatingCountingAlloc<U>& other)
+      : Base(other.bytes_used_, other.instance_count_) {}
+
+  template <typename U>
+  struct rebind {
+    using other = SwapPropagatingCountingAlloc<U>;
+  };
+};
+
+// Tries to allocate memory at the minimum alignment even when the default
+// allocator uses a higher alignment.
+template <typename T>
+struct MinimumAlignmentAlloc : std::allocator<T> {
+  MinimumAlignmentAlloc() = default;
+
+  template <typename U>
+  explicit MinimumAlignmentAlloc(const MinimumAlignmentAlloc<U>& /*other*/) {}
+
+  template <class U>
+  struct rebind {
+    using other = MinimumAlignmentAlloc<U>;
+  };
+
+  T* allocate(size_t n) {
+    T* ptr = std::allocator<T>::allocate(n + 1);
+    char* cptr = reinterpret_cast<char*>(ptr);
+    cptr += alignof(T);
+    return reinterpret_cast<T*>(cptr);
+  }
+
+  void deallocate(T* ptr, size_t n) {
+    char* cptr = reinterpret_cast<char*>(ptr);
+    cptr -= alignof(T);
+    std::allocator<T>::deallocate(reinterpret_cast<T*>(cptr), n + 1);
+  }
+};
+
+inline bool IsAssertEnabled() {
+  // Use an assert with side-effects to figure out if they are actually enabled.
+  bool assert_enabled = false;
+  assert([&]() {  // NOLINT
+    assert_enabled = true;
+    return true;
+  }());
+  return assert_enabled;
+}
+
+template <template <class Alloc> class Container>
+void TestCopyAssignAllocPropagation() {
+  int64_t bytes1 = 0, instances1 = 0, bytes2 = 0, instances2 = 0;
+  CopyAssignPropagatingCountingAlloc<int> allocator1(&bytes1, &instances1);
+  CopyAssignPropagatingCountingAlloc<int> allocator2(&bytes2, &instances2);
+
+  // Test propagating allocator_type.
+  {
+    Container<CopyAssignPropagatingCountingAlloc<int>> c1(allocator1);
+    Container<CopyAssignPropagatingCountingAlloc<int>> c2(allocator2);
+
+    for (int i = 0; i < 100; ++i) c1.insert(i);
+
+    EXPECT_NE(c2.get_allocator(), allocator1);
+    EXPECT_EQ(instances1, 100);
+    EXPECT_EQ(instances2, 0);
+
+    c2 = c1;
+
+    EXPECT_EQ(c2.get_allocator(), allocator1);
+    EXPECT_EQ(instances1, 200);
+    EXPECT_EQ(instances2, 0);
+  }
+  // Test non-propagating allocator_type with different allocators.
+  {
+    Container<CountingAllocator<int>> c1(allocator1), c2(allocator2);
+
+    for (int i = 0; i < 100; ++i) c1.insert(i);
+
+    EXPECT_EQ(c2.get_allocator(), allocator2);
+    EXPECT_EQ(instances1, 100);
+    EXPECT_EQ(instances2, 0);
+
+    c2 = c1;
+
+    EXPECT_EQ(c2.get_allocator(), allocator2);
+    EXPECT_EQ(instances1, 100);
+    EXPECT_EQ(instances2, 100);
+  }
+  EXPECT_EQ(bytes1, 0);
+  EXPECT_EQ(instances1, 0);
+  EXPECT_EQ(bytes2, 0);
+  EXPECT_EQ(instances2, 0);
+}
+
+template <template <class Alloc> class Container>
+void TestMoveAssignAllocPropagation() {
+  int64_t bytes1 = 0, instances1 = 0, bytes2 = 0, instances2 = 0;
+  MoveAssignPropagatingCountingAlloc<int> allocator1(&bytes1, &instances1);
+  MoveAssignPropagatingCountingAlloc<int> allocator2(&bytes2, &instances2);
+
+  // Test propagating allocator_type.
+  {
+    Container<MoveAssignPropagatingCountingAlloc<int>> c1(allocator1);
+    Container<MoveAssignPropagatingCountingAlloc<int>> c2(allocator2);
+
+    for (int i = 0; i < 100; ++i) c1.insert(i);
+
+    EXPECT_NE(c2.get_allocator(), allocator1);
+    EXPECT_EQ(instances1, 100);
+    EXPECT_EQ(instances2, 0);
+
+    c2 = std::move(c1);
+
+    EXPECT_EQ(c2.get_allocator(), allocator1);
+    EXPECT_EQ(instances1, 100);
+    EXPECT_EQ(instances2, 0);
+  }
+  // Test non-propagating allocator_type with equal allocators.
+  {
+    Container<CountingAllocator<int>> c1(allocator1), c2(allocator1);
+
+    for (int i = 0; i < 100; ++i) c1.insert(i);
+
+    EXPECT_EQ(c2.get_allocator(), allocator1);
+    EXPECT_EQ(instances1, 100);
+    EXPECT_EQ(instances2, 0);
+
+    c2 = std::move(c1);
+
+    EXPECT_EQ(c2.get_allocator(), allocator1);
+    EXPECT_EQ(instances1, 100);
+    EXPECT_EQ(instances2, 0);
+  }
+  // Test non-propagating allocator_type with different allocators.
+  {
+    Container<CountingAllocator<int>> c1(allocator1), c2(allocator2);
+
+    for (int i = 0; i < 100; ++i) c1.insert(i);
+
+    EXPECT_NE(c2.get_allocator(), allocator1);
+    EXPECT_EQ(instances1, 100);
+    EXPECT_EQ(instances2, 0);
+
+    c2 = std::move(c1);
+
+    EXPECT_EQ(c2.get_allocator(), allocator2);
+    EXPECT_LE(instances1, 100);  // The values in c1 may or may not have been
+                                 // destroyed at this point.
+    EXPECT_EQ(instances2, 100);
+  }
+  EXPECT_EQ(bytes1, 0);
+  EXPECT_EQ(instances1, 0);
+  EXPECT_EQ(bytes2, 0);
+  EXPECT_EQ(instances2, 0);
+}
+
+template <template <class Alloc> class Container>
+void TestSwapAllocPropagation() {
+  int64_t bytes1 = 0, instances1 = 0, bytes2 = 0, instances2 = 0;
+  SwapPropagatingCountingAlloc<int> allocator1(&bytes1, &instances1);
+  SwapPropagatingCountingAlloc<int> allocator2(&bytes2, &instances2);
+
+  // Test propagating allocator_type.
+  {
+    Container<SwapPropagatingCountingAlloc<int>> c1(allocator1), c2(allocator2);
+
+    for (int i = 0; i < 100; ++i) c1.insert(i);
+
+    EXPECT_NE(c2.get_allocator(), allocator1);
+    EXPECT_EQ(instances1, 100);
+    EXPECT_EQ(instances2, 0);
+
+    c2.swap(c1);
+
+    EXPECT_EQ(c2.get_allocator(), allocator1);
+    EXPECT_EQ(instances1, 100);
+    EXPECT_EQ(instances2, 0);
+  }
+  // Test non-propagating allocator_type with equal allocators.
+  {
+    Container<CountingAllocator<int>> c1(allocator1), c2(allocator1);
+
+    for (int i = 0; i < 100; ++i) c1.insert(i);
+
+    EXPECT_EQ(c2.get_allocator(), allocator1);
+    EXPECT_EQ(instances1, 100);
+    EXPECT_EQ(instances2, 0);
+
+    c2.swap(c1);
+
+    EXPECT_EQ(c2.get_allocator(), allocator1);
+    EXPECT_EQ(instances1, 100);
+    EXPECT_EQ(instances2, 0);
+  }
+  // Test non-propagating allocator_type with different allocators.
+  {
+    Container<CountingAllocator<int>> c1(allocator1), c2(allocator2);
+
+    for (int i = 0; i < 100; ++i) c1.insert(i);
+
+    EXPECT_NE(c1.get_allocator(), c2.get_allocator());
+    if (IsAssertEnabled()) {
+      EXPECT_DEATH_IF_SUPPORTED(c2.swap(c1), "");
+    }
+  }
+  EXPECT_EQ(bytes1, 0);
+  EXPECT_EQ(instances1, 0);
+  EXPECT_EQ(bytes2, 0);
+  EXPECT_EQ(instances2, 0);
+}
+
+template <template <class Alloc> class Container>
+void TestAllocPropagation() {
+  TestCopyAssignAllocPropagation<Container>();
+  TestMoveAssignAllocPropagation<Container>();
+  TestSwapAllocPropagation<Container>();
+}
+
+}  // namespace container_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif  // ABSL_CONTAINER_INTERNAL_TEST_ALLOCATOR_H_
diff --git a/absl/crc/BUILD.bazel b/absl/crc/BUILD.bazel
index cdbaa9b..f44c3f6 100644
--- a/absl/crc/BUILD.bazel
+++ b/absl/crc/BUILD.bazel
@@ -19,7 +19,14 @@
     "ABSL_TEST_COPTS",
 )
 
-package(default_visibility = ["//visibility:private"])
+package(
+    default_visibility = ["//visibility:private"],
+    features = [
+        "header_modules",
+        "layering_check",
+        "parse_headers",
+    ],
+)
 
 licenses(["notice"])
 
@@ -54,10 +61,8 @@
     visibility = ["//visibility:private"],
     deps = [
         ":cpu_detect",
-        "//absl/base",
         "//absl/base:config",
         "//absl/base:core_headers",
-        "//absl/base:dynamic_annotations",
         "//absl/base:endian",
         "//absl/base:prefetch",
         "//absl/base:raw_logging_internal",
@@ -72,7 +77,7 @@
         "crc32c.cc",
         "internal/crc32c_inline.h",
         "internal/crc_memcpy_fallback.cc",
-        "internal/crc_memcpy_x86_64.cc",
+        "internal/crc_memcpy_x86_arm_combined.cc",
         "internal/crc_non_temporal_memcpy.cc",
     ],
     hdrs = [
@@ -89,7 +94,6 @@
         ":non_temporal_memcpy",
         "//absl/base:config",
         "//absl/base:core_headers",
-        "//absl/base:dynamic_annotations",
         "//absl/base:endian",
         "//absl/base:prefetch",
         "//absl/strings",
@@ -107,6 +111,7 @@
         ":crc32c",
         "//absl/strings",
         "//absl/strings:str_format",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -149,6 +154,7 @@
         "//absl/random",
         "//absl/random:distributions",
         "//absl/strings",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -161,6 +167,7 @@
     visibility = ["//visibility:private"],
     deps = [
         ":non_temporal_memcpy",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -189,6 +196,7 @@
     deps = [
         ":crc32c",
         ":crc_cord_state",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
diff --git a/absl/crc/CMakeLists.txt b/absl/crc/CMakeLists.txt
index 2124716..ec7b451 100644
--- a/absl/crc/CMakeLists.txt
+++ b/absl/crc/CMakeLists.txt
@@ -42,10 +42,8 @@
     ${ABSL_DEFAULT_COPTS}
   DEPS
     absl::crc_cpu_detect
-    absl::base
     absl::config
     absl::core_headers
-    absl::dynamic_annotations
     absl::endian
     absl::prefetch
     absl::raw_logging_internal
@@ -64,7 +62,7 @@
     "crc32c.cc"
     "internal/crc32c_inline.h"
     "internal/crc_memcpy_fallback.cc"
-    "internal/crc_memcpy_x86_64.cc"
+    "internal/crc_memcpy_x86_arm_combined.cc"
     "internal/crc_non_temporal_memcpy.cc"
   COPTS
     ${ABSL_DEFAULT_COPTS}
@@ -74,7 +72,6 @@
     absl::non_temporal_memcpy
     absl::config
     absl::core_headers
-    absl::dynamic_annotations
     absl::endian
     absl::prefetch
     absl::str_format
diff --git a/absl/crc/internal/cpu_detect.cc b/absl/crc/internal/cpu_detect.cc
index 8383808..d7eedd1 100644
--- a/absl/crc/internal/cpu_detect.cc
+++ b/absl/crc/internal/cpu_detect.cc
@@ -189,8 +189,14 @@
       break;
     case 0x19:
       switch (model_num) {
+        case 0x0:  // Stepping Ax
         case 0x1:  // Stepping B0
           return CpuType::kAmdMilan;
+        case 0x10:  // Stepping A0
+        case 0x11:  // Stepping B0
+          return CpuType::kAmdGenoa;
+        case 0x44:  // Stepping A0
+          return CpuType::kAmdRyzenV3000;
         default:
           return CpuType::kUnknown;
       }
@@ -237,8 +243,26 @@
     ABSL_INTERNAL_AARCH64_ID_REG_READ(MIDR_EL1, midr);
     uint32_t implementer = (midr >> 24) & 0xff;
     uint32_t part_number = (midr >> 4) & 0xfff;
-    if (implementer == 0x41 && part_number == 0xd0c) {
-      return CpuType::kArmNeoverseN1;
+    switch (implementer) {
+      case 0x41:
+        switch (part_number) {
+          case 0xd0c: return CpuType::kArmNeoverseN1;
+          case 0xd40: return CpuType::kArmNeoverseV1;
+          case 0xd49: return CpuType::kArmNeoverseN2;
+          case 0xd4f: return CpuType::kArmNeoverseV2;
+          default:
+            return CpuType::kUnknown;
+        }
+        break;
+      case 0xc0:
+        switch (part_number) {
+          case 0xac3: return CpuType::kAmpereSiryn;
+          default:
+            return CpuType::kUnknown;
+        }
+        break;
+      default:
+        return CpuType::kUnknown;
     }
   }
   return CpuType::kUnknown;
diff --git a/absl/crc/internal/cpu_detect.h b/absl/crc/internal/cpu_detect.h
index 6054f69..01e1959 100644
--- a/absl/crc/internal/cpu_detect.h
+++ b/absl/crc/internal/cpu_detect.h
@@ -29,6 +29,8 @@
   kAmdRome,
   kAmdNaples,
   kAmdMilan,
+  kAmdGenoa,
+  kAmdRyzenV3000,
   kIntelCascadelakeXeon,
   kIntelSkylakeXeon,
   kIntelBroadwell,
@@ -37,6 +39,10 @@
   kIntelSandybridge,
   kIntelWestmere,
   kArmNeoverseN1,
+  kArmNeoverseV1,
+  kAmpereSiryn,
+  kArmNeoverseN2,
+  kArmNeoverseV2
 };
 
 // Returns the type of host CPU this code is running on.  Returns kUnknown if
diff --git a/absl/crc/internal/crc32_x86_arm_combined_simd.h b/absl/crc/internal/crc32_x86_arm_combined_simd.h
index 39e53dd..59995ae 100644
--- a/absl/crc/internal/crc32_x86_arm_combined_simd.h
+++ b/absl/crc/internal/crc32_x86_arm_combined_simd.h
@@ -59,6 +59,8 @@
 #if defined(ABSL_CRC_INTERNAL_HAVE_ARM_SIMD)
 using V128 = uint64x2_t;
 #else
+// Note: Do not use __m128i_u, it is not portable.
+// Use V128_LoadU() perform an unaligned load from __m128i*.
 using V128 = __m128i;
 #endif
 
@@ -78,6 +80,9 @@
 // Load 128 bits of integer data. |src| does not need to be aligned.
 V128 V128_LoadU(const V128* src);
 
+// Store 128 bits of integer data. |src| must be 16-byte aligned.
+void V128_Store(V128* dst, V128 data);
+
 // Polynomially multiplies the high 64 bits of |l| and |r|.
 V128 V128_PMulHi(const V128 l, const V128 r);
 
@@ -109,6 +114,10 @@
 template <int imm>
 int V128_Extract32(const V128 l);
 
+// Extracts a 64-bit integer from |l|, selected with |imm|.
+template <int imm>
+uint64_t V128_Extract64(const V128 l);
+
 // Extracts the low 64 bits from V128.
 int64_t V128_Low64(const V128 l);
 
@@ -139,6 +148,8 @@
 
 inline V128 V128_LoadU(const V128* src) { return _mm_loadu_si128(src); }
 
+inline void V128_Store(V128* dst, V128 data) { _mm_store_si128(dst, data); }
+
 inline V128 V128_PMulHi(const V128 l, const V128 r) {
   return _mm_clmulepi64_si128(l, r, 0x11);
 }
@@ -173,6 +184,11 @@
   return _mm_extract_epi32(l, imm);
 }
 
+template <int imm>
+inline uint64_t V128_Extract64(const V128 l) {
+  return static_cast<uint64_t>(_mm_extract_epi64(l, imm));
+}
+
 inline int64_t V128_Low64(const V128 l) { return _mm_cvtsi128_si64(l); }
 
 inline V128 V128_ShiftLeft64(const V128 l, const V128 r) {
@@ -203,10 +219,14 @@
   return vld1q_u64(reinterpret_cast<const uint64_t*>(src));
 }
 
+inline void V128_Store(V128* dst, V128 data) {
+  vst1q_u64(reinterpret_cast<uint64_t*>(dst), data);
+}
+
 // Using inline assembly as clang does not generate the pmull2 instruction and
 // performance drops by 15-20%.
-// TODO(b/193678732): Investigate why the compiler decides not to generate
-// such instructions and why it becomes so much worse.
+// TODO(b/193678732): Investigate why there is a slight performance hit when
+// using intrinsics instead of inline assembly.
 inline V128 V128_PMulHi(const V128 l, const V128 r) {
   uint64x2_t res;
   __asm__ __volatile__("pmull2 %0.1q, %1.2d, %2.2d \n\t"
@@ -215,10 +235,14 @@
   return res;
 }
 
+// TODO(b/193678732): Investigate why the compiler decides to move the constant
+// loop multiplicands from GPR to Neon registers every loop iteration.
 inline V128 V128_PMulLow(const V128 l, const V128 r) {
-  return reinterpret_cast<V128>(vmull_p64(
-      reinterpret_cast<poly64_t>(vget_low_p64(vreinterpretq_p64_u64(l))),
-      reinterpret_cast<poly64_t>(vget_low_p64(vreinterpretq_p64_u64(r)))));
+  uint64x2_t res;
+  __asm__ __volatile__("pmull %0.1q, %1.1d, %2.1d \n\t"
+                       : "=w"(res)
+                       : "w"(l), "w"(r));
+  return res;
 }
 
 inline V128 V128_PMul01(const V128 l, const V128 r) {
@@ -252,6 +276,11 @@
   return vgetq_lane_s32(vreinterpretq_s32_u64(l), imm);
 }
 
+template <int imm>
+inline uint64_t V128_Extract64(const V128 l) {
+  return vgetq_lane_u64(l, imm);
+}
+
 inline int64_t V128_Low64(const V128 l) {
   return vgetq_lane_s64(vreinterpretq_s64_u64(l), 0);
 }
diff --git a/absl/crc/internal/crc_memcpy.h b/absl/crc/internal/crc_memcpy.h
index 4909d43..a0fed65 100644
--- a/absl/crc/internal/crc_memcpy.h
+++ b/absl/crc/internal/crc_memcpy.h
@@ -20,12 +20,15 @@
 
 #include "absl/base/config.h"
 #include "absl/crc/crc32c.h"
+#include "absl/crc/internal/crc32_x86_arm_combined_simd.h"
 
 // Defined if the class AcceleratedCrcMemcpyEngine exists.
-#if defined(__x86_64__) && defined(__SSE4_2__)
+// TODO(b/299127771): Consider relaxing the pclmul requirement once the other
+// intrinsics are conditionally compiled without it.
+#if defined(ABSL_CRC_INTERNAL_HAVE_X86_SIMD)
 #define ABSL_INTERNAL_HAVE_X86_64_ACCELERATED_CRC_MEMCPY_ENGINE 1
-#elif defined(_MSC_VER) && defined(__AVX__)
-#define ABSL_INTERNAL_HAVE_X86_64_ACCELERATED_CRC_MEMCPY_ENGINE 1
+#elif defined(ABSL_CRC_INTERNAL_HAVE_ARM_SIMD)
+#define ABSL_INTERNAL_HAVE_ARM_ACCELERATED_CRC_MEMCPY_ENGINE 1
 #endif
 
 namespace absl {
diff --git a/absl/crc/internal/crc_memcpy_fallback.cc b/absl/crc/internal/crc_memcpy_fallback.cc
index 15b4b05..0779550 100644
--- a/absl/crc/internal/crc_memcpy_fallback.cc
+++ b/absl/crc/internal/crc_memcpy_fallback.cc
@@ -54,7 +54,8 @@
 }
 
 // Compile the following only if we don't have
-#ifndef ABSL_INTERNAL_HAVE_X86_64_ACCELERATED_CRC_MEMCPY_ENGINE
+#if !defined(ABSL_INTERNAL_HAVE_X86_64_ACCELERATED_CRC_MEMCPY_ENGINE) && \
+    !defined(ABSL_INTERNAL_HAVE_ARM_ACCELERATED_CRC_MEMCPY_ENGINE)
 
 CrcMemcpy::ArchSpecificEngines CrcMemcpy::GetArchSpecificEngines() {
   CrcMemcpy::ArchSpecificEngines engines;
@@ -68,7 +69,8 @@
   return std::make_unique<FallbackCrcMemcpyEngine>();
 }
 
-#endif  // ABSL_INTERNAL_HAVE_X86_64_ACCELERATED_CRC_MEMCPY_ENGINE
+#endif  // !ABSL_INTERNAL_HAVE_X86_64_ACCELERATED_CRC_MEMCPY_ENGINE &&
+        // !ABSL_INTERNAL_HAVE_ARM_ACCELERATED_CRC_MEMCPY_ENGINE
 
 }  // namespace crc_internal
 ABSL_NAMESPACE_END
diff --git a/absl/crc/internal/crc_memcpy_test.cc b/absl/crc/internal/crc_memcpy_test.cc
index bbdcd20..045a1e5 100644
--- a/absl/crc/internal/crc_memcpy_test.cc
+++ b/absl/crc/internal/crc_memcpy_test.cc
@@ -33,7 +33,7 @@
 namespace {
 
 enum CrcEngine {
-  X86 = 0,
+  ACCELERATED = 0,
   NONTEMPORAL = 1,
   FALLBACK = 2,
 };
@@ -66,10 +66,10 @@
 typedef CrcMemcpyTest<(1 << 24)> CrcLargeTest;
 // Parametrize the small test so that it can be done with all configurations.
 template <typename ParamsT>
-class x86ParamTestTemplate : public CrcSmallTest,
-                             public ::testing::WithParamInterface<ParamsT> {
+class EngineParamTestTemplate : public CrcSmallTest,
+                                public ::testing::WithParamInterface<ParamsT> {
  protected:
-  x86ParamTestTemplate() {
+  EngineParamTestTemplate() {
     if (GetParam().crc_engine_selector == FALLBACK) {
       engine_ = std::make_unique<absl::crc_internal::FallbackCrcMemcpyEngine>();
     } else if (GetParam().crc_engine_selector == NONTEMPORAL) {
@@ -89,14 +89,14 @@
   std::unique_ptr<absl::crc_internal::CrcMemcpyEngine> engine_;
 };
 struct TestParams {
-  CrcEngine crc_engine_selector = X86;
+  CrcEngine crc_engine_selector = ACCELERATED;
   int vector_lanes = 0;
   int integer_lanes = 0;
 };
-using x86ParamTest = x86ParamTestTemplate<TestParams>;
+using EngineParamTest = EngineParamTestTemplate<TestParams>;
 // SmallCorrectness is designed to exercise every possible set of code paths
 // in the memcpy code, not including the loop.
-TEST_P(x86ParamTest, SmallCorrectnessCheckSourceAlignment) {
+TEST_P(EngineParamTest, SmallCorrectnessCheckSourceAlignment) {
   constexpr size_t kTestSizes[] = {0, 100, 255, 512, 1024, 4000, kMaxCopySize};
 
   for (size_t source_alignment = 0; source_alignment < kAlignment;
@@ -107,6 +107,9 @@
         *(base_data + i) =
             static_cast<char>(absl::Uniform<unsigned char>(gen_));
       }
+      SCOPED_TRACE(absl::StrCat("engine=<", GetParam().vector_lanes, ",",
+                                GetParam().integer_lanes, ">, ", "size=", size,
+                                ", source_alignment=", source_alignment));
       absl::crc32c_t initial_crc =
           absl::crc32c_t{absl::Uniform<uint32_t>(gen_)};
       absl::crc32c_t experiment_crc =
@@ -127,7 +130,7 @@
   }
 }
 
-TEST_P(x86ParamTest, SmallCorrectnessCheckDestAlignment) {
+TEST_P(EngineParamTest, SmallCorrectnessCheckDestAlignment) {
   constexpr size_t kTestSizes[] = {0, 100, 255, 512, 1024, 4000, kMaxCopySize};
 
   for (size_t dest_alignment = 0; dest_alignment < kAlignment;
@@ -138,6 +141,9 @@
         *(base_data + i) =
             static_cast<char>(absl::Uniform<unsigned char>(gen_));
       }
+      SCOPED_TRACE(absl::StrCat("engine=<", GetParam().vector_lanes, ",",
+                                GetParam().integer_lanes, ">, ", "size=", size,
+                                ", destination_alignment=", dest_alignment));
       absl::crc32c_t initial_crc =
           absl::crc32c_t{absl::Uniform<uint32_t>(gen_)};
       absl::crc32c_t experiment_crc =
@@ -157,10 +163,12 @@
   }
 }
 
-INSTANTIATE_TEST_SUITE_P(x86ParamTest, x86ParamTest,
+INSTANTIATE_TEST_SUITE_P(EngineParamTest, EngineParamTest,
                          ::testing::Values(
                              // Tests for configurations that may occur in prod.
-                             TestParams{X86, 3, 0}, TestParams{X86, 1, 2},
+                             TestParams{ACCELERATED, 3, 0},
+                             TestParams{ACCELERATED, 1, 2},
+                             TestParams{ACCELERATED, 1, 0},
                              // Fallback test.
                              TestParams{FALLBACK, 0, 0},
                              // Non Temporal
diff --git a/absl/crc/internal/crc_memcpy_x86_64.cc b/absl/crc/internal/crc_memcpy_x86_arm_combined.cc
similarity index 84%
rename from absl/crc/internal/crc_memcpy_x86_64.cc
rename to absl/crc/internal/crc_memcpy_x86_arm_combined.cc
index d42b08d..968e9ae 100644
--- a/absl/crc/internal/crc_memcpy_x86_64.cc
+++ b/absl/crc/internal/crc_memcpy_x86_arm_combined.cc
@@ -12,9 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// Simultaneous memcopy and CRC-32C for x86-64.  Uses integer registers because
-// XMM registers do not support the CRC instruction (yet).  While copying,
-// compute the running CRC of the data being copied.
+// Simultaneous memcopy and CRC-32C for x86-64 and ARM 64. Uses integer
+// registers because XMM registers do not support the CRC instruction (yet).
+// While copying, compute the running CRC of the data being copied.
 //
 // It is assumed that any CPU running this code has SSE4.2 instructions
 // available (for CRC32C).  This file will do nothing if that is not true.
@@ -49,17 +49,20 @@
 #include <array>
 #include <cstddef>
 #include <cstdint>
-#include <type_traits>
+#include <cstring>
+#include <memory>
 
-#include "absl/base/dynamic_annotations.h"
+#include "absl/base/config.h"
 #include "absl/base/optimization.h"
 #include "absl/base/prefetch.h"
 #include "absl/crc/crc32c.h"
 #include "absl/crc/internal/cpu_detect.h"
+#include "absl/crc/internal/crc32_x86_arm_combined_simd.h"
 #include "absl/crc/internal/crc_memcpy.h"
 #include "absl/strings/string_view.h"
 
-#ifdef ABSL_INTERNAL_HAVE_X86_64_ACCELERATED_CRC_MEMCPY_ENGINE
+#if defined(ABSL_INTERNAL_HAVE_X86_64_ACCELERATED_CRC_MEMCPY_ENGINE) || \
+    defined(ABSL_INTERNAL_HAVE_ARM_ACCELERATED_CRC_MEMCPY_ENGINE)
 
 namespace absl {
 ABSL_NAMESPACE_BEGIN
@@ -74,7 +77,7 @@
   uint32_t crc_uint32 = static_cast<uint32_t>(crc);
   for (std::size_t i = 0; i < length; i++) {
     uint8_t data = *reinterpret_cast<const uint8_t*>(src);
-    crc_uint32 = _mm_crc32_u8(crc_uint32, data);
+    crc_uint32 = CRC32_u8(crc_uint32, data);
     *reinterpret_cast<uint8_t*>(dst) = data;
     ++src;
     ++dst;
@@ -82,36 +85,35 @@
   return crc32c_t{crc_uint32};
 }
 
-constexpr size_t kIntLoadsPerVec = sizeof(__m128i) / sizeof(uint64_t);
+constexpr size_t kIntLoadsPerVec = sizeof(V128) / sizeof(uint64_t);
 
 // Common function for copying the tails of multiple large regions.
 template <size_t vec_regions, size_t int_regions>
 inline void LargeTailCopy(crc32c_t* crcs, char** dst, const char** src,
                           size_t region_size, size_t copy_rounds) {
-  std::array<__m128i, vec_regions> data;
+  std::array<V128, vec_regions> data;
   std::array<uint64_t, kIntLoadsPerVec * int_regions> int_data;
 
   while (copy_rounds > 0) {
     for (size_t i = 0; i < vec_regions; i++) {
       size_t region = i;
 
-      auto* vsrc =
-          reinterpret_cast<const __m128i*>(*src + region_size * region);
-      auto* vdst = reinterpret_cast<__m128i*>(*dst + region_size * region);
+      auto* vsrc = reinterpret_cast<const V128*>(*src + region_size * region);
+      auto* vdst = reinterpret_cast<V128*>(*dst + region_size * region);
 
       // Load the blocks, unaligned
-      data[i] = _mm_loadu_si128(vsrc);
+      data[i] = V128_LoadU(vsrc);
 
       // Store the blocks, aligned
-      _mm_store_si128(vdst, data[i]);
+      V128_Store(vdst, data[i]);
 
       // Compute the running CRC
       crcs[region] = crc32c_t{static_cast<uint32_t>(
-          _mm_crc32_u64(static_cast<uint32_t>(crcs[region]),
-                        static_cast<uint64_t>(_mm_extract_epi64(data[i], 0))))};
+          CRC32_u64(static_cast<uint32_t>(crcs[region]),
+                    static_cast<uint64_t>(V128_Extract64<0>(data[i]))))};
       crcs[region] = crc32c_t{static_cast<uint32_t>(
-          _mm_crc32_u64(static_cast<uint32_t>(crcs[region]),
-                        static_cast<uint64_t>(_mm_extract_epi64(data[i], 1))))};
+          CRC32_u64(static_cast<uint32_t>(crcs[region]),
+                    static_cast<uint64_t>(V128_Extract64<1>(data[i]))))};
     }
 
     for (size_t i = 0; i < int_regions; i++) {
@@ -125,7 +127,7 @@
         size_t data_index = i * kIntLoadsPerVec + j;
 
         int_data[data_index] = *(usrc + j);
-        crcs[region] = crc32c_t{static_cast<uint32_t>(_mm_crc32_u64(
+        crcs[region] = crc32c_t{static_cast<uint32_t>(CRC32_u64(
             static_cast<uint32_t>(crcs[region]), int_data[data_index]))};
 
         *(udst + j) = int_data[data_index];
@@ -133,8 +135,8 @@
     }
 
     // Increment pointers
-    *src += sizeof(__m128i);
-    *dst += sizeof(__m128i);
+    *src += sizeof(V128);
+    *dst += sizeof(V128);
     --copy_rounds;
   }
 }
@@ -158,8 +160,9 @@
     void* __restrict dst, const void* __restrict src, std::size_t length,
     crc32c_t initial_crc) const {
   constexpr std::size_t kRegions = vec_regions + int_regions;
+  static_assert(kRegions > 0, "Must specify at least one region.");
   constexpr uint32_t kCrcDataXor = uint32_t{0xffffffff};
-  constexpr std::size_t kBlockSize = sizeof(__m128i);
+  constexpr std::size_t kBlockSize = sizeof(V128);
   constexpr std::size_t kCopyRoundSize = kRegions * kBlockSize;
 
   // Number of blocks per cacheline.
@@ -235,7 +238,7 @@
   const std::size_t tail_size = length - (kRegions * region_size);
 
   // Holding registers for data in each region.
-  std::array<__m128i, vec_regions> vec_data;
+  std::array<V128, vec_regions> vec_data;
   std::array<uint64_t, int_regions * kIntLoadsPerVec> int_data;
 
   // Main loop.
@@ -243,7 +246,10 @@
     // Prefetch kPrefetchAhead bytes ahead of each pointer.
     for (size_t i = 0; i < kRegions; i++) {
       absl::PrefetchToLocalCache(src_bytes + kPrefetchAhead + region_size * i);
+#ifdef ABSL_INTERNAL_HAVE_X86_64_ACCELERATED_CRC_MEMCPY_ENGINE
+      // TODO(b/297082454): investigate dropping prefetch on x86.
       absl::PrefetchToLocalCache(dst_bytes + kPrefetchAhead + region_size * i);
+#endif
     }
 
     // Load and store data, computing CRC on the way.
@@ -256,21 +262,20 @@
         size_t region = (j + i) % kRegions;
 
         auto* vsrc =
-            reinterpret_cast<const __m128i*>(src_bytes + region_size * region);
-        auto* vdst =
-            reinterpret_cast<__m128i*>(dst_bytes + region_size * region);
+            reinterpret_cast<const V128*>(src_bytes + region_size * region);
+        auto* vdst = reinterpret_cast<V128*>(dst_bytes + region_size * region);
 
         // Load and CRC data.
-        vec_data[j] = _mm_loadu_si128(vsrc + i);
-        crcs[region] = crc32c_t{static_cast<uint32_t>(_mm_crc32_u64(
-            static_cast<uint32_t>(crcs[region]),
-            static_cast<uint64_t>(_mm_extract_epi64(vec_data[j], 0))))};
-        crcs[region] = crc32c_t{static_cast<uint32_t>(_mm_crc32_u64(
-            static_cast<uint32_t>(crcs[region]),
-            static_cast<uint64_t>(_mm_extract_epi64(vec_data[j], 1))))};
+        vec_data[j] = V128_LoadU(vsrc + i);
+        crcs[region] = crc32c_t{static_cast<uint32_t>(
+            CRC32_u64(static_cast<uint32_t>(crcs[region]),
+                      static_cast<uint64_t>(V128_Extract64<0>(vec_data[j]))))};
+        crcs[region] = crc32c_t{static_cast<uint32_t>(
+            CRC32_u64(static_cast<uint32_t>(crcs[region]),
+                      static_cast<uint64_t>(V128_Extract64<1>(vec_data[j]))))};
 
         // Store the data.
-        _mm_store_si128(vdst + i, vec_data[j]);
+        V128_Store(vdst + i, vec_data[j]);
       }
 
       // Preload the partial CRCs for the CLMUL subregions.
@@ -290,7 +295,7 @@
 
           // Load and CRC the data.
           int_data[data_index] = *(usrc + i * kIntLoadsPerVec + k);
-          crcs[region] = crc32c_t{static_cast<uint32_t>(_mm_crc32_u64(
+          crcs[region] = crc32c_t{static_cast<uint32_t>(CRC32_u64(
               static_cast<uint32_t>(crcs[region]), int_data[data_index]))};
 
           // Store the data.
@@ -313,6 +318,21 @@
   src_bytes += region_size * (kRegions - 1);
   dst_bytes += region_size * (kRegions - 1);
 
+  // Copy and CRC the tail through the XMM registers.
+  std::size_t tail_blocks = tail_size / kBlockSize;
+  LargeTailCopy<0, 1>(&crcs[kRegions - 1], &dst_bytes, &src_bytes, 0,
+                      tail_blocks);
+
+  // Final tail copy for under 16 bytes.
+  crcs[kRegions - 1] =
+      ShortCrcCopy(dst_bytes, src_bytes, tail_size - tail_blocks * kBlockSize,
+                   crcs[kRegions - 1]);
+
+  if (kRegions == 1) {
+    // If there is only one region, finalize and return its CRC.
+    return crc32c_t{static_cast<uint32_t>(crcs[0]) ^ kCrcDataXor};
+  }
+
   // Finalize the first CRCs: XOR the internal CRCs by the XOR mask to undo the
   // XOR done before doing block copy + CRCs.
   for (size_t i = 0; i + 1 < kRegions; i++) {
@@ -325,16 +345,6 @@
     full_crc = ConcatCrc32c(full_crc, crcs[i], region_size);
   }
 
-  // Copy and CRC the tail through the XMM registers.
-  std::size_t tail_blocks = tail_size / kBlockSize;
-  LargeTailCopy<0, 1>(&crcs[kRegions - 1], &dst_bytes, &src_bytes, 0,
-                      tail_blocks);
-
-  // Final tail copy for under 16 bytes.
-  crcs[kRegions - 1] =
-      ShortCrcCopy(dst_bytes, src_bytes, tail_size - tail_blocks * kBlockSize,
-                   crcs[kRegions - 1]);
-
   // Finalize and concatenate the final CRC, then return.
   crcs[kRegions - 1] =
       crc32c_t{static_cast<uint32_t>(crcs[kRegions - 1]) ^ kCrcDataXor};
@@ -347,9 +357,11 @@
   // Get the underlying architecture.
   CpuType cpu_type = GetCpuType();
   switch (cpu_type) {
-    case CpuType::kUnknown:
     case CpuType::kAmdRome:
     case CpuType::kAmdNaples:
+    case CpuType::kAmdMilan:
+    case CpuType::kAmdGenoa:
+    case CpuType::kAmdRyzenV3000:
     case CpuType::kIntelCascadelakeXeon:
     case CpuType::kIntelSkylakeXeon:
     case CpuType::kIntelSkylake:
@@ -385,6 +397,9 @@
     // strided access to each region, and do the right thing.
     case CpuType::kAmdRome:
     case CpuType::kAmdNaples:
+    case CpuType::kAmdMilan:
+    case CpuType::kAmdGenoa:
+    case CpuType::kAmdRyzenV3000:
       return {
           /*.temporal=*/new AcceleratedCrcMemcpyEngine<1, 2>(),
           /*.non_temporal=*/new CrcNonTemporalMemcpyAVXEngine(),
@@ -421,6 +436,8 @@
     return std::make_unique<AcceleratedCrcMemcpyEngine<3, 0>>();
   } else if (vector == 1 && integer == 2) {
     return std::make_unique<AcceleratedCrcMemcpyEngine<1, 2>>();
+  } else if (vector == 1 && integer == 0) {
+    return std::make_unique<AcceleratedCrcMemcpyEngine<1, 0>>();
   }
   return nullptr;
 }
@@ -429,4 +446,5 @@
 ABSL_NAMESPACE_END
 }  // namespace absl
 
-#endif  // ABSL_INTERNAL_HAVE_X86_64_ACCELERATED_CRC_MEMCPY_ENGINE
+#endif  // ABSL_INTERNAL_HAVE_X86_64_ACCELERATED_CRC_MEMCPY_ENGINE ||
+        // ABSL_INTERNAL_HAVE_ARM_ACCELERATED_CRC_MEMCPY_ENGINE
diff --git a/absl/crc/internal/crc_x86_arm_combined.cc b/absl/crc/internal/crc_x86_arm_combined.cc
index ef521d2..51eff4e 100644
--- a/absl/crc/internal/crc_x86_arm_combined.cc
+++ b/absl/crc/internal/crc_x86_arm_combined.cc
@@ -16,14 +16,14 @@
 
 #include <cstddef>
 #include <cstdint>
+#include <memory>
+#include <vector>
 
 #include "absl/base/attributes.h"
 #include "absl/base/config.h"
-#include "absl/base/dynamic_annotations.h"
 #include "absl/base/internal/endian.h"
 #include "absl/base/prefetch.h"
 #include "absl/crc/internal/cpu_detect.h"
-#include "absl/crc/internal/crc.h"
 #include "absl/crc/internal/crc32_x86_arm_combined_simd.h"
 #include "absl/crc/internal/crc_internal.h"
 #include "absl/memory/memory.h"
@@ -634,8 +634,16 @@
       return new CRC32AcceleratedX86ARMCombinedMultipleStreams<
           3, 0, CutoffStrategy::Fold3>();
     case CpuType::kArmNeoverseN1:
+    case CpuType::kArmNeoverseN2:
+    case CpuType::kArmNeoverseV1:
       return new CRC32AcceleratedX86ARMCombinedMultipleStreams<
           1, 1, CutoffStrategy::Unroll64CRC>();
+    case CpuType::kAmpereSiryn:
+      return new CRC32AcceleratedX86ARMCombinedMultipleStreams<
+          3, 2, CutoffStrategy::Fold3>();
+    case CpuType::kArmNeoverseV2:
+      return new CRC32AcceleratedX86ARMCombinedMultipleStreams<
+          1, 2, CutoffStrategy::Unroll64CRC>();
 #if defined(__aarch64__)
     default:
       // Not all ARM processors support the needed instructions, so check here
diff --git a/absl/debugging/BUILD.bazel b/absl/debugging/BUILD.bazel
index 42124bf..5baff7a 100644
--- a/absl/debugging/BUILD.bazel
+++ b/absl/debugging/BUILD.bazel
@@ -23,6 +23,11 @@
 
 package(
     default_visibility = ["//visibility:public"],
+    features = [
+        "header_modules",
+        "layering_check",
+        "parse_headers",
+    ],
 )
 
 licenses(["notice"])
@@ -62,6 +67,7 @@
     deps = [
         ":stacktrace",
         "//absl/base:core_headers",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -217,7 +223,10 @@
     hdrs = ["internal/demangle.h"],
     copts = ABSL_DEFAULT_COPTS,
     linkopts = ABSL_DEFAULT_LINKOPTS,
-    visibility = ["//visibility:private"],
+    visibility = [
+        "//absl/container:__pkg__",
+        "//absl/debugging:__pkg__",
+    ],
     deps = [
         "//absl/base",
         "//absl/base:config",
@@ -237,6 +246,7 @@
         "//absl/base:core_headers",
         "//absl/log",
         "//absl/memory",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -263,6 +273,7 @@
         ":leak_check",
         "//absl/base:config",
         "//absl/log",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -280,6 +291,7 @@
     deps = [
         ":leak_check",
         "//absl/log",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -309,6 +321,7 @@
         ":stack_consumption",
         "//absl/base:core_headers",
         "//absl/log",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
diff --git a/absl/debugging/failure_signal_handler.cc b/absl/debugging/failure_signal_handler.cc
index 992c89c..570d1e5 100644
--- a/absl/debugging/failure_signal_handler.cc
+++ b/absl/debugging/failure_signal_handler.cc
@@ -54,7 +54,7 @@
 #include "absl/debugging/internal/examine_stack.h"
 #include "absl/debugging/stacktrace.h"
 
-#ifndef _WIN32
+#if !defined(_WIN32) && !defined(__wasi__)
 #define ABSL_HAVE_SIGACTION
 // Apple WatchOS and TVOS don't allow sigaltstack
 // Apple macOS has sigaltstack, but using it makes backtrace() unusable.
diff --git a/absl/debugging/internal/address_is_readable.cc b/absl/debugging/internal/address_is_readable.cc
index 91eaa76..be17a10 100644
--- a/absl/debugging/internal/address_is_readable.cc
+++ b/absl/debugging/internal/address_is_readable.cc
@@ -50,8 +50,10 @@
 // NOTE: any new system calls here may also require sandbox reconfiguration.
 //
 bool AddressIsReadable(const void *addr) {
-  // Align address on 8-byte boundary. On aarch64, checking last
-  // byte before inaccessible page returned unexpected EFAULT.
+  // rt_sigprocmask below checks 8 contiguous bytes. If addr resides in the
+  // last 7 bytes of a page (unaligned), rt_sigprocmask would additionally
+  // check the readability of the next page, which is not desired. Align
+  // address on 8-byte boundary to check only the current page.
   const uintptr_t u_addr = reinterpret_cast<uintptr_t>(addr) & ~uintptr_t{7};
   addr = reinterpret_cast<const void *>(u_addr);
 
diff --git a/absl/debugging/internal/demangle.cc b/absl/debugging/internal/demangle.cc
index f283291..381a2b5 100644
--- a/absl/debugging/internal/demangle.cc
+++ b/absl/debugging/internal/demangle.cc
@@ -21,7 +21,15 @@
 
 #include <cstdint>
 #include <cstdio>
+#include <cstdlib>
 #include <limits>
+#include <string>
+
+#include "absl/base/config.h"
+
+#if ABSL_INTERNAL_HAS_CXA_DEMANGLE
+#include <cxxabi.h>
+#endif
 
 namespace absl {
 ABSL_NAMESPACE_BEGIN
@@ -1983,6 +1991,22 @@
          state.parse_state.out_cur_idx > 0;
 }
 
+std::string DemangleString(const char* mangled) {
+  std::string out;
+  int status = 0;
+  char* demangled = nullptr;
+#if ABSL_INTERNAL_HAS_CXA_DEMANGLE
+  demangled = abi::__cxa_demangle(mangled, nullptr, nullptr, &status);
+#endif
+  if (status == 0 && demangled != nullptr) {
+    out.append(demangled);
+    free(demangled);
+  } else {
+    out.append(mangled);
+  }
+  return out;
+}
+
 }  // namespace debugging_internal
 ABSL_NAMESPACE_END
 }  // namespace absl
diff --git a/absl/debugging/internal/demangle.h b/absl/debugging/internal/demangle.h
index e1f1569..146d115 100644
--- a/absl/debugging/internal/demangle.h
+++ b/absl/debugging/internal/demangle.h
@@ -12,13 +12,23 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// An async-signal-safe and thread-safe demangler for Itanium C++ ABI
-// (aka G++ V3 ABI).
+#ifndef ABSL_DEBUGGING_INTERNAL_DEMANGLE_H_
+#define ABSL_DEBUGGING_INTERNAL_DEMANGLE_H_
+
+#include <string>
+#include "absl/base/config.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace debugging_internal {
+
+// Demangle `mangled`.  On success, return true and write the
+// demangled symbol name to `out`.  Otherwise, return false.
+// `out` is modified even if demangling is unsuccessful.
 //
-// The demangler is implemented to be used in async signal handlers to
-// symbolize stack traces.  We cannot use libstdc++'s
-// abi::__cxa_demangle() in such signal handlers since it's not async
-// signal safe (it uses malloc() internally).
+// This function provides an alternative to libstdc++'s abi::__cxa_demangle,
+// which is not async signal safe (it uses malloc internally).  It's intended to
+// be used in async signal handlers to symbolize stack traces.
 //
 // Note that this demangler doesn't support full demangling.  More
 // specifically, it doesn't print types of function parameters and
@@ -30,40 +40,32 @@
 //
 // Example:
 //
-// | Mangled Name  | The Demangler | abi::__cxa_demangle()
-// |---------------|---------------|-----------------------
-// | _Z1fv         | f()           | f()
-// | _Z1fi         | f()           | f(int)
-// | _Z3foo3bar    | foo()         | foo(bar)
-// | _Z1fIiEvi     | f<>()         | void f<int>(int)
-// | _ZN1N1fE      | N::f          | N::f
-// | _ZN3Foo3BarEv | Foo::Bar()    | Foo::Bar()
-// | _Zrm1XS_"     | operator%()   | operator%(X, X)
-// | _ZN3FooC1Ev   | Foo::Foo()    | Foo::Foo()
-// | _Z1fSs        | f()           | f(std::basic_string<char,
-// |               |               |   std::char_traits<char>,
-// |               |               |   std::allocator<char> >)
+// | Mangled Name  | Demangle    | DemangleString
+// |---------------|-------------|-----------------------
+// | _Z1fv         | f()         | f()
+// | _Z1fi         | f()         | f(int)
+// | _Z3foo3bar    | foo()       | foo(bar)
+// | _Z1fIiEvi     | f<>()       | void f<int>(int)
+// | _ZN1N1fE      | N::f        | N::f
+// | _ZN3Foo3BarEv | Foo::Bar()  | Foo::Bar()
+// | _Zrm1XS_"     | operator%() | operator%(X, X)
+// | _ZN3FooC1Ev   | Foo::Foo()  | Foo::Foo()
+// | _Z1fSs        | f()         | f(std::basic_string<char,
+// |               |             |   std::char_traits<char>,
+// |               |             |   std::allocator<char> >)
 //
 // See the unit test for more examples.
 //
 // Note: we might want to write demanglers for ABIs other than Itanium
 // C++ ABI in the future.
-//
-
-#ifndef ABSL_DEBUGGING_INTERNAL_DEMANGLE_H_
-#define ABSL_DEBUGGING_INTERNAL_DEMANGLE_H_
-
-#include "absl/base/config.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace debugging_internal {
-
-// Demangle `mangled`.  On success, return true and write the
-// demangled symbol name to `out`.  Otherwise, return false.
-// `out` is modified even if demangling is unsuccessful.
 bool Demangle(const char* mangled, char* out, size_t out_size);
 
+// A wrapper around `abi::__cxa_demangle()`.  On success, returns the demangled
+// name.  On failure, returns the input mangled name.
+//
+// This function is not async-signal-safe.
+std::string DemangleString(const char* mangled);
+
 }  // namespace debugging_internal
 ABSL_NAMESPACE_END
 }  // namespace absl
diff --git a/absl/debugging/internal/demangle_test.cc b/absl/debugging/internal/demangle_test.cc
index faec72b..a16ab75 100644
--- a/absl/debugging/internal/demangle_test.cc
+++ b/absl/debugging/internal/demangle_test.cc
@@ -17,6 +17,7 @@
 #include <cstdlib>
 #include <string>
 
+#include "gmock/gmock.h"
 #include "gtest/gtest.h"
 #include "absl/base/config.h"
 #include "absl/debugging/internal/stack_consumption.h"
@@ -28,15 +29,7 @@
 namespace debugging_internal {
 namespace {
 
-// A wrapper function for Demangle() to make the unit test simple.
-static const char *DemangleIt(const char * const mangled) {
-  static char demangled[4096];
-  if (Demangle(mangled, demangled, sizeof(demangled))) {
-    return demangled;
-  } else {
-    return mangled;
-  }
-}
+using ::testing::ContainsRegex;
 
 // Test corner cases of boundary conditions.
 TEST(Demangle, CornerCases) {
@@ -237,6 +230,25 @@
   TestOnInput(data.c_str());
 }
 
+struct Base {
+  virtual ~Base() = default;
+};
+
+struct Derived : public Base {};
+
+TEST(DemangleStringTest, SupportsSymbolNameReturnedByTypeId) {
+  EXPECT_EQ(DemangleString(typeid(int).name()), "int");
+  // We want to test that `DemangleString` can demangle the symbol names
+  // returned by `typeid`, but without hard-coding the actual demangled values
+  // (because they are platform-specific).
+  EXPECT_THAT(
+      DemangleString(typeid(Base).name()),
+      ContainsRegex("absl.*debugging_internal.*anonymous namespace.*::Base"));
+  EXPECT_THAT(DemangleString(typeid(Derived).name()),
+              ContainsRegex(
+                  "absl.*debugging_internal.*anonymous namespace.*::Derived"));
+}
+
 }  // namespace
 }  // namespace debugging_internal
 ABSL_NAMESPACE_END
diff --git a/absl/debugging/internal/stack_consumption.cc b/absl/debugging/internal/stack_consumption.cc
index 3f40bea..b54a1b2 100644
--- a/absl/debugging/internal/stack_consumption.cc
+++ b/absl/debugging/internal/stack_consumption.cc
@@ -185,4 +185,22 @@
 ABSL_NAMESPACE_END
 }  // namespace absl
 
+#else
+
+// https://github.com/abseil/abseil-cpp/issues/1465
+// CMake builds on Apple platforms error when libraries are empty.
+// Our CMake configuration can avoid this error on header-only libraries,
+// but since this library is conditionally empty, including a single
+// variable is an easy workaround.
+#ifdef __APPLE__
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace debugging_internal {
+extern const char kAvoidEmptyStackConsumptionLibraryWarning;
+const char kAvoidEmptyStackConsumptionLibraryWarning = 0;
+}  // namespace debugging_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
+#endif  // __APPLE__
+
 #endif  // ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION
diff --git a/absl/debugging/internal/stacktrace_aarch64-inl.inc b/absl/debugging/internal/stacktrace_aarch64-inl.inc
index 3f08716..1caf7bb 100644
--- a/absl/debugging/internal/stacktrace_aarch64-inl.inc
+++ b/absl/debugging/internal/stacktrace_aarch64-inl.inc
@@ -4,6 +4,7 @@
 // Generate stack tracer for aarch64
 
 #if defined(__linux__)
+#include <signal.h>
 #include <sys/mman.h>
 #include <ucontext.h>
 #include <unistd.h>
@@ -70,7 +71,7 @@
 // Compute the size of a stack frame in [low..high).  We assume that
 // low < high.  Return size of kUnknownFrameSize.
 template<typename T>
-static inline size_t ComputeStackFrameSize(const T* low,
+static size_t ComputeStackFrameSize(const T* low,
                                            const T* high) {
   const char* low_char_ptr = reinterpret_cast<const char *>(low);
   const char* high_char_ptr = reinterpret_cast<const char *>(high);
@@ -78,6 +79,20 @@
                     : kUnknownFrameSize;
 }
 
+// Saves stack info that is expensive to calculate to avoid recalculating per frame.
+struct StackInfo {
+  uintptr_t stack_low;
+  uintptr_t stack_high;
+  uintptr_t sig_stack_low;
+  uintptr_t sig_stack_high;
+};
+
+static bool InsideSignalStack(void** ptr, const StackInfo* stack_info) {
+  uintptr_t comparable_ptr = reinterpret_cast<uintptr_t>(ptr);
+  return (comparable_ptr >= stack_info->sig_stack_low &&
+          comparable_ptr < stack_info->sig_stack_high);
+}
+
 // Given a pointer to a stack frame, locate and return the calling
 // stackframe, or return null if no stackframe can be found. Perform sanity
 // checks (the strictness of which is controlled by the boolean parameter
@@ -86,9 +101,8 @@
 ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS  // May read random elements from stack.
 ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY  // May read random elements from stack.
 static void **NextStackFrame(void **old_frame_pointer, const void *uc,
-                             size_t stack_low, size_t stack_high) {
+                             const StackInfo *stack_info) {
   void **new_frame_pointer = reinterpret_cast<void**>(*old_frame_pointer);
-  bool check_frame_size = true;
 
 #if defined(__linux__)
   if (WITH_CONTEXT && uc != nullptr) {
@@ -114,10 +128,6 @@
       if (!absl::debugging_internal::AddressIsReadable(
               new_frame_pointer))
         return nullptr;
-
-      // Skip frame size check if we return from a signal. We may be using a
-      // an alternate stack for signals.
-      check_frame_size = false;
     }
   }
 #endif
@@ -126,9 +136,11 @@
   if ((reinterpret_cast<uintptr_t>(new_frame_pointer) & 7) != 0)
     return nullptr;
 
-  // Check frame size.  In strict mode, we assume frames to be under
-  // 100,000 bytes.  In non-strict mode, we relax the limit to 1MB.
-  if (check_frame_size) {
+  // Only check the size if both frames are in the same stack.
+  if (InsideSignalStack(new_frame_pointer, stack_info) ==
+      InsideSignalStack(old_frame_pointer, stack_info)) {
+    // Check frame size.  In strict mode, we assume frames to be under
+    // 100,000 bytes.  In non-strict mode, we relax the limit to 1MB.
     const size_t max_size = STRICT_UNWINDING ? 100000 : 1000000;
     const size_t frame_size =
         ComputeStackFrameSize(old_frame_pointer, new_frame_pointer);
@@ -136,15 +148,21 @@
        return nullptr;
     // A very large frame may mean corrupt memory or an erroneous frame
     // pointer. But also maybe just a plain-old large frame.  Assume that if the
-    // frame is within the known stack, then it is valid.
+    // frame is within a known stack, then it is valid.
     if (frame_size > max_size) {
-       if (stack_high < kUnknownStackEnd &&
+      size_t stack_low = stack_info->stack_low;
+      size_t stack_high = stack_info->stack_high;
+      if (InsideSignalStack(new_frame_pointer, stack_info)) {
+        stack_low = stack_info->sig_stack_low;
+        stack_high = stack_info->sig_stack_high;
+      }
+      if (stack_high < kUnknownStackEnd &&
           static_cast<size_t>(getpagesize()) < stack_low) {
         const uintptr_t new_fp_u =
             reinterpret_cast<uintptr_t>(new_frame_pointer);
         // Stack bounds are known.
         if (!(stack_low < new_fp_u && new_fp_u <= stack_high)) {
-          // new_frame_pointer is not within the known stack.
+          // new_frame_pointer is not within a known stack.
           return nullptr;
         }
       } else {
@@ -158,6 +176,9 @@
 }
 
 template <bool IS_STACK_FRAMES, bool IS_WITH_CONTEXT>
+// We count on the bottom frame being this one. See the comment
+// at prev_return_address
+ABSL_ATTRIBUTE_NOINLINE
 ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS  // May read random elements from stack.
 ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY   // May read random elements from stack.
 static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count,
@@ -171,8 +192,11 @@
   int n = 0;
 
   // Assume that the first page is not stack.
-  size_t stack_low = static_cast<size_t>(getpagesize());
-  size_t stack_high = kUnknownStackEnd;
+  StackInfo stack_info;
+  stack_info.stack_low = static_cast<uintptr_t>(getpagesize());
+  stack_info.stack_high = kUnknownStackEnd;
+  stack_info.sig_stack_low = stack_info.stack_low;
+  stack_info.sig_stack_high = kUnknownStackEnd;
 
   // The frame pointer points to low address of a frame.  The first 64-bit
   // word of a frame points to the next frame up the call chain, which normally
@@ -207,7 +231,7 @@
     // that is as complete as possible (even if it contains a few bogus
     // entries in some rare cases).
     frame_pointer = NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(
-        frame_pointer, ucp, stack_low, stack_high);
+        frame_pointer, ucp, &stack_info);
   }
 
   if (min_dropped_frames != nullptr) {
@@ -222,7 +246,7 @@
         num_dropped_frames++;
       }
       frame_pointer = NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(
-          frame_pointer, ucp, stack_low, stack_high);
+          frame_pointer, ucp, &stack_info);
     }
     *min_dropped_frames = num_dropped_frames;
   }
diff --git a/absl/debugging/symbolize_elf.inc b/absl/debugging/symbolize_elf.inc
index 30638cb..ae75cd4 100644
--- a/absl/debugging/symbolize_elf.inc
+++ b/absl/debugging/symbolize_elf.inc
@@ -289,6 +289,30 @@
   return new (&obj_[size_++]) ObjFile;
 }
 
+class CachingFile {
+ public:
+  // Setup reader for fd that uses buf[0, buf_size-1] as a cache.
+  CachingFile(int fd, char *buf, size_t buf_size)
+      : fd_(fd),
+        cache_(buf),
+        cache_size_(buf_size),
+        cache_start_(0),
+        cache_limit_(0) {}
+
+  int fd() const { return fd_; }
+  ssize_t ReadFromOffset(void *buf, size_t count, off_t offset);
+  bool ReadFromOffsetExact(void *buf, size_t count, off_t offset);
+
+ private:
+  // Bytes [cache_start_, cache_limit_-1] from fd_ are stored in
+  // a prefix of cache_[0, cache_size_-1].
+  int fd_;
+  char *cache_;
+  size_t cache_size_;
+  off_t cache_start_;
+  off_t cache_limit_;
+};
+
 // ---------------------------------------------------------------
 
 enum FindSymbolResult { SYMBOL_NOT_FOUND = 1, SYMBOL_TRUNCATED, SYMBOL_FOUND };
@@ -330,6 +354,7 @@
     SYMBOL_BUF_SIZE = 3072,
     TMP_BUF_SIZE = 1024,
     SYMBOL_CACHE_LINES = 128,
+    FILE_CACHE_SIZE = 8192,
   };
 
   AddrMap addr_map_;
@@ -338,6 +363,7 @@
   bool addr_map_read_;
 
   char symbol_buf_[SYMBOL_BUF_SIZE];
+  char file_cache_[FILE_CACHE_SIZE];
 
   // tmp_buf_ will be used to store arrays of ElfW(Shdr) and ElfW(Sym)
   // so we ensure that tmp_buf_ is properly aligned to store either.
@@ -436,34 +462,58 @@
   return static_cast<ssize_t>(num_bytes);
 }
 
-// Read up to "count" bytes from "offset" in the file pointed by file
-// descriptor "fd" into the buffer starting at "buf".  On success,
-// return the number of bytes read.  Otherwise, return -1.
-static ssize_t ReadFromOffset(const int fd, void *buf, const size_t count,
-                              const off_t offset) {
-  off_t off = lseek(fd, offset, SEEK_SET);
-  if (off == (off_t)-1) {
-    ABSL_RAW_LOG(WARNING, "lseek(%d, %jd, SEEK_SET) failed: errno=%d", fd,
-                 static_cast<intmax_t>(offset), errno);
-    return -1;
+// Read up to "count" bytes from "offset" into the buffer starting at "buf",
+// while handling short reads and EINTR.  On success, return the number of bytes
+// read.  Otherwise, return -1.
+ssize_t CachingFile::ReadFromOffset(void *buf, size_t count, off_t offset) {
+  char *dst = static_cast<char *>(buf);
+  size_t read = 0;
+  while (read < count) {
+    // Look in cache first.
+    if (offset >= cache_start_ && offset < cache_limit_) {
+      const char *hit_start = &cache_[offset - cache_start_];
+      const size_t n =
+          std::min(count - read, static_cast<size_t>(cache_limit_ - offset));
+      memcpy(dst, hit_start, n);
+      dst += n;
+      read += static_cast<size_t>(n);
+      offset += static_cast<off_t>(n);
+      continue;
+    }
+
+    cache_start_ = 0;
+    cache_limit_ = 0;
+    ssize_t n = pread(fd_, cache_, cache_size_, offset);
+    if (n < 0) {
+      if (errno == EINTR) {
+        continue;
+      }
+      ABSL_RAW_LOG(WARNING, "read failed: errno=%d", errno);
+      return -1;
+    }
+    if (n == 0) {  // Reached EOF.
+      break;
+    }
+
+    cache_start_ = offset;
+    cache_limit_ = offset + static_cast<off_t>(n);
+    // Next iteration will copy from cache into dst.
   }
-  return ReadPersistent(fd, buf, count);
+  return static_cast<ssize_t>(read);
 }
 
-// Try reading exactly "count" bytes from "offset" bytes in a file
-// pointed by "fd" into the buffer starting at "buf" while handling
-// short reads and EINTR.  On success, return true. Otherwise, return
-// false.
-static bool ReadFromOffsetExact(const int fd, void *buf, const size_t count,
-                                const off_t offset) {
-  ssize_t len = ReadFromOffset(fd, buf, count, offset);
+// Try reading exactly "count" bytes from "offset" bytes into the buffer
+// starting at "buf" while handling short reads and EINTR.  On success, return
+// true. Otherwise, return false.
+bool CachingFile::ReadFromOffsetExact(void *buf, size_t count, off_t offset) {
+  ssize_t len = ReadFromOffset(buf, count, offset);
   return len >= 0 && static_cast<size_t>(len) == count;
 }
 
 // Returns elf_header.e_type if the file pointed by fd is an ELF binary.
-static int FileGetElfType(const int fd) {
+static int FileGetElfType(CachingFile *file) {
   ElfW(Ehdr) elf_header;
-  if (!ReadFromOffsetExact(fd, &elf_header, sizeof(elf_header), 0)) {
+  if (!file->ReadFromOffsetExact(&elf_header, sizeof(elf_header), 0)) {
     return -1;
   }
   if (memcmp(elf_header.e_ident, ELFMAG, SELFMAG) != 0) {
@@ -478,8 +528,8 @@
 // To keep stack consumption low, we would like this function to not get
 // inlined.
 static ABSL_ATTRIBUTE_NOINLINE bool GetSectionHeaderByType(
-    const int fd, ElfW(Half) sh_num, const off_t sh_offset, ElfW(Word) type,
-    ElfW(Shdr) * out, char *tmp_buf, size_t tmp_buf_size) {
+    CachingFile *file, ElfW(Half) sh_num, const off_t sh_offset,
+    ElfW(Word) type, ElfW(Shdr) * out, char *tmp_buf, size_t tmp_buf_size) {
   ElfW(Shdr) *buf = reinterpret_cast<ElfW(Shdr) *>(tmp_buf);
   const size_t buf_entries = tmp_buf_size / sizeof(buf[0]);
   const size_t buf_bytes = buf_entries * sizeof(buf[0]);
@@ -490,7 +540,7 @@
     const size_t num_bytes_to_read =
         (buf_bytes > num_bytes_left) ? num_bytes_left : buf_bytes;
     const off_t offset = sh_offset + static_cast<off_t>(i * sizeof(buf[0]));
-    const ssize_t len = ReadFromOffset(fd, buf, num_bytes_to_read, offset);
+    const ssize_t len = file->ReadFromOffset(buf, num_bytes_to_read, offset);
     if (len < 0) {
       ABSL_RAW_LOG(
           WARNING,
@@ -524,11 +574,17 @@
 // but there has (as yet) been no need for anything longer either.
 const int kMaxSectionNameLen = 64;
 
+// Small cache to use for miscellaneous file reads.
+const int kSmallFileCacheSize = 100;
+
 bool ForEachSection(int fd,
                     const std::function<bool(absl::string_view name,
                                              const ElfW(Shdr) &)> &callback) {
+  char buf[kSmallFileCacheSize];
+  CachingFile file(fd, buf, sizeof(buf));
+
   ElfW(Ehdr) elf_header;
-  if (!ReadFromOffsetExact(fd, &elf_header, sizeof(elf_header), 0)) {
+  if (!file.ReadFromOffsetExact(&elf_header, sizeof(elf_header), 0)) {
     return false;
   }
 
@@ -540,7 +596,7 @@
   ElfW(Shdr) shstrtab;
   off_t shstrtab_offset = static_cast<off_t>(elf_header.e_shoff) +
                           elf_header.e_shentsize * elf_header.e_shstrndx;
-  if (!ReadFromOffsetExact(fd, &shstrtab, sizeof(shstrtab), shstrtab_offset)) {
+  if (!file.ReadFromOffsetExact(&shstrtab, sizeof(shstrtab), shstrtab_offset)) {
     return false;
   }
 
@@ -548,13 +604,13 @@
     ElfW(Shdr) out;
     off_t section_header_offset =
         static_cast<off_t>(elf_header.e_shoff) + elf_header.e_shentsize * i;
-    if (!ReadFromOffsetExact(fd, &out, sizeof(out), section_header_offset)) {
+    if (!file.ReadFromOffsetExact(&out, sizeof(out), section_header_offset)) {
       return false;
     }
     off_t name_offset = static_cast<off_t>(shstrtab.sh_offset) + out.sh_name;
     char header_name[kMaxSectionNameLen];
     ssize_t n_read =
-        ReadFromOffset(fd, &header_name, kMaxSectionNameLen, name_offset);
+        file.ReadFromOffset(&header_name, kMaxSectionNameLen, name_offset);
     if (n_read < 0) {
       return false;
     } else if (n_read > kMaxSectionNameLen) {
@@ -584,8 +640,10 @@
     return false;
   }
 
+  char buf[kSmallFileCacheSize];
+  CachingFile file(fd, buf, sizeof(buf));
   ElfW(Ehdr) elf_header;
-  if (!ReadFromOffsetExact(fd, &elf_header, sizeof(elf_header), 0)) {
+  if (!file.ReadFromOffsetExact(&elf_header, sizeof(elf_header), 0)) {
     return false;
   }
 
@@ -597,18 +655,18 @@
   ElfW(Shdr) shstrtab;
   off_t shstrtab_offset = static_cast<off_t>(elf_header.e_shoff) +
                           elf_header.e_shentsize * elf_header.e_shstrndx;
-  if (!ReadFromOffsetExact(fd, &shstrtab, sizeof(shstrtab), shstrtab_offset)) {
+  if (!file.ReadFromOffsetExact(&shstrtab, sizeof(shstrtab), shstrtab_offset)) {
     return false;
   }
 
   for (int i = 0; i < elf_header.e_shnum; ++i) {
     off_t section_header_offset =
         static_cast<off_t>(elf_header.e_shoff) + elf_header.e_shentsize * i;
-    if (!ReadFromOffsetExact(fd, out, sizeof(*out), section_header_offset)) {
+    if (!file.ReadFromOffsetExact(out, sizeof(*out), section_header_offset)) {
       return false;
     }
     off_t name_offset = static_cast<off_t>(shstrtab.sh_offset) + out->sh_name;
-    ssize_t n_read = ReadFromOffset(fd, &header_name, name_len, name_offset);
+    ssize_t n_read = file.ReadFromOffset(&header_name, name_len, name_offset);
     if (n_read < 0) {
       return false;
     } else if (static_cast<size_t>(n_read) != name_len) {
@@ -683,7 +741,7 @@
 // To keep stack consumption low, we would like this function to not get
 // inlined.
 static ABSL_ATTRIBUTE_NOINLINE FindSymbolResult FindSymbol(
-    const void *const pc, const int fd, char *out, size_t out_size,
+    const void *const pc, CachingFile *file, char *out, size_t out_size,
     ptrdiff_t relocation, const ElfW(Shdr) * strtab, const ElfW(Shdr) * symtab,
     const ElfW(Shdr) * opd, char *tmp_buf, size_t tmp_buf_size) {
   if (symtab == nullptr) {
@@ -716,7 +774,7 @@
     const size_t entries_in_chunk =
         std::min(num_remaining_symbols, buf_entries);
     const size_t bytes_in_chunk = entries_in_chunk * sizeof(buf[0]);
-    const ssize_t len = ReadFromOffset(fd, buf, bytes_in_chunk, offset);
+    const ssize_t len = file->ReadFromOffset(buf, bytes_in_chunk, offset);
     SAFE_ASSERT(len >= 0);
     SAFE_ASSERT(static_cast<size_t>(len) % sizeof(buf[0]) == 0);
     const size_t num_symbols_in_buf = static_cast<size_t>(len) / sizeof(buf[0]);
@@ -772,12 +830,12 @@
   if (found_match) {
     const off_t off =
         static_cast<off_t>(strtab->sh_offset) + best_match.st_name;
-    const ssize_t n_read = ReadFromOffset(fd, out, out_size, off);
+    const ssize_t n_read = file->ReadFromOffset(out, out_size, off);
     if (n_read <= 0) {
       // This should never happen.
       ABSL_RAW_LOG(WARNING,
-                   "Unable to read from fd %d at offset %lld: n_read = %zd", fd,
-                   static_cast<long long>(off), n_read);
+                   "Unable to read from fd %d at offset %lld: n_read = %zd",
+                   file->fd(), static_cast<long long>(off), n_read);
       return SYMBOL_NOT_FOUND;
     }
     ABSL_RAW_CHECK(static_cast<size_t>(n_read) <= out_size,
@@ -827,22 +885,24 @@
     }
   }
 
+  CachingFile file(obj.fd, file_cache_, sizeof(file_cache_));
+
   // Consult a regular symbol table, then fall back to the dynamic symbol table.
   for (const auto symbol_table_type : {SHT_SYMTAB, SHT_DYNSYM}) {
-    if (!GetSectionHeaderByType(obj.fd, obj.elf_header.e_shnum,
+    if (!GetSectionHeaderByType(&file, obj.elf_header.e_shnum,
                                 static_cast<off_t>(obj.elf_header.e_shoff),
                                 static_cast<ElfW(Word)>(symbol_table_type),
                                 &symtab, tmp_buf, tmp_buf_size)) {
       continue;
     }
-    if (!ReadFromOffsetExact(
-            obj.fd, &strtab, sizeof(strtab),
+    if (!file.ReadFromOffsetExact(
+            &strtab, sizeof(strtab),
             static_cast<off_t>(obj.elf_header.e_shoff +
                                symtab.sh_link * sizeof(symtab)))) {
       continue;
     }
     const FindSymbolResult rc =
-        FindSymbol(pc, obj.fd, out, out_size, relocation, &strtab, &symtab,
+        FindSymbol(pc, &file, out, out_size, relocation, &strtab, &symtab,
                    opd_ptr, tmp_buf, tmp_buf_size);
     if (rc != SYMBOL_NOT_FOUND) {
       return rc;
@@ -1323,15 +1383,19 @@
       ABSL_RAW_LOG(WARNING, "%s: open failed: errno=%d", obj->filename, errno);
       return false;
     }
-    obj->elf_type = FileGetElfType(obj->fd);
+
+    char buf[kSmallFileCacheSize];
+    CachingFile file(obj->fd, buf, sizeof(buf));
+
+    obj->elf_type = FileGetElfType(&file);
     if (obj->elf_type < 0) {
       ABSL_RAW_LOG(WARNING, "%s: wrong elf type: %d", obj->filename,
                    obj->elf_type);
       return false;
     }
 
-    if (!ReadFromOffsetExact(obj->fd, &obj->elf_header, sizeof(obj->elf_header),
-                             0)) {
+    if (!file.ReadFromOffsetExact(&obj->elf_header, sizeof(obj->elf_header),
+                                  0)) {
       ABSL_RAW_LOG(WARNING, "%s: failed to read elf header", obj->filename);
       return false;
     }
@@ -1341,7 +1405,7 @@
     size_t num_interesting_load_segments = 0;
     for (int j = 0; j < phnum; j++) {
       ElfW(Phdr) phdr;
-      if (!ReadFromOffsetExact(obj->fd, &phdr, sizeof(phdr), phoff)) {
+      if (!file.ReadFromOffsetExact(&phdr, sizeof(phdr), phoff)) {
         ABSL_RAW_LOG(WARNING, "%s: failed to read program header %d",
                      obj->filename, j);
         return false;
diff --git a/absl/flags/BUILD.bazel b/absl/flags/BUILD.bazel
index 50bf387..d3b0622 100644
--- a/absl/flags/BUILD.bazel
+++ b/absl/flags/BUILD.bazel
@@ -21,7 +21,14 @@
     "ABSL_TEST_COPTS",
 )
 
-package(default_visibility = ["//visibility:public"])
+package(
+    default_visibility = ["//visibility:public"],
+    features = [
+        "header_modules",
+        "layering_check",
+        "parse_headers",
+    ],
+)
 
 licenses(["notice"])
 
@@ -183,6 +190,7 @@
         ":private_handle_accessor",
         "//absl/base:config",
         "//absl/base:core_headers",
+        "//absl/base:no_destructor",
         "//absl/container:flat_hash_map",
         "//absl/strings",
         "//absl/synchronization",
@@ -221,10 +229,6 @@
 
 cc_library(
     name = "flag",
-    srcs = [
-        "flag.cc",
-        "internal/flag_msvc.inc",
-    ],
     hdrs = [
         "declare.h",
         "flag.h",
@@ -266,8 +270,8 @@
         ":reflection",
         "//absl/base:config",
         "//absl/base:core_headers",
-        "//absl/container:flat_hash_map",
         "//absl/strings",
+        "//absl/synchronization",
     ],
 )
 
@@ -346,6 +350,7 @@
         ":reflection",
         "//absl/memory",
         "//absl/strings",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -360,6 +365,7 @@
     linkopts = ABSL_DEFAULT_LINKOPTS,
     deps = [
         ":config",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -391,6 +397,7 @@
         "//absl/numeric:int128",
         "//absl/strings",
         "//absl/time",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -430,6 +437,7 @@
     linkopts = ABSL_DEFAULT_LINKOPTS,
     deps = [
         ":marshalling",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -437,6 +445,7 @@
 cc_test(
     name = "parse_test",
     size = "small",
+    timeout = "moderate",
     srcs = [
         "parse_test.cc",
     ],
@@ -458,6 +467,7 @@
         "//absl/log",
         "//absl/strings",
         "//absl/types:span",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -472,6 +482,7 @@
     linkopts = ABSL_DEFAULT_LINKOPTS,
     deps = [
         ":path_util",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -488,6 +499,7 @@
     deps = [
         ":program_name",
         "//absl/strings",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -515,6 +527,7 @@
         ":usage_internal",
         "//absl/memory",
         "//absl/strings",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -535,6 +548,7 @@
         "//absl/base",
         "//absl/container:fixed_array",
         "//absl/time",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -552,6 +566,7 @@
         ":path_util",
         ":program_name",
         "//absl/strings",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
diff --git a/absl/flags/CMakeLists.txt b/absl/flags/CMakeLists.txt
index b20463f..4495312 100644
--- a/absl/flags/CMakeLists.txt
+++ b/absl/flags/CMakeLists.txt
@@ -169,6 +169,7 @@
     absl::strings
     absl::synchronization
     absl::flat_hash_map
+    absl::no_destructor
 )
 
 # Internal-only target, do not depend on directly.
@@ -200,12 +201,9 @@
 absl_cc_library(
   NAME
     flags
-  SRCS
-    "flag.cc"
   HDRS
     "declare.h"
     "flag.h"
-    "internal/flag_msvc.inc"
   COPTS
     ${ABSL_DEFAULT_COPTS}
   LINKOPTS
@@ -243,7 +241,6 @@
     absl::flags_private_handle_accessor
     absl::flags_program_name
     absl::flags_reflection
-    absl::flat_hash_map
     absl::strings
     absl::synchronization
 )
diff --git a/absl/flags/declare.h b/absl/flags/declare.h
index d1437bb..8d2a856 100644
--- a/absl/flags/declare.h
+++ b/absl/flags/declare.h
@@ -40,13 +40,8 @@
 // Flag
 //
 // Forward declaration of the `absl::Flag` type for use in defining the macro.
-#if defined(_MSC_VER) && !defined(__clang__)
-template <typename T>
-class Flag;
-#else
 template <typename T>
 using Flag = flags_internal::Flag<T>;
-#endif
 
 ABSL_NAMESPACE_END
 }  // namespace absl
diff --git a/absl/flags/flag.cc b/absl/flags/flag.cc
deleted file mode 100644
index 531df12..0000000
--- a/absl/flags/flag.cc
+++ /dev/null
@@ -1,38 +0,0 @@
-//
-//  Copyright 2019 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/flags/flag.h"
-
-#include "absl/base/config.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-// This global mutex protects on-demand construction of flag objects in MSVC
-// builds.
-#if defined(_MSC_VER) && !defined(__clang__)
-
-namespace flags_internal {
-
-ABSL_CONST_INIT static absl::Mutex construction_guard(absl::kConstInit);
-
-absl::Mutex* GetGlobalConstructionGuard() { return &construction_guard; }
-
-}  // namespace flags_internal
-
-#endif
-
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/absl/flags/flag.h b/absl/flags/flag.h
index b7f94be..06ea693 100644
--- a/absl/flags/flag.h
+++ b/absl/flags/flag.h
@@ -71,12 +71,9 @@
 // For type support of Abseil Flags, see the marshalling.h header file, which
 // discusses supported standard types, optional flags, and additional Abseil
 // type support.
-#if !defined(_MSC_VER) || defined(__clang__)
+
 template <typename T>
 using Flag = flags_internal::Flag<T>;
-#else
-#include "absl/flags/internal/flag_msvc.inc"
-#endif
 
 // GetFlag()
 //
@@ -196,18 +193,12 @@
 // -----------------------------------------------------------------------------
 
 // ABSL_FLAG_IMPL macro definition conditional on ABSL_FLAGS_STRIP_NAMES
-#if !defined(_MSC_VER) || defined(__clang__)
 #define ABSL_FLAG_IMPL_FLAG_PTR(flag) flag
 #define ABSL_FLAG_IMPL_HELP_ARG(name)                      \
   absl::flags_internal::HelpArg<AbslFlagHelpGenFor##name>( \
       FLAGS_help_storage_##name)
 #define ABSL_FLAG_IMPL_DEFAULT_ARG(Type, name) \
   absl::flags_internal::DefaultArg<Type, AbslFlagDefaultGenFor##name>(0)
-#else
-#define ABSL_FLAG_IMPL_FLAG_PTR(flag) flag.GetImpl()
-#define ABSL_FLAG_IMPL_HELP_ARG(name) &AbslFlagHelpGenFor##name::NonConst
-#define ABSL_FLAG_IMPL_DEFAULT_ARG(Type, name) &AbslFlagDefaultGenFor##name::Gen
-#endif
 
 #if ABSL_FLAGS_STRIP_NAMES
 #define ABSL_FLAG_IMPL_FLAGNAME(txt) ""
diff --git a/absl/flags/flag_test.cc b/absl/flags/flag_test.cc
index f9cda02..8d14ba8 100644
--- a/absl/flags/flag_test.cc
+++ b/absl/flags/flag_test.cc
@@ -144,7 +144,6 @@
 using int128 = absl::int128;
 using uint128 = absl::uint128;
 
-#if !defined(_MSC_VER) || defined(__clang__)
 #define DEFINE_CONSTRUCTED_FLAG(T, dflt, dflt_kind)                        \
   constexpr flags::FlagDefaultArg f1default##T{                            \
       flags::FlagDefaultSrc{dflt}, flags::FlagDefaultKind::dflt_kind};     \
@@ -157,16 +156,6 @@
           flags::FlagDefaultKind::kGenFunc                                 \
     }                                                                      \
   }
-#else
-#define DEFINE_CONSTRUCTED_FLAG(T, dflt, dflt_kind)                    \
-  constexpr flags::FlagDefaultArg f1default##T{                        \
-      flags::FlagDefaultSrc{dflt}, flags::FlagDefaultKind::dflt_kind}; \
-  constexpr absl::Flag<T> f1##T{"f1", "file", &TestLiteralHelpMsg,     \
-                                &TestMakeDflt<T>};                     \
-  ABSL_CONST_INIT absl::Flag<T> f2##T {                                \
-    "f2", "file", &TestHelpMsg, &TestMakeDflt<T>                       \
-  }
-#endif
 
 DEFINE_CONSTRUCTED_FLAG(bool, true, kOneWord);
 DEFINE_CONSTRUCTED_FLAG(int16_t, 1, kOneWord);
diff --git a/absl/flags/internal/flag.h b/absl/flags/internal/flag.h
index b41f9a6..2e6e6b8 100644
--- a/absl/flags/internal/flag.h
+++ b/absl/flags/internal/flag.h
@@ -54,13 +54,8 @@
 class Flag;
 }  // namespace flags_internal
 
-#if defined(_MSC_VER) && !defined(__clang__)
-template <typename T>
-class Flag;
-#else
 template <typename T>
 using Flag = flags_internal::Flag<T>;
-#endif
 
 template <typename T>
 ABSL_MUST_USE_RESULT T GetFlag(const absl::Flag<T>& flag);
diff --git a/absl/flags/internal/flag_msvc.inc b/absl/flags/internal/flag_msvc.inc
deleted file mode 100644
index 614d09f..0000000
--- a/absl/flags/internal/flag_msvc.inc
+++ /dev/null
@@ -1,116 +0,0 @@
-//
-//  Copyright 2021 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.
-
-// Do not include this file directly.
-// Include absl/flags/flag.h instead.
-
-// MSVC debug builds do not implement initialization with constexpr constructors
-// correctly. To work around this we add a level of indirection, so that the
-// class `absl::Flag` contains an `internal::Flag*` (instead of being an alias
-// to that class) and dynamically allocates an instance when necessary. We also
-// forward all calls to internal::Flag methods via trampoline methods. In this
-// setup the `absl::Flag` class does not have constructor and virtual methods,
-// all the data members are public and thus MSVC is able to initialize it at
-// link time. To deal with multiple threads accessing the flag for the first
-// time concurrently we use an atomic boolean indicating if flag object is
-// initialized. We also employ the double-checked locking pattern where the
-// second level of protection is a global Mutex, so if two threads attempt to
-// construct the flag concurrently only one wins.
-//
-// This solution is based on a recommendation here:
-// https://developercommunity.visualstudio.com/content/problem/336946/class-with-constexpr-constructor-not-using-static.html?childToView=648454#comment-648454
-
-namespace flags_internal {
-absl::Mutex* GetGlobalConstructionGuard();
-}  // namespace flags_internal
-
-// Public methods of `absl::Flag<T>` are NOT part of the Abseil Flags API.
-// See https://abseil.io/docs/cpp/guides/flags
-template <typename T>
-class Flag {
- public:
-  // No constructor and destructor to ensure this is an aggregate type.
-  // Visual Studio 2015 still requires the constructor for class to be
-  // constexpr initializable.
-#if _MSC_VER <= 1900
-  constexpr Flag(const char* name, const char* filename,
-                 const flags_internal::HelpGenFunc help_gen,
-                 const flags_internal::FlagDfltGenFunc default_value_gen)
-      : name_(name),
-        filename_(filename),
-        help_gen_(help_gen),
-        default_value_gen_(default_value_gen),
-        inited_(false),
-        impl_(nullptr) {}
-#endif
-
-  flags_internal::Flag<T>& GetImpl() const {
-    if (!inited_.load(std::memory_order_acquire)) {
-      absl::MutexLock l(flags_internal::GetGlobalConstructionGuard());
-
-      if (inited_.load(std::memory_order_acquire)) {
-        return *impl_;
-      }
-
-      impl_ = new flags_internal::Flag<T>(
-          name_, filename_,
-          {flags_internal::FlagHelpMsg(help_gen_),
-           flags_internal::FlagHelpKind::kGenFunc},
-          {flags_internal::FlagDefaultSrc(default_value_gen_),
-           flags_internal::FlagDefaultKind::kGenFunc});
-      inited_.store(true, std::memory_order_release);
-    }
-
-    return *impl_;
-  }
-
-  // Public methods of `absl::Flag<T>` are NOT part of the Abseil Flags API.
-  // See https://abseil.io/docs/cpp/guides/flags
-  bool IsRetired() const { return GetImpl().IsRetired(); }
-  absl::string_view Name() const { return GetImpl().Name(); }
-  std::string Help() const { return GetImpl().Help(); }
-  bool IsModified() const { return GetImpl().IsModified(); }
-  bool IsSpecifiedOnCommandLine() const {
-    return GetImpl().IsSpecifiedOnCommandLine();
-  }
-  std::string Filename() const { return GetImpl().Filename(); }
-  std::string DefaultValue() const { return GetImpl().DefaultValue(); }
-  std::string CurrentValue() const { return GetImpl().CurrentValue(); }
-  template <typename U>
-  inline bool IsOfType() const {
-    return GetImpl().template IsOfType<U>();
-  }
-  T Get() const {
-    return flags_internal::FlagImplPeer::InvokeGet<T>(GetImpl());
-  }
-  void Set(const T& v) {
-    flags_internal::FlagImplPeer::InvokeSet(GetImpl(), v);
-  }
-  void InvokeCallback() { GetImpl().InvokeCallback(); }
-
-  const CommandLineFlag& Reflect() const {
-    return flags_internal::FlagImplPeer::InvokeReflect(GetImpl());
-  }
-
-  // The data members are logically private, but they need to be public for
-  // this to be an aggregate type.
-  const char* name_;
-  const char* filename_;
-  const flags_internal::HelpGenFunc help_gen_;
-  const flags_internal::FlagDfltGenFunc default_value_gen_;
-
-  mutable std::atomic<bool> inited_;
-  mutable flags_internal::Flag<T>* impl_;
-};
diff --git a/absl/flags/internal/usage.cc b/absl/flags/internal/usage.cc
index 13852e1..8b169bc 100644
--- a/absl/flags/internal/usage.cc
+++ b/absl/flags/internal/usage.cc
@@ -27,7 +27,10 @@
 #include <utility>
 #include <vector>
 
+#include "absl/base/attributes.h"
 #include "absl/base/config.h"
+#include "absl/base/const_init.h"
+#include "absl/base/thread_annotations.h"
 #include "absl/flags/commandlineflag.h"
 #include "absl/flags/flag.h"
 #include "absl/flags/internal/flag.h"
@@ -40,6 +43,8 @@
 #include "absl/strings/str_cat.h"
 #include "absl/strings/str_split.h"
 #include "absl/strings/string_view.h"
+#include "absl/strings/strip.h"
+#include "absl/synchronization/mutex.h"
 
 // Dummy global variables to prevent anyone else defining these.
 bool FLAGS_help = false;
diff --git a/absl/flags/marshalling.cc b/absl/flags/marshalling.cc
index dc69754..ca4a130 100644
--- a/absl/flags/marshalling.cc
+++ b/absl/flags/marshalling.cc
@@ -247,6 +247,14 @@
     *err = "no value provided";
     return false;
   }
+  if (absl::EqualsIgnoreCase(text, "dfatal")) {
+    *dst = absl::kLogDebugFatal;
+    return true;
+  }
+  if (absl::EqualsIgnoreCase(text, "klogdebugfatal")) {
+    *dst = absl::kLogDebugFatal;
+    return true;
+  }
   if (text.front() == 'k' || text.front() == 'K') text.remove_prefix(1);
   if (absl::EqualsIgnoreCase(text, "info")) {
     *dst = absl::LogSeverity::kInfo;
@@ -269,7 +277,8 @@
     *dst = static_cast<absl::LogSeverity>(numeric_value);
     return true;
   }
-  *err = "only integers and absl::LogSeverity enumerators are accepted";
+  *err =
+      "only integers, absl::LogSeverity enumerators, and DFATAL are accepted";
   return false;
 }
 
diff --git a/absl/flags/parse.cc b/absl/flags/parse.cc
index 4cdd9d0..526b61d 100644
--- a/absl/flags/parse.cc
+++ b/absl/flags/parse.cc
@@ -637,7 +637,7 @@
 // --------------------------------------------------------------------
 
 bool WasPresentOnCommandLine(absl::string_view flag_name) {
-  absl::MutexLock l(&specified_flags_guard);
+  absl::ReaderMutexLock l(&specified_flags_guard);
   ABSL_INTERNAL_CHECK(specified_flags != nullptr,
                       "ParseCommandLine is not invoked yet");
 
diff --git a/absl/flags/reflection.cc b/absl/flags/reflection.cc
index dbce403..841921a 100644
--- a/absl/flags/reflection.cc
+++ b/absl/flags/reflection.cc
@@ -21,6 +21,7 @@
 #include <string>
 
 #include "absl/base/config.h"
+#include "absl/base/no_destructor.h"
 #include "absl/base/thread_annotations.h"
 #include "absl/container/flat_hash_map.h"
 #include "absl/flags/commandlineflag.h"
@@ -169,7 +170,7 @@
 }
 
 FlagRegistry& FlagRegistry::GlobalRegistry() {
-  static FlagRegistry* global_registry = new FlagRegistry;
+  static absl::NoDestructor<FlagRegistry> global_registry;
   return *global_registry;
 }
 
diff --git a/absl/functional/BUILD.bazel b/absl/functional/BUILD.bazel
index 4ceac53..1a18af2 100644
--- a/absl/functional/BUILD.bazel
+++ b/absl/functional/BUILD.bazel
@@ -21,7 +21,14 @@
     "ABSL_TEST_COPTS",
 )
 
-package(default_visibility = ["//visibility:public"])
+package(
+    default_visibility = ["//visibility:public"],
+    features = [
+        "header_modules",
+        "layering_check",
+        "parse_headers",
+    ],
+)
 
 licenses(["notice"])
 
@@ -55,6 +62,7 @@
         "//absl/base:core_headers",
         "//absl/meta:type_traits",
         "//absl/utility",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -81,6 +89,7 @@
     deps = [
         ":bind_front",
         "//absl/memory",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -109,6 +118,34 @@
         ":function_ref",
         "//absl/container:test_instance_tracker",
         "//absl/memory",
+        "@com_google_googletest//:gtest",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
+
+cc_library(
+    name = "overload",
+    hdrs = ["overload.h"],
+    copts = ABSL_DEFAULT_COPTS,
+    linkopts = ABSL_DEFAULT_LINKOPTS,
+    deps = [
+        "//absl/base:config",
+        "//absl/meta:type_traits",
+    ],
+)
+
+cc_test(
+    name = "overload_test",
+    size = "small",
+    srcs = ["overload_test.cc"],
+    copts = ABSL_TEST_COPTS,
+    deps = [
+        ":overload",
+        "//absl/base:config",
+        "//absl/strings",
+        "//absl/strings:string_view",
+        "//absl/types:variant",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -126,5 +163,6 @@
         ":function_ref",
         "//absl/base:core_headers",
         "@com_github_google_benchmark//:benchmark_main",
+        "@com_google_googletest//:gtest",
     ],
 )
diff --git a/absl/functional/CMakeLists.txt b/absl/functional/CMakeLists.txt
index c704e04..602829c 100644
--- a/absl/functional/CMakeLists.txt
+++ b/absl/functional/CMakeLists.txt
@@ -108,3 +108,27 @@
     absl::test_instance_tracker
     GTest::gmock_main
 )
+
+absl_cc_library(
+  NAME
+    overload
+  HDRS
+    "overload.h"
+  COPTS
+    ${ABSL_DEFAULT_COPTS}
+  DEPS
+    absl::meta
+  PUBLIC
+)
+
+absl_cc_test(
+  NAME
+    overload_test
+  SRCS
+    "overload_test.cc"
+  COPTS
+    ${ABSL_TEST_COPTS}
+  DEPS
+    absl::strings
+    GTest::gmock_main
+)
diff --git a/absl/functional/function_ref.h b/absl/functional/function_ref.h
index 2b9139d..96cece5 100644
--- a/absl/functional/function_ref.h
+++ b/absl/functional/function_ref.h
@@ -137,6 +137,14 @@
   absl::functional_internal::Invoker<R, Args...> invoker_;
 };
 
+// Allow const qualified function signatures. Since FunctionRef requires
+// constness anyway we can just make this a no-op.
+template <typename R, typename... Args>
+class FunctionRef<R(Args...) const> : public FunctionRef<R(Args...)> {
+ public:
+  using FunctionRef<R(Args...)>::FunctionRef;
+};
+
 ABSL_NAMESPACE_END
 }  // namespace absl
 
diff --git a/absl/functional/function_ref_test.cc b/absl/functional/function_ref_test.cc
index c61117e..c021113 100644
--- a/absl/functional/function_ref_test.cc
+++ b/absl/functional/function_ref_test.cc
@@ -47,6 +47,11 @@
   EXPECT_EQ(1337, ref());
 }
 
+TEST(FunctionRefTest, ConstFunction) {
+  FunctionRef<int() const> ref(Function);
+  EXPECT_EQ(1337, ref());
+}
+
 int NoExceptFunction() noexcept { return 1337; }
 
 // TODO(jdennett): Add a test for noexcept member functions.
diff --git a/absl/functional/internal/any_invocable.h b/absl/functional/internal/any_invocable.h
index f096bb0..b04436d 100644
--- a/absl/functional/internal/any_invocable.h
+++ b/absl/functional/internal/any_invocable.h
@@ -215,8 +215,8 @@
   // behavior, which works as intended on Abseil's officially supported
   // platforms as of Q2 2022.
 #if !defined(__clang__) && defined(__GNUC__)
-#pragma GCC diagnostic ignored "-Wstrict-aliasing"
 #pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wstrict-aliasing"
 #endif
   return *reinterpret_cast<T*>(&state->storage);
 #if !defined(__clang__) && defined(__GNUC__)
@@ -526,10 +526,10 @@
 // Since this is template-heavy code, we prefer to disable these warnings
 // locally instead of adding yet another overload of this function.
 #if !defined(__clang__) && defined(__GNUC__)
+#pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wpragmas"
 #pragma GCC diagnostic ignored "-Waddress"
 #pragma GCC diagnostic ignored "-Wnonnull-compare"
-#pragma GCC diagnostic push
 #endif
     if (static_cast<RemoveCVRef<QualDecayedTRef>>(f) == nullptr) {
 #if !defined(__clang__) && defined(__GNUC__)
diff --git a/absl/functional/overload.h b/absl/functional/overload.h
new file mode 100644
index 0000000..4651f14
--- /dev/null
+++ b/absl/functional/overload.h
@@ -0,0 +1,75 @@
+// 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.
+//
+// -----------------------------------------------------------------------------
+// File: overload.h
+// -----------------------------------------------------------------------------
+//
+// `absl::Overload()` returns a functor that provides overloads based on the
+// functors passed to it.
+// Before using this function, consider whether named function overloads would
+// be a better design.
+// One use case for this is locally defining visitors for `std::visit` inside a
+// function using lambdas.
+
+// Example: Using  `absl::Overload` to define a visitor for `std::variant`.
+//
+// std::variant<int, std::string, double> v(int{1});
+//
+// assert(std::visit(absl::Overload(
+//                        [](int) -> absl::string_view { return "int"; },
+//                        [](const std::string&) -> absl::string_view {
+//                          return "string";
+//                        },
+//                        [](double) -> absl::string_view { return "double"; }),
+//                     v) == "int");
+//
+// Note: This requires C++17.
+
+#ifndef ABSL_FUNCTIONAL_OVERLOAD_H_
+#define ABSL_FUNCTIONAL_OVERLOAD_H_
+
+#include "absl/base/config.h"
+#include "absl/meta/type_traits.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+
+#if defined(ABSL_INTERNAL_CPLUSPLUS_LANG) && \
+    ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L
+
+template <int&... ExplicitArgumentBarrier, typename... T>
+auto Overload(T&&... ts) {
+  struct OverloadImpl : absl::remove_cvref_t<T>... {
+    using absl::remove_cvref_t<T>::operator()...;
+  };
+  return OverloadImpl{std::forward<T>(ts)...};
+}
+#else
+namespace functional_internal {
+template <typename T>
+constexpr bool kDependentFalse = false;
+}
+
+template <typename Dependent = int, typename... T>
+auto Overload(T&&...) {
+  static_assert(functional_internal::kDependentFalse<Dependent>,
+                "Overload is only usable with C++17 or above.");
+}
+
+#endif
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif  // ABSL_FUNCTIONAL_OVERLOAD_H_
diff --git a/absl/functional/overload_test.cc b/absl/functional/overload_test.cc
new file mode 100644
index 0000000..739c4c4
--- /dev/null
+++ b/absl/functional/overload_test.cc
@@ -0,0 +1,130 @@
+// 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/functional/overload.h"
+
+#include <cstdint>
+#include <string>
+#include <type_traits>
+
+#include "absl/base/config.h"
+#include "absl/strings/str_cat.h"
+#include "absl/strings/string_view.h"
+#include "absl/types/variant.h"
+
+#if defined(ABSL_INTERNAL_CPLUSPLUS_LANG) && \
+    ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L
+
+#include "gtest/gtest.h"
+
+namespace {
+
+TEST(OverloadTest, DispatchConsidersType) {
+  auto overloaded = absl::Overload(
+      [](int v) -> std::string { return absl::StrCat("int ", v); },        //
+      [](double v) -> std::string { return absl::StrCat("double ", v); },  //
+      [](const char* v) -> std::string {                                   //
+        return absl::StrCat("const char* ", v);                            //
+      },                                                                   //
+      [](auto v) -> std::string { return absl::StrCat("auto ", v); }       //
+  );
+  EXPECT_EQ("int 1", overloaded(1));
+  EXPECT_EQ("double 2.5", overloaded(2.5));
+  EXPECT_EQ("const char* hello", overloaded("hello"));
+  EXPECT_EQ("auto 1.5", overloaded(1.5f));
+}
+
+TEST(OverloadTest, DispatchConsidersNumberOfArguments) {
+  auto overloaded = absl::Overload(                 //
+      [](int a) { return a + 1; },                  //
+      [](int a, int b) { return a * b; },           //
+      []() -> absl::string_view { return "none"; }  //
+  );
+  EXPECT_EQ(3, overloaded(2));
+  EXPECT_EQ(21, overloaded(3, 7));
+  EXPECT_EQ("none", overloaded());
+}
+
+TEST(OverloadTest, SupportsConstantEvaluation) {
+  auto overloaded = absl::Overload(                 //
+      [](int a) { return a + 1; },                  //
+      [](int a, int b) { return a * b; },           //
+      []() -> absl::string_view { return "none"; }  //
+  );
+  static_assert(overloaded() == "none");
+  static_assert(overloaded(2) == 3);
+  static_assert(overloaded(3, 7) == 21);
+}
+
+TEST(OverloadTest, PropogatesDefaults) {
+  auto overloaded = absl::Overload(            //
+      [](int a, int b = 5) { return a * b; },  //
+      [](double c) { return c; }               //
+  );
+
+  EXPECT_EQ(21, overloaded(3, 7));
+  EXPECT_EQ(35, overloaded(7));
+  EXPECT_EQ(2.5, overloaded(2.5));
+}
+
+TEST(OverloadTest, AmbiguousWithDefaultsNotInvocable) {
+  auto overloaded = absl::Overload(            //
+      [](int a, int b = 5) { return a * b; },  //
+      [](int c) { return c; }                  //
+  );
+  static_assert(!std::is_invocable_v<decltype(overloaded), int>);
+  static_assert(std::is_invocable_v<decltype(overloaded), int, int>);
+}
+
+TEST(OverloadTest, AmbiguousDuplicatesNotInvocable) {
+  auto overloaded = absl::Overload(  //
+      [](int a) { return a; },       //
+      [](int c) { return c; }        //
+  );
+  static_assert(!std::is_invocable_v<decltype(overloaded), int>);
+}
+
+TEST(OverloadTest, AmbiguousConversionNotInvocable) {
+  auto overloaded = absl::Overload(  //
+      [](uint16_t a) { return a; },  //
+      [](uint64_t c) { return c; }   //
+  );
+  static_assert(!std::is_invocable_v<decltype(overloaded), int>);
+}
+
+TEST(OverloadTest, DispatchConsidersSfinae) {
+  auto overloaded = absl::Overload(                    //
+      [](auto a) -> decltype(a + 1) { return a + 1; }  //
+  );
+  static_assert(std::is_invocable_v<decltype(overloaded), int>);
+  static_assert(!std::is_invocable_v<decltype(overloaded), std::string>);
+}
+
+TEST(OverloadTest, VariantVisitDispatchesCorrectly) {
+  absl::variant<int, double, std::string> v(1);
+  auto overloaded = absl::Overload(
+      [](int) -> absl::string_view { return "int"; },                   //
+      [](double) -> absl::string_view { return "double"; },             //
+      [](const std::string&) -> absl::string_view { return "string"; }  //
+  );
+  EXPECT_EQ("int", absl::visit(overloaded, v));
+  v = 1.1;
+  EXPECT_EQ("double", absl::visit(overloaded, v));
+  v = "hello";
+  EXPECT_EQ("string", absl::visit(overloaded, v));
+}
+
+}  // namespace
+
+#endif
diff --git a/absl/hash/BUILD.bazel b/absl/hash/BUILD.bazel
index 4346fc4..1e8ad45 100644
--- a/absl/hash/BUILD.bazel
+++ b/absl/hash/BUILD.bazel
@@ -21,7 +21,14 @@
     "ABSL_TEST_COPTS",
 )
 
-package(default_visibility = ["//visibility:public"])
+package(
+    default_visibility = ["//visibility:public"],
+    features = [
+        "header_modules",
+        "layering_check",
+        "parse_headers",
+    ],
+)
 
 licenses(["notice"])
 
@@ -85,9 +92,14 @@
         "//absl/container:flat_hash_set",
         "//absl/container:node_hash_map",
         "//absl/container:node_hash_set",
+        "//absl/memory",
         "//absl/meta:type_traits",
         "//absl/numeric:int128",
         "//absl/strings:cord_test_helpers",
+        "//absl/strings:string_view",
+        "//absl/types:optional",
+        "//absl/types:variant",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -109,6 +121,7 @@
         "//absl/container:flat_hash_set",
         "//absl/container:node_hash_map",
         "//absl/container:node_hash_set",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -169,6 +182,7 @@
     linkopts = ABSL_DEFAULT_LINKOPTS,
     deps = [
         ":city",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -197,6 +211,7 @@
     deps = [
         ":low_level_hash",
         "//absl/strings",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
diff --git a/absl/hash/CMakeLists.txt b/absl/hash/CMakeLists.txt
index 65fd2a5..99d6fa1 100644
--- a/absl/hash/CMakeLists.txt
+++ b/absl/hash/CMakeLists.txt
@@ -68,18 +68,22 @@
   COPTS
     ${ABSL_TEST_COPTS}
   DEPS
-    absl::cord_test_helpers
-    absl::hash
-    absl::hash_testing
-    absl::core_headers
     absl::btree
+    absl::cord_test_helpers
+    absl::core_headers
     absl::flat_hash_map
     absl::flat_hash_set
+    absl::hash
+    absl::hash_testing
+    absl::int128
+    absl::memory
+    absl::meta
     absl::node_hash_map
     absl::node_hash_set
+    absl::optional
     absl::spy_hash_state
-    absl::meta
-    absl::int128
+    absl::string_view
+    absl::variant
     GTest::gmock_main
 )
 
@@ -87,7 +91,7 @@
   NAME
     hash_instantiated_test
   SRCS
-    "hash_test.cc"
+    "hash_instantiated_test.cc"
     "internal/hash_test.h"
   COPTS
     ${ABSL_TEST_COPTS}
@@ -100,7 +104,7 @@
     absl::flat_hash_set
     absl::node_hash_map
     absl::node_hash_set
-    GTest::gtest_main
+    GTest::gmock_main
 )
 
 # Internal-only target, do not depend on directly.
diff --git a/absl/hash/hash_benchmark.cc b/absl/hash/hash_benchmark.cc
index 8712a01..d18ea69 100644
--- a/absl/hash/hash_benchmark.cc
+++ b/absl/hash/hash_benchmark.cc
@@ -12,7 +12,13 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include <algorithm>
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+#include <cstring>
 #include <string>
+#include <tuple>
 #include <type_traits>
 #include <typeindex>
 #include <utility>
@@ -79,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();
@@ -160,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/hash/hash_test.cc b/absl/hash/hash_test.cc
index a0e2e4a..59fe8de 100644
--- a/absl/hash/hash_test.cc
+++ b/absl/hash/hash_test.cc
@@ -17,42 +17,40 @@
 #include <algorithm>
 #include <array>
 #include <bitset>
+#include <cstddef>
 #include <cstdint>
+#include <cstdlib>
 #include <cstring>
-#include <deque>
-#include <forward_list>
 #include <functional>
 #include <initializer_list>
-#include <iterator>
+#include <ios>
 #include <limits>
-#include <list>
-#include <map>
 #include <memory>
-#include <numeric>
-#include <random>
+#include <ostream>
 #include <set>
 #include <string>
 #include <tuple>
 #include <type_traits>
 #include <unordered_map>
-#include <unordered_set>
 #include <utility>
 #include <vector>
 
-#include "gmock/gmock.h"
 #include "gtest/gtest.h"
-#include "absl/container/btree_map.h"
-#include "absl/container/btree_set.h"
-#include "absl/container/flat_hash_map.h"
+#include "absl/base/config.h"
 #include "absl/container/flat_hash_set.h"
-#include "absl/container/node_hash_map.h"
-#include "absl/container/node_hash_set.h"
 #include "absl/hash/hash_testing.h"
 #include "absl/hash/internal/hash_test.h"
 #include "absl/hash/internal/spy_hash_state.h"
+#include "absl/memory/memory.h"
 #include "absl/meta/type_traits.h"
-#include "absl/numeric/int128.h"
 #include "absl/strings/cord_test_helpers.h"
+#include "absl/strings/string_view.h"
+#include "absl/types/optional.h"
+#include "absl/types/variant.h"
+
+#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L
+#include <filesystem>  // NOLINT
+#endif
 
 #ifdef ABSL_HAVE_STD_STRING_VIEW
 #include <string_view>
@@ -486,6 +484,43 @@
 #endif
 }
 
+TEST(HashValueTest, StdFilesystemPath) {
+#ifndef ABSL_INTERNAL_STD_FILESYSTEM_PATH_HASH_AVAILABLE
+  GTEST_SKIP() << "std::filesystem::path is unavailable on this platform";
+#else
+  EXPECT_TRUE((is_hashable<std::filesystem::path>::value));
+
+  // clang-format off
+  const auto kTestCases = std::make_tuple(
+      std::filesystem::path(),
+      std::filesystem::path("/"),
+#ifndef __GLIBCXX__
+      // libstdc++ has a known issue normalizing "//".
+      // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=106452
+      std::filesystem::path("//"),
+#endif
+      std::filesystem::path("/a/b"),
+      std::filesystem::path("/a//b"),
+      std::filesystem::path("a/b"),
+      std::filesystem::path("a/b/"),
+      std::filesystem::path("a//b"),
+      std::filesystem::path("a//b/"),
+      std::filesystem::path("c:/"),
+      std::filesystem::path("c:\\"),
+      std::filesystem::path("c:\\/"),
+      std::filesystem::path("c:\\//"),
+      std::filesystem::path("c://"),
+      std::filesystem::path("c://\\"),
+      std::filesystem::path("/e/p"),
+      std::filesystem::path("/s/../e/p"),
+      std::filesystem::path("e/p"),
+      std::filesystem::path("s/../e/p"));
+  // clang-format on
+
+  EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(kTestCases));
+#endif
+}
+
 TEST(HashValueTest, StdArray) {
   EXPECT_TRUE((is_hashable<std::array<int, 3>>::value));
 
diff --git a/absl/hash/internal/hash.h b/absl/hash/internal/hash.h
index ef3f366..f4a94f9 100644
--- a/absl/hash/internal/hash.h
+++ b/absl/hash/internal/hash.h
@@ -19,6 +19,11 @@
 #ifndef ABSL_HASH_INTERNAL_HASH_H_
 #define ABSL_HASH_INTERNAL_HASH_H_
 
+#ifdef __APPLE__
+#include <Availability.h>
+#include <TargetConditionals.h>
+#endif
+
 #include <algorithm>
 #include <array>
 #include <bitset>
@@ -56,6 +61,11 @@
 #include "absl/types/variant.h"
 #include "absl/utility/utility.h"
 
+#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L && \
+    !defined(_LIBCPP_HAS_NO_FILESYSTEM_LIBRARY)
+#include <filesystem>  // NOLINT
+#endif
+
 #ifdef ABSL_HAVE_STD_STRING_VIEW
 #include <string_view>
 #endif
@@ -409,9 +419,23 @@
   return H::combine(std::move(hash_state), category);
 }
 
+// Without this overload, an array decays to a pointer and we hash that, which
+// is not likely to be what the caller intended.
+template <typename H, typename T, size_t N>
+H AbslHashValue(H hash_state, T (&)[N]) {
+  static_assert(
+      sizeof(T) == -1,
+      "Hashing C arrays is not allowed. For string literals, wrap the literal "
+      "in absl::string_view(). To hash the array contents, use "
+      "absl::MakeSpan() or make the array an std::array. To hash the array "
+      "address, use &array[0].");
+  return hash_state;
+}
+
 // AbslHashValue() for hashing pointers
 template <typename H, typename T>
-H AbslHashValue(H hash_state, T* ptr) {
+std::enable_if_t<std::is_pointer<T>::value, H> AbslHashValue(H hash_state,
+                                                             T ptr) {
   auto v = reinterpret_cast<uintptr_t>(ptr);
   // Due to alignment, pointers tend to have low bits as zero, and the next few
   // bits follow a pattern since they are also multiples of some base value.
@@ -564,6 +588,29 @@
 
 #endif  // ABSL_HAVE_STD_STRING_VIEW
 
+#if defined(__cpp_lib_filesystem) && __cpp_lib_filesystem >= 201703L && \
+    !defined(_LIBCPP_HAS_NO_FILESYSTEM_LIBRARY) && \
+    (!defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__) ||        \
+     __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ >= 130000)
+
+#define ABSL_INTERNAL_STD_FILESYSTEM_PATH_HASH_AVAILABLE 1
+
+// Support std::filesystem::path. The SFINAE is required because some string
+// types are implicitly convertible to std::filesystem::path.
+template <typename Path, typename H,
+          typename = absl::enable_if_t<
+              std::is_same_v<Path, std::filesystem::path>>>
+H AbslHashValue(H hash_state, const Path& path) {
+  // This is implemented by deferring to the standard library to compute the
+  // hash.  The standard library requires that for two paths, `p1 == p2`, then
+  // `hash_value(p1) == hash_value(p2)`. `AbslHashValue` has the same
+  // requirement. Since `operator==` does platform specific matching, deferring
+  // to the standard library is the simplest approach.
+  return H::combine(std::move(hash_state), std::filesystem::hash_value(path));
+}
+
+#endif  // ABSL_INTERNAL_STD_FILESYSTEM_PATH_HASH_AVAILABLE
+
 // -----------------------------------------------------------------------------
 // AbslHashValue for Sequence Containers
 // -----------------------------------------------------------------------------
@@ -818,7 +865,7 @@
 template <typename H, size_t N>
 H AbslHashValue(H hash_state, const std::bitset<N>& set) {
   typename H::AbslInternalPiecewiseCombiner combiner;
-  for (int i = 0; i < N; i++) {
+  for (size_t i = 0; i < N; i++) {
     unsigned char c = static_cast<unsigned char>(set[i]);
     hash_state = combiner.add_buffer(std::move(hash_state), &c, sizeof(c));
   }
diff --git a/absl/log/BUILD.bazel b/absl/log/BUILD.bazel
index e141063..40e87cc 100644
--- a/absl/log/BUILD.bazel
+++ b/absl/log/BUILD.bazel
@@ -21,7 +21,14 @@
     "ABSL_TEST_COPTS",
 )
 
-package(default_visibility = ["//visibility:public"])
+package(
+    default_visibility = ["//visibility:public"],
+    features = [
+        "header_modules",
+        "layering_check",
+        "parse_headers",
+    ],
+)
 
 licenses(["notice"])
 
@@ -91,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.
@@ -111,6 +119,7 @@
         "//absl/base:log_severity",
         "//absl/base:raw_logging_internal",
         "//absl/hash",
+        "//absl/log/internal:vlog_config",
         "//absl/strings",
     ],
 )
@@ -136,6 +145,7 @@
     copts = ABSL_DEFAULT_COPTS,
     linkopts = ABSL_DEFAULT_LINKOPTS,
     deps = [
+        ":vlog_is_on",
         "//absl/log/internal:log_impl",
     ],
 )
@@ -228,6 +238,58 @@
     ],
 )
 
+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",
+        ":globals",
+        ":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(
@@ -244,6 +306,7 @@
     deps = [
         ":absl_check",
         ":check_test_impl",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -257,6 +320,7 @@
     deps = [
         ":absl_log",
         ":log_basic_test_impl",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -275,6 +339,7 @@
     deps = [
         ":check",
         ":check_test_impl",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -310,6 +375,7 @@
         ":die_if_null",
         "//absl/base:core_headers",
         "//absl/log/internal:test_helpers",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -333,6 +399,7 @@
         "//absl/log/internal:test_helpers",
         "//absl/log/internal:test_matchers",
         "//absl/strings",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -351,6 +418,7 @@
         "//absl/base:log_severity",
         "//absl/log/internal:globals",
         "//absl/log/internal:test_helpers",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -364,6 +432,7 @@
     deps = [
         ":log",
         ":log_basic_test_impl",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -405,6 +474,7 @@
         "//absl/strings",
         "//absl/time",
         "//absl/types:span",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -423,6 +493,7 @@
         "//absl/strings",
         "//absl/strings:str_format",
         "//absl/types:optional",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -438,6 +509,7 @@
         ":scoped_mock_log",
         "//absl/base:core_headers",
         "//absl/base:log_severity",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -463,6 +535,7 @@
         "//absl/log/internal:test_helpers",
         "//absl/log/internal:test_matchers",
         "//absl/strings",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -484,6 +557,7 @@
         "//absl/log/internal:test_helpers",
         "//absl/log/internal:test_matchers",
         "//absl/strings",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -503,6 +577,7 @@
         "//absl/log/internal:test_matchers",
         "//absl/strings",
         "//absl/time",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -530,6 +605,7 @@
         "//absl/memory",
         "//absl/strings",
         "//absl/synchronization",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -550,8 +626,10 @@
         "//absl/base:strerror",
         "//absl/flags:program_name",
         "//absl/log/internal:test_helpers",
+        "//absl/status",
         "//absl/strings",
         "//absl/strings:str_format",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -569,13 +647,14 @@
         "//absl/base:core_headers",
         "//absl/log/internal:test_helpers",
         "//absl/log/internal:test_matchers",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
 
-cc_binary(
+cc_test(
     name = "log_benchmark",
-    testonly = 1,
+    size = "small",
     srcs = ["log_benchmark.cc"],
     copts = ABSL_TEST_COPTS,
     linkopts = ABSL_DEFAULT_LINKOPTS,
diff --git a/absl/log/CMakeLists.txt b/absl/log/CMakeLists.txt
index 9320ce5..a7d8b69 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(
@@ -238,6 +239,7 @@
     absl::log_entry
     absl::log_severity
     absl::log_sink
+    absl::no_destructor
     absl::raw_logging_internal
     absl::synchronization
     absl::span
@@ -459,6 +461,11 @@
   PUBLIC
 )
 
+# Warning: Many linkers will strip the contents of this library because its
+# symbols are only used in a global constructor. A workaround is for clients
+# to link this using $<LINK_LIBRARY:WHOLE_ARCHIVE,absl::log_flags> instead of
+# the plain absl::log_flags.
+# TODO(b/320467376): Implement the equivalent of Bazel's alwayslink=True.
 absl_cc_library(
   NAME
     log_flags
@@ -480,6 +487,7 @@
     absl::flags
     absl::flags_marshalling
     absl::strings
+    absl::vlog_config_internal
   PUBLIC
 )
 
@@ -502,6 +510,7 @@
     absl::log_severity
     absl::raw_logging_internal
     absl::strings
+    absl::vlog_config_internal
 )
 
 absl_cc_library(
@@ -535,6 +544,7 @@
     ${ABSL_DEFAULT_LINKOPTS}
   DEPS
     absl::log_internal_log_impl
+    absl::vlog_is_on
   PUBLIC
 )
 
@@ -671,6 +681,95 @@
   PUBLIC
 )
 
+absl_cc_library(
+  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
+    absl::log_flags
+    absl::log_globals
+    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
 
 absl_cc_test(
@@ -689,8 +788,7 @@
     absl::core_headers
     absl::log_internal_test_helpers
     absl::status
-    GTest::gmock
-    GTest::gtest_main
+    GTest::gmock_main
 )
 
 absl_cc_test(
@@ -713,8 +811,7 @@
     absl::log_internal_test_helpers
     absl::log_internal_test_matchers
     absl::scoped_mock_log
-    GTest::gmock
-    GTest::gtest_main
+    GTest::gmock_main
 )
 
 absl_cc_test(
@@ -733,8 +830,7 @@
     absl::core_headers
     absl::log_internal_test_helpers
     absl::status
-    GTest::gmock
-    GTest::gtest_main
+    GTest::gmock_main
 )
 
 absl_cc_test(
@@ -773,8 +869,7 @@
     absl::log_internal_test_helpers
     absl::log_internal_test_matchers
     absl::scoped_mock_log
-    GTest::gmock
-    GTest::gtest_main
+    GTest::gmock_main
 )
 
 absl_cc_test(
@@ -798,8 +893,7 @@
     absl::span
     absl::strings
     absl::time
-    GTest::gmock
-    GTest::gtest_main
+    GTest::gmock_main
 )
 
 absl_cc_test(
@@ -824,8 +918,7 @@
     absl::flags_reflection
     absl::scoped_mock_log
     absl::strings
-    GTest::gmock
-    GTest::gtest_main
+    GTest::gmock_main
 )
 
 absl_cc_test(
@@ -845,7 +938,7 @@
     absl::log_internal_test_helpers
     absl::log_severity
     absl::scoped_mock_log
-    GTest::gtest_main
+    GTest::gmock_main
 )
 
 absl_cc_test(
@@ -865,8 +958,7 @@
     absl::scoped_mock_log
     absl::str_format
     absl::strings
-    GTest::gmock
-    GTest::gtest_main
+    GTest::gmock_main
 )
 
 absl_cc_test(
@@ -883,8 +975,7 @@
     absl::log
     absl::log_severity
     absl::scoped_mock_log
-    GTest::gmock
-    GTest::gtest_main
+    GTest::gmock_main
 )
 
 absl_cc_test(
@@ -907,7 +998,7 @@
     absl::log_severity
     absl::scoped_mock_log
     absl::strings
-    GTest::gtest_main
+    GTest::gmock_main
 )
 
 absl_cc_test(
@@ -930,7 +1021,7 @@
     absl::log_severity
     absl::scoped_mock_log
     absl::strings
-    GTest::gtest_main
+    GTest::gmock_main
 )
 
 absl_cc_test(
@@ -951,8 +1042,7 @@
     absl::scoped_mock_log
     absl::strings
     absl::time
-    GTest::gmock
-    GTest::gtest_main
+    GTest::gmock_main
 )
 
 absl_cc_test(
@@ -995,8 +1085,7 @@
     absl::log_globals
     absl::log_internal_test_helpers
     absl::log_severity
-    GTest::gmock
-    GTest::gtest_main
+    GTest::gmock_main
 )
 
 absl_cc_test(
@@ -1014,11 +1103,11 @@
     absl::log
     absl::log_internal_test_helpers
     absl::log_severity
+    absl::status
     absl::strerror
     absl::strings
     absl::str_format
-    GTest::gmock
-    GTest::gtest_main
+    GTest::gmock_main
 )
 
 absl_cc_test(
@@ -1037,6 +1126,19 @@
     absl::log_internal_test_matchers
     absl::log_structured
     absl::scoped_mock_log
-    GTest::gmock
-    GTest::gtest_main
+    GTest::gmock_main
+)
+
+absl_cc_test(
+  NAME
+    internal_fnmatch_test
+  SRCS
+    "internal/fnmatch_test.cc"
+  COPTS
+    ${ABSL_TEST_COPTS}
+  LINKOPTS
+    ${ABSL_DEFAULT_LINKOPTS}
+  DEPS
+    absl::log_internal_fnmatch
+    GTest::gmock_main
 )
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..29096b4
--- /dev/null
+++ b/absl/log/absl_vlog_is_on.h
@@ -0,0 +1,93 @@
+// 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))
+
+#endif  // ABSL_LOG_ABSL_VLOG_IS_ON_H_
diff --git a/absl/log/check.h b/absl/log/check.h
index 0a2f2e4..50f633d 100644
--- a/absl/log/check.h
+++ b/absl/log/check.h
@@ -169,7 +169,7 @@
 #define DCHECK_OK(status) ABSL_LOG_INTERNAL_DCHECK_OK_IMPL((status), #status)
 
 // `CHECK_STREQ` and friends provide `CHECK_EQ` functionality for C strings,
-// i.e., nul-terminated char arrays.  The `CASE` versions are case-insensitive.
+// i.e., null-terminated char arrays.  The `CASE` versions are case-insensitive.
 //
 // Example:
 //
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/globals.h b/absl/log/globals.h
index bc3864c..b36e47e 100644
--- a/absl/log/globals.h
+++ b/absl/log/globals.h
@@ -24,6 +24,7 @@
 #include "absl/base/attributes.h"
 #include "absl/base/config.h"
 #include "absl/base/log_severity.h"
+#include "absl/log/internal/vlog_config.h"
 #include "absl/strings/string_view.h"
 
 namespace absl {
@@ -153,6 +154,28 @@
 void EnableLogPrefix(bool on_off);
 
 //------------------------------------------------------------------------------
+// Set Global VLOG Level
+//------------------------------------------------------------------------------
+//
+// 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);
+}
+
+//------------------------------------------------------------------------------
+// Set VLOG Level
+//------------------------------------------------------------------------------
+//
+// Sets `(ABSL_)VLOG(_IS_ON)` level for `module_pattern` to `log_level`.  This
+// allows programmatic control of what is normally set by the --vmodule flag.
+// Returns the level that previously applied to `module_pattern`.
+inline int SetVLogLevel(absl::string_view module_pattern, int log_level) {
+  return absl::log_internal::PrependVModule(module_pattern, log_level);
+}
+
+//------------------------------------------------------------------------------
 // Configure Android Native Log Tag
 //------------------------------------------------------------------------------
 //
diff --git a/absl/log/globals_test.cc b/absl/log/globals_test.cc
index f7af47c..0dc54d5 100644
--- a/absl/log/globals_test.cc
+++ b/absl/log/globals_test.cc
@@ -88,6 +88,20 @@
   EXPECT_TRUE(absl::ShouldPrependLogPrefix());
 }
 
+TEST(TestGlobals, SetGlobalVLogLevel) {
+  EXPECT_EQ(absl::SetGlobalVLogLevel(42), 0);
+  EXPECT_EQ(absl::SetGlobalVLogLevel(1337), 42);
+  // Restore the value since it affects the default unset module value for
+  // `SetVLogLevel()`.
+  EXPECT_EQ(absl::SetGlobalVLogLevel(0), 1337);
+}
+
+TEST(TestGlobals, SetVLogLevel) {
+  EXPECT_EQ(absl::SetVLogLevel("setvloglevel", 42), 0);
+  EXPECT_EQ(absl::SetVLogLevel("setvloglevel", 1337), 42);
+  EXPECT_EQ(absl::SetVLogLevel("othersetvloglevel", 50), 0);
+}
+
 TEST(TestGlobals, AndroidLogTag) {
   // Verify invalid tags result in a check failure.
   EXPECT_DEATH_IF_SUPPORTED(absl::SetAndroidNativeTag(nullptr), ".*");
@@ -101,4 +115,33 @@
   EXPECT_DEATH_IF_SUPPORTED(absl::SetAndroidNativeTag("test_tag_fail"), ".*");
 }
 
+TEST(TestExitOnDFatal, OffTest) {
+  // Turn off...
+  absl::log_internal::SetExitOnDFatal(false);
+  EXPECT_FALSE(absl::log_internal::ExitOnDFatal());
+
+  // We don't die.
+  {
+    absl::ScopedMockLog log(absl::MockLogDefault::kDisallowUnexpected);
+
+    // LOG(DFATAL) has severity FATAL if debugging, but is
+    // downgraded to ERROR if not debugging.
+    EXPECT_CALL(log, Log(absl::kLogDebugFatal, _, "This should not be fatal"));
+
+    log.StartCapturingLogs();
+    LOG(DFATAL) << "This should not be fatal";
+  }
+}
+
+#if GTEST_HAS_DEATH_TEST
+TEST(TestDeathWhileExitOnDFatal, OnTest) {
+  absl::log_internal::SetExitOnDFatal(true);
+  EXPECT_TRUE(absl::log_internal::ExitOnDFatal());
+
+  // Death comes on little cats' feet.
+  EXPECT_DEBUG_DEATH({ LOG(DFATAL) << "This should be fatal in debug mode"; },
+                     "This should be fatal in debug mode");
+}
+#endif
+
 }  // namespace
diff --git a/absl/log/initialize.cc b/absl/log/initialize.cc
index a3f6d6c..ef5d314 100644
--- a/absl/log/initialize.cc
+++ b/absl/log/initialize.cc
@@ -21,14 +21,18 @@
 namespace absl {
 ABSL_NAMESPACE_BEGIN
 
-void InitializeLog() {
+namespace {
+void InitializeLogImpl(absl::TimeZone time_zone) {
   // This comes first since it is used by RAW_LOG.
-  absl::log_internal::SetTimeZone(absl::LocalTimeZone());
+  absl::log_internal::SetTimeZone(time_zone);
 
   // Note that initialization is complete, so logs can now be sent to their
   // proper destinations rather than stderr.
   log_internal::SetInitialized();
 }
+}  // namespace
+
+void InitializeLog() { InitializeLogImpl(absl::LocalTimeZone()); }
 
 ABSL_NAMESPACE_END
 }  // namespace absl
diff --git a/absl/log/internal/BUILD.bazel b/absl/log/internal/BUILD.bazel
index 555c5e5..1be1349 100644
--- a/absl/log/internal/BUILD.bazel
+++ b/absl/log/internal/BUILD.bazel
@@ -21,9 +21,16 @@
     "ABSL_TEST_COPTS",
 )
 
-package(default_visibility = [
-    "//absl/log:__pkg__",
-])
+package(
+    default_visibility = [
+        "//absl/log:__pkg__",
+    ],
+    features = [
+        "header_modules",
+        "layering_check",
+        "parse_headers",
+    ],
+)
 
 licenses(["notice"])
 
@@ -146,6 +153,7 @@
         ":conditions",
         ":log_message",
         ":strip",
+        "//absl/log:absl_vlog_is_on",
     ],
 )
 
@@ -213,6 +221,7 @@
         "//absl/base:config",
         "//absl/base:core_headers",
         "//absl/base:log_severity",
+        "//absl/base:no_destructor",
         "//absl/base:raw_logging_internal",
         "//absl/cleanup",
         "//absl/log:globals",
@@ -357,6 +366,60 @@
     ],
 )
 
+cc_library(
+    name = "fnmatch",
+    srcs = ["fnmatch.cc"],
+    hdrs = ["fnmatch.h"],
+    copts = ABSL_DEFAULT_COPTS,
+    linkopts = ABSL_DEFAULT_LINKOPTS,
+    deps = [
+        "//absl/base:config",
+        "//absl/strings",
+    ],
+)
+
+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",
@@ -378,6 +441,31 @@
         "//absl/base:log_severity",
         "//absl/log",
         "//absl/log:globals",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
+
+cc_test(
+    name = "fnmatch_test",
+    srcs = ["fnmatch_test.cc"],
+    copts = ABSL_TEST_COPTS,
+    linkopts = ABSL_DEFAULT_LINKOPTS,
+    deps = [
+        ":fnmatch",
+        "@com_google_googletest//:gtest",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
+
+cc_test(
+    name = "fnmatch_benchmark",
+    srcs = ["fnmatch_benchmark.cc"],
+    copts = ABSL_TEST_COPTS,
+    linkopts = ABSL_DEFAULT_LINKOPTS,
+    tags = ["benchmark"],
+    deps = [
+        ":fnmatch",
+        "@com_github_google_benchmark//:benchmark_main",
+    ],
+)
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 f576d65..645f3c2 100644
--- a/absl/log/internal/conditions.h
+++ b/absl/log/internal/conditions.h
@@ -57,7 +57,7 @@
 // (dangling else, missing switch case) and preserving noreturn semantics (e.g.
 // on `LOG(FATAL)`) without requiring braces.
 //
-// The `switch` ensures that this expansion is the begnning of a statement (as
+// The `switch` ensures that this expansion is the beginning of a statement (as
 // opposed to an expression) and prevents shenanigans like
 // `AFunction(LOG(INFO))` and `decltype(LOG(INFO))`.  The apparently-redundant
 // `default` case makes the condition more amenable to Clang dataflow analysis.
@@ -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`.
 //
@@ -137,21 +137,30 @@
                   ? true                                                   \
                   : (::absl::log_internal::ExitQuietly(), false))          \
            : false))
+#define ABSL_LOG_INTERNAL_CONDITION_DFATAL(type, condition)             \
+  ABSL_LOG_INTERNAL_##type##_CONDITION(                                 \
+      (ABSL_ASSUME(absl::kLogDebugFatal == absl::LogSeverity::kError || \
+                   absl::kLogDebugFatal == absl::LogSeverity::kFatal),  \
+       (condition) &&                                                   \
+           (::absl::kLogDebugFatal >=                                   \
+                static_cast<::absl::LogSeverity>(ABSL_MIN_LOG_LEVEL) || \
+            (::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)
@@ -163,12 +172,14 @@
   ABSL_LOG_INTERNAL_##type##_CONDITION(condition)
 #define ABSL_LOG_INTERNAL_CONDITION_QFATAL(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_DFATAL(type, condition) \
+  ABSL_LOG_INTERNAL_##type##_CONDITION(condition)
+#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 0c5e81e..c453978 100644
--- a/absl/log/internal/flags.h
+++ b/absl/log/internal/flags.h
@@ -33,7 +33,7 @@
 // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 
 // Log messages at this severity or above are sent to stderr in *addition* to
-// logfiles.  Defaults to `ERROR`.  See log_severity.h for numeric values of
+// `LogSink`s.  Defaults to `ERROR`.  See log_severity.h for numeric values of
 // severity levels.
 ABSL_DECLARE_FLAG(int, stderrthreshold);
 
@@ -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/fnmatch.cc b/absl/log/internal/fnmatch.cc
new file mode 100644
index 0000000..26e1e57
--- /dev/null
+++ b/absl/log/internal/fnmatch.cc
@@ -0,0 +1,73 @@
+// 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/internal/fnmatch.h"
+
+#include <cstddef>
+
+#include "absl/base/config.h"
+#include "absl/strings/string_view.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace log_internal {
+bool FNMatch(absl::string_view pattern, absl::string_view str) {
+  bool in_wildcard_match = false;
+  while (true) {
+    if (pattern.empty()) {
+      // `pattern` is exhausted; succeed if all of `str` was consumed matching
+      // it.
+      return in_wildcard_match || str.empty();
+    }
+    if (str.empty()) {
+      // `str` is exhausted; succeed if `pattern` is empty or all '*'s.
+      return pattern.find_first_not_of('*') == pattern.npos;
+    }
+    switch (pattern.front()) {
+      case '*':
+        pattern.remove_prefix(1);
+        in_wildcard_match = true;
+        break;
+      case '?':
+        pattern.remove_prefix(1);
+        str.remove_prefix(1);
+        break;
+      default:
+        if (in_wildcard_match) {
+          absl::string_view fixed_portion = pattern;
+          const size_t end = fixed_portion.find_first_of("*?");
+          if (end != fixed_portion.npos) {
+            fixed_portion = fixed_portion.substr(0, end);
+          }
+          const size_t match = str.find(fixed_portion);
+          if (match == str.npos) {
+            return false;
+          }
+          pattern.remove_prefix(fixed_portion.size());
+          str.remove_prefix(match + fixed_portion.size());
+          in_wildcard_match = false;
+        } else {
+          if (pattern.front() != str.front()) {
+            return false;
+          }
+          pattern.remove_prefix(1);
+          str.remove_prefix(1);
+        }
+        break;
+    }
+  }
+}
+}  // namespace log_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
diff --git a/absl/log/internal/fnmatch.h b/absl/log/internal/fnmatch.h
new file mode 100644
index 0000000..4ea147c
--- /dev/null
+++ b/absl/log/internal/fnmatch.h
@@ -0,0 +1,35 @@
+// 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.
+
+#ifndef ABSL_LOG_INTERNAL_FNMATCH_H_
+#define ABSL_LOG_INTERNAL_FNMATCH_H_
+
+#include "absl/base/config.h"
+#include "absl/strings/string_view.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace log_internal {
+// Like POSIX `fnmatch`, but:
+// * accepts `string_view`
+// * does not allocate any dynamic memory
+// * only supports * and ? wildcards and not bracket expressions [...]
+// * wildcards may match /
+// * no backslash-escaping
+bool FNMatch(absl::string_view pattern, absl::string_view str);
+}  // namespace log_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif  // ABSL_LOG_INTERNAL_FNMATCH_H_
diff --git a/absl/log/internal/fnmatch_benchmark.cc b/absl/log/internal/fnmatch_benchmark.cc
new file mode 100644
index 0000000..f062ba2
--- /dev/null
+++ b/absl/log/internal/fnmatch_benchmark.cc
@@ -0,0 +1,29 @@
+// 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/internal/fnmatch.h"
+#include "benchmark/benchmark.h"
+
+namespace {
+void BM_FNMatch(benchmark::State& state) {
+  while (state.KeepRunning()) {
+    bool ret =
+        absl::log_internal::FNMatch("*?*asdf*?*we???asdf**asdf*we",
+                                    "QWERFASVWERASDFWEDFASDasdfQWERGFWASDERREWF"
+                                    "weHOOasdf@#$%TW#ZSERasdfQW#REGTZSERERwe");
+    benchmark::DoNotOptimize(ret);
+  }
+}
+BENCHMARK(BM_FNMatch);
+}  // namespace
diff --git a/absl/log/internal/fnmatch_test.cc b/absl/log/internal/fnmatch_test.cc
new file mode 100644
index 0000000..e16a64e
--- /dev/null
+++ b/absl/log/internal/fnmatch_test.cc
@@ -0,0 +1,59 @@
+// 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/internal/fnmatch.h"
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace {
+using ::testing::IsFalse;
+using ::testing::IsTrue;
+
+TEST(FNMatchTest, Works) {
+  using absl::log_internal::FNMatch;
+  EXPECT_THAT(FNMatch("foo", "foo"), IsTrue());
+  EXPECT_THAT(FNMatch("foo", "bar"), IsFalse());
+  EXPECT_THAT(FNMatch("foo", "fo"), IsFalse());
+  EXPECT_THAT(FNMatch("foo", "foo2"), IsFalse());
+  EXPECT_THAT(FNMatch("bar/foo.ext", "bar/foo.ext"), IsTrue());
+  EXPECT_THAT(FNMatch("*ba*r/fo*o.ext*", "bar/foo.ext"), IsTrue());
+  EXPECT_THAT(FNMatch("bar/foo.ext", "bar/baz.ext"), IsFalse());
+  EXPECT_THAT(FNMatch("bar/foo.ext", "bar/foo"), IsFalse());
+  EXPECT_THAT(FNMatch("bar/foo.ext", "bar/foo.ext.zip"), IsFalse());
+  EXPECT_THAT(FNMatch("ba?/*.ext", "bar/foo.ext"), IsTrue());
+  EXPECT_THAT(FNMatch("ba?/*.ext", "baZ/FOO.ext"), IsTrue());
+  EXPECT_THAT(FNMatch("ba?/*.ext", "barr/foo.ext"), IsFalse());
+  EXPECT_THAT(FNMatch("ba?/*.ext", "bar/foo.ext2"), IsFalse());
+  EXPECT_THAT(FNMatch("ba?/*", "bar/foo.ext2"), IsTrue());
+  EXPECT_THAT(FNMatch("ba?/*", "bar/"), IsTrue());
+  EXPECT_THAT(FNMatch("ba?/?", "bar/"), IsFalse());
+  EXPECT_THAT(FNMatch("ba?/*", "bar"), IsFalse());
+  EXPECT_THAT(FNMatch("?x", "zx"), IsTrue());
+  EXPECT_THAT(FNMatch("*b", "aab"), IsTrue());
+  EXPECT_THAT(FNMatch("a*b", "aXb"), IsTrue());
+  EXPECT_THAT(FNMatch("", ""), IsTrue());
+  EXPECT_THAT(FNMatch("", "a"), IsFalse());
+  EXPECT_THAT(FNMatch("ab*", "ab"), IsTrue());
+  EXPECT_THAT(FNMatch("ab**", "ab"), IsTrue());
+  EXPECT_THAT(FNMatch("ab*?", "ab"), IsFalse());
+  EXPECT_THAT(FNMatch("*", "bbb"), IsTrue());
+  EXPECT_THAT(FNMatch("*", ""), IsTrue());
+  EXPECT_THAT(FNMatch("?", ""), IsFalse());
+  EXPECT_THAT(FNMatch("***", "**p"), IsTrue());
+  EXPECT_THAT(FNMatch("**", "*"), IsTrue());
+  EXPECT_THAT(FNMatch("*?", "*"), IsTrue());
+}
+
+}  // namespace
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 4693772..4ecb8a1 100644
--- a/absl/log/internal/log_message.h
+++ b/absl/log/internal/log_message.h
@@ -40,7 +40,7 @@
 #include "absl/log/internal/nullguard.h"
 #include "absl/log/log_entry.h"
 #include "absl/log/log_sink.h"
-#include "absl/strings/internal/has_absl_stringify.h"
+#include "absl/strings/has_absl_stringify.h"
 #include "absl/strings/string_view.h"
 #include "absl/time/time.h"
 
@@ -170,15 +170,15 @@
 
   // Types that support `AbslStringify()` are serialized that way.
   template <typename T,
-            typename std::enable_if<
-                strings_internal::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<
-                !strings_internal::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
@@ -283,8 +283,7 @@
 
 // Note: the following is declared `ABSL_ATTRIBUTE_NOINLINE`
 template <typename T,
-          typename std::enable_if<strings_internal::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.
@@ -294,8 +293,7 @@
 
 // Note: the following is declared `ABSL_ATTRIBUTE_NOINLINE`
 template <typename T,
-          typename std::enable_if<!strings_internal::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/log_sink_set.cc b/absl/log/internal/log_sink_set.cc
index b7cbe36..3d5c699 100644
--- a/absl/log/internal/log_sink_set.cc
+++ b/absl/log/internal/log_sink_set.cc
@@ -35,6 +35,7 @@
 #include "absl/base/config.h"
 #include "absl/base/internal/raw_logging.h"
 #include "absl/base/log_severity.h"
+#include "absl/base/no_destructor.h"
 #include "absl/base/thread_annotations.h"
 #include "absl/cleanup/cleanup.h"
 #include "absl/log/globals.h"
@@ -168,17 +169,16 @@
 #if defined(__myriad2__) || defined(__Fuchsia__)
     // myriad2 and Fuchsia do not log to stderr by default.
 #else
-    static StderrLogSink* stderr_log_sink = new StderrLogSink;
-    AddLogSink(stderr_log_sink);
+    static absl::NoDestructor<StderrLogSink> stderr_log_sink;
+    AddLogSink(stderr_log_sink.get());
 #endif
 #ifdef __ANDROID__
-    static AndroidLogSink* android_log_sink = new AndroidLogSink;
-    AddLogSink(android_log_sink);
+    static absl::NoDestructor<AndroidLogSink> android_log_sink;
+    AddLogSink(android_log_sink.get());
 #endif
 #if defined(_WIN32)
-    static WindowsDebuggerLogSink* debugger_log_sink =
-        new WindowsDebuggerLogSink;
-    AddLogSink(debugger_log_sink);
+    static absl::NoDestructor<WindowsDebuggerLogSink> debugger_log_sink;
+    AddLogSink(debugger_log_sink.get());
 #endif  // !defined(_WIN32)
   }
 
@@ -268,7 +268,7 @@
 
 // Returns reference to the global LogSinks set.
 GlobalLogSinkSet& GlobalSinks() {
-  static GlobalLogSinkSet* global_sinks = new GlobalLogSinkSet;
+  static absl::NoDestructor<GlobalLogSinkSet> global_sinks;
   return *global_sinks;
 }
 
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 602b5ac..b721b08 100644
--- a/absl/log/log.h
+++ b/absl/log/log.h
@@ -32,6 +32,8 @@
 // * The `QFATAL` pseudo-severity level is equivalent to `FATAL` but triggers
 //   quieter termination messages, e.g. without a full stack trace, and skips
 //   running registered error handlers.
+// * The `DFATAL` pseudo-severity level is defined as `FATAL` in debug mode and
+//   as `ERROR` otherwise.
 // Some preprocessor shenanigans are used to ensure that e.g. `LOG(INFO)` has
 // the same meaning even if a local symbol or preprocessor macro named `INFO` is
 // defined.  To specify a severity level using an expression instead of a
@@ -51,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.
@@ -188,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()
 //
@@ -211,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) \
@@ -283,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_basic_test_impl.inc b/absl/log/log_basic_test_impl.inc
index f340009..e2f3356 100644
--- a/absl/log/log_basic_test_impl.inc
+++ b/absl/log/log_basic_test_impl.inc
@@ -277,6 +277,96 @@
 }
 #endif
 
+#ifdef NDEBUG
+TEST_P(BasicLogTest, DFatal) {
+  absl::log_internal::ScopedMinLogLevel scoped_min_log_level(GetParam());
+
+  absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
+
+  const int log_line = __LINE__ + 1;
+  auto do_log = [] { ABSL_TEST_LOG(DFATAL) << "hello world"; };
+
+  if (LoggingEnabledAt(absl::LogSeverity::kError)) {
+    EXPECT_CALL(
+        test_sink,
+        Send(AllOf(SourceFilename(Eq(__FILE__)),
+                   SourceBasename(Eq("log_basic_test_impl.inc")),
+                   SourceLine(Eq(log_line)), Prefix(IsTrue()),
+                   LogSeverity(Eq(absl::LogSeverity::kError)),
+                   TimestampInMatchWindow(),
+                   ThreadID(Eq(absl::base_internal::GetTID())),
+                   TextMessage(Eq("hello world")),
+                   Verbosity(Eq(absl::LogEntry::kNoVerbosityLevel)),
+                   ENCODED_MESSAGE(EqualsProto(R"pb(value {
+                                                      literal: "hello world"
+                                                    })pb")),
+                   Stacktrace(IsEmpty()))));
+  }
+
+  test_sink.StartCapturingLogs();
+  do_log();
+}
+
+#elif GTEST_HAS_DEATH_TEST
+TEST_P(BasicLogDeathTest, DFatal) {
+  // TODO(b/242568884): re-enable once bug is fixed.
+  // absl::log_internal::ScopedMinLogLevel scoped_min_log_level(GetParam());
+
+  const int log_line = __LINE__ + 1;
+  auto do_log = [] { ABSL_TEST_LOG(DFATAL) << "hello world"; };
+
+  EXPECT_EXIT(
+      {
+        absl::ScopedMockLog test_sink(
+            absl::MockLogDefault::kDisallowUnexpected);
+
+        EXPECT_CALL(test_sink, Send)
+            .Times(AnyNumber())
+            .WillRepeatedly(DeathTestUnexpectedLogging());
+
+        ::testing::InSequence s;
+
+        if (LoggingEnabledAt(absl::LogSeverity::kFatal)) {
+          // The first call without the stack trace.
+          EXPECT_CALL(
+              test_sink,
+              Send(AllOf(SourceFilename(Eq(__FILE__)),
+                         SourceBasename(Eq("log_basic_test_impl.inc")),
+                         SourceLine(Eq(log_line)), Prefix(IsTrue()),
+                         LogSeverity(Eq(absl::LogSeverity::kFatal)),
+                         TimestampInMatchWindow(),
+                         ThreadID(Eq(absl::base_internal::GetTID())),
+                         TextMessage(Eq("hello world")),
+                         Verbosity(Eq(absl::LogEntry::kNoVerbosityLevel)),
+                         ENCODED_MESSAGE(EqualsProto(
+                             R"pb(value { literal: "hello world" })pb")),
+                         Stacktrace(IsEmpty()))))
+              .WillOnce(DeathTestExpectedLogging());
+
+          // The second call with the stack trace.
+          EXPECT_CALL(
+              test_sink,
+              Send(AllOf(SourceFilename(Eq(__FILE__)),
+                         SourceBasename(Eq("log_basic_test_impl.inc")),
+                         SourceLine(Eq(log_line)), Prefix(IsTrue()),
+                         LogSeverity(Eq(absl::LogSeverity::kFatal)),
+                         TimestampInMatchWindow(),
+                         ThreadID(Eq(absl::base_internal::GetTID())),
+                         TextMessage(Eq("hello world")),
+                         Verbosity(Eq(absl::LogEntry::kNoVerbosityLevel)),
+                         ENCODED_MESSAGE(EqualsProto(
+                             R"pb(value { literal: "hello world" })pb")),
+                         Stacktrace(Not(IsEmpty())))))
+              .WillOnce(DeathTestExpectedLogging());
+        }
+
+        test_sink.StartCapturingLogs();
+        do_log();
+      },
+      DiedOfFatal, DeathTestValidateExpectations());
+}
+#endif
+
 TEST_P(BasicLogTest, Level) {
   absl::log_internal::ScopedMinLogLevel scoped_min_log_level(GetParam());
 
diff --git a/absl/log/log_entry.cc b/absl/log/log_entry.cc
index 19c3b3f..fe58a57 100644
--- a/absl/log/log_entry.cc
+++ b/absl/log/log_entry.cc
@@ -25,5 +25,17 @@
 constexpr int LogEntry::kNoVerboseLevel;
 #endif
 
+// https://github.com/abseil/abseil-cpp/issues/1465
+// CMake builds on Apple platforms error when libraries are empty.
+// Our CMake configuration can avoid this error on header-only libraries,
+// but since this library is conditionally empty, including a single
+// variable is an easy workaround.
+#ifdef __APPLE__
+namespace log_internal {
+extern const char kAvoidEmptyLogEntryLibraryWarning;
+const char kAvoidEmptyLogEntryLibraryWarning = 0;
+}  // namespace log_internal
+#endif  // __APPLE__
+
 ABSL_NAMESPACE_END
 }  // namespace absl
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/log_streamer.h b/absl/log/log_streamer.h
index 2d41a07..4ed2435 100644
--- a/absl/log/log_streamer.h
+++ b/absl/log/log_streamer.h
@@ -165,6 +165,16 @@
   return absl::LogStreamer(absl::LogSeverity::kFatal, file, line);
 }
 
+// LogDebugFatalStreamer()
+//
+// Returns a LogStreamer that writes at level LogSeverity::kLogDebugFatal.
+//
+// In debug mode, the program will be terminated when this `LogStreamer` is
+// destroyed, regardless of whether any data were streamed in.
+inline LogStreamer LogDebugFatalStreamer(absl::string_view file, int line) {
+  return absl::LogStreamer(absl::kLogDebugFatal, file, line);
+}
+
 ABSL_NAMESPACE_END
 }  // namespace absl
 
diff --git a/absl/log/log_streamer_test.cc b/absl/log/log_streamer_test.cc
index 328d70d..40c7d48 100644
--- a/absl/log/log_streamer_test.cc
+++ b/absl/log/log_streamer_test.cc
@@ -151,6 +151,57 @@
 }
 #endif
 
+#ifdef NDEBUG
+TEST(LogStreamerTest, LogDebugFatalStreamer) {
+  absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
+
+  EXPECT_CALL(
+      test_sink,
+      Send(AllOf(SourceFilename(Eq("path/file.cc")), SourceLine(Eq(1234)),
+                 Prefix(IsTrue()), LogSeverity(Eq(absl::LogSeverity::kError)),
+                 TimestampInMatchWindow(),
+                 ThreadID(Eq(absl::base_internal::GetTID())),
+                 TextMessage(Eq("WriteToStream: foo")),
+                 ENCODED_MESSAGE(EqualsProto(R"pb(value {
+                                                    str: "WriteToStream: foo"
+                                                  })pb")),
+                 Stacktrace(IsEmpty()))));
+
+  test_sink.StartCapturingLogs();
+  WriteToStream("foo",
+                &absl::LogDebugFatalStreamer("path/file.cc", 1234).stream());
+}
+#elif GTEST_HAS_DEATH_TEST
+TEST(LogStreamerDeathTest, LogDebugFatalStreamer) {
+  EXPECT_EXIT(
+      {
+        absl::ScopedMockLog test_sink;
+
+        EXPECT_CALL(test_sink, Send)
+            .Times(AnyNumber())
+            .WillRepeatedly(DeathTestUnexpectedLogging());
+
+        EXPECT_CALL(
+            test_sink,
+            Send(AllOf(
+                SourceFilename(Eq("path/file.cc")), SourceLine(Eq(1234)),
+                Prefix(IsTrue()), LogSeverity(Eq(absl::LogSeverity::kFatal)),
+                TimestampInMatchWindow(),
+                ThreadID(Eq(absl::base_internal::GetTID())),
+                TextMessage(Eq("WriteToStream: foo")),
+                ENCODED_MESSAGE(EqualsProto(R"pb(value {
+                                                   str: "WriteToStream: foo"
+                                                 })pb")))))
+            .WillOnce(DeathTestExpectedLogging());
+
+        test_sink.StartCapturingLogs();
+        WriteToStream(
+            "foo", &absl::LogDebugFatalStreamer("path/file.cc", 1234).stream());
+      },
+      DiedOfFatal, DeathTestValidateExpectations());
+}
+#endif
+
 TEST(LogStreamerTest, LogStreamer) {
   absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
 
diff --git a/absl/log/stripping_test.cc b/absl/log/stripping_test.cc
index aff9149..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"
@@ -322,6 +323,49 @@
   }
 }
 
+TEST_F(StrippingTest, DFatal) {
+  // We need to load a copy of the needle string into memory (so we can search
+  // for it) without leaving it lying around in plaintext in the executable file
+  // as would happen if we used a literal.  We might (or might not) leave it
+  // lying around later; that's what the tests are for!
+  const std::string needle = absl::Base64Escape("StrippingTest.DFatal");
+  // We don't care if the LOG statement is actually executed, we're just
+  // checking that it's stripped.
+  if (kReallyDie) LOG(DFATAL) << "U3RyaXBwaW5nVGVzdC5ERmF0YWw=";
+
+  std::unique_ptr<FILE, std::function<void(FILE*)>> exe = OpenTestExecutable();
+  ASSERT_THAT(exe, NotNull());
+  // `DFATAL` can be `ERROR` or `FATAL`, and a compile-time optimizer doesn't
+  // know which, because `absl::kLogDebugFatal` is declared `extern` and defined
+  // in another TU.  Link-time optimization might do better.  We have six cases:
+  // |         `AMLL` is-> | `<=ERROR` | `FATAL` | `>FATAL` |
+  // | ------------------- | --------- | ------- | -------- |
+  // | `DFATAL` is `ERROR` |   present |       ? | stripped |
+  // | `DFATAL` is `FATAL` |   present | present | stripped |
+
+  // These constexpr variables are used to suppress unreachable code warnings
+  // in the if-else statements below.
+
+  // "present" in the table above: `DFATAL` exceeds `ABSL_MIN_LOG_LEVEL`, so
+  // `DFATAL` statements should not be stripped (and they should be logged
+  // when executed, but that's a different testsuite).
+  constexpr bool kExpectPresent = absl::kLogDebugFatal >= kAbslMinLogLevel;
+
+  // "stripped" in the table above: even though the compiler may not know
+  // which value `DFATAL` has, it should be able to strip it since both
+  // possible values ought to be stripped.
+  constexpr bool kExpectStripped = kAbslMinLogLevel > absl::LogSeverity::kFatal;
+
+  if (kExpectPresent) {
+    EXPECT_THAT(exe.get(), FileHasSubstr(needle));
+  } else if (kExpectStripped) {
+    EXPECT_THAT(exe.get(), Not(FileHasSubstr(needle)));
+  } else {
+    // "?" in the table above; may or may not be stripped depending on whether
+    // any link-time optimization is done.  Either outcome is ok.
+  }
+}
+
 TEST_F(StrippingTest, Level) {
   const std::string needle = absl::Base64Escape("StrippingTest.Level");
   volatile auto severity = absl::LogSeverity::kWarning;
@@ -371,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..7898651
--- /dev/null
+++ b/absl/log/vlog_is_on.h
@@ -0,0 +1,72 @@
+// 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
+
+// IWYU pragma: private, include "absl/log/log.h"
+
+// 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..883d798
--- /dev/null
+++ b/absl/log/vlog_is_on_test.cc
@@ -0,0 +1,176 @@
+// 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/globals.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/memory/BUILD.bazel b/absl/memory/BUILD.bazel
index a93f54a..4573f17 100644
--- a/absl/memory/BUILD.bazel
+++ b/absl/memory/BUILD.bazel
@@ -21,7 +21,14 @@
     "ABSL_TEST_COPTS",
 )
 
-package(default_visibility = ["//visibility:public"])
+package(
+    default_visibility = ["//visibility:public"],
+    features = [
+        "header_modules",
+        "layering_check",
+        "parse_headers",
+    ],
+)
 
 licenses(["notice"])
 
@@ -47,6 +54,7 @@
     deps = [
         ":memory",
         "//absl/base:core_headers",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
diff --git a/absl/meta/BUILD.bazel b/absl/meta/BUILD.bazel
index 13051d8..cf5df9b 100644
--- a/absl/meta/BUILD.bazel
+++ b/absl/meta/BUILD.bazel
@@ -21,7 +21,14 @@
     "ABSL_TEST_COPTS",
 )
 
-package(default_visibility = ["//visibility:public"])
+package(
+    default_visibility = ["//visibility:public"],
+    features = [
+        "header_modules",
+        "layering_check",
+        "parse_headers",
+    ],
+)
 
 licenses(["notice"])
 
@@ -46,6 +53,7 @@
         "//absl/base:config",
         "//absl/base:core_headers",
         "//absl/time",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
diff --git a/absl/numeric/BUILD.bazel b/absl/numeric/BUILD.bazel
index c5aaf72..db02b9c 100644
--- a/absl/numeric/BUILD.bazel
+++ b/absl/numeric/BUILD.bazel
@@ -19,7 +19,14 @@
     "ABSL_TEST_COPTS",
 )
 
-package(default_visibility = ["//visibility:public"])
+package(
+    default_visibility = ["//visibility:public"],
+    features = [
+        "header_modules",
+        "layering_check",
+        "parse_headers",
+    ],
+)
 
 licenses(["notice"])
 
@@ -48,6 +55,7 @@
         "//absl/base:core_headers",
         "//absl/random",
         "@com_github_google_benchmark//:benchmark_main",
+        "@com_google_googletest//:gtest",
     ],
 )
 
@@ -62,6 +70,7 @@
     deps = [
         ":bits",
         "//absl/random",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -98,6 +107,7 @@
         "//absl/hash:hash_testing",
         "//absl/meta:type_traits",
         "//absl/strings",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -112,6 +122,7 @@
         ":int128",
         "//absl/base:config",
         "@com_github_google_benchmark//:benchmark_main",
+        "@com_google_googletest//:gtest",
     ],
 )
 
diff --git a/absl/numeric/bits.h b/absl/numeric/bits.h
index 5ed36f5..c76454c 100644
--- a/absl/numeric/bits.h
+++ b/absl/numeric/bits.h
@@ -49,9 +49,19 @@
 
 namespace absl {
 ABSL_NAMESPACE_BEGIN
-#if !(defined(__cpp_lib_bitops) && __cpp_lib_bitops >= 201907L)
 
-// rotating
+// https://github.com/llvm/llvm-project/issues/64544
+// libc++ had the wrong signature for std::rotl and std::rotr
+// prior to libc++ 18.0.
+//
+#if (defined(__cpp_lib_bitops) && __cpp_lib_bitops >= 201907L) &&     \
+    (!defined(_LIBCPP_VERSION) || _LIBCPP_VERSION >= 180000)
+using std::rotl;
+using std::rotr;
+
+#else
+
+// Rotating functions
 template <class T>
 ABSL_MUST_USE_RESULT constexpr
     typename std::enable_if<std::is_unsigned<T>::value, T>::type
@@ -66,6 +76,22 @@
   return numeric_internal::RotateRight(x, s);
 }
 
+#endif
+
+// https://github.com/llvm/llvm-project/issues/64544
+// libc++ had the wrong signature for std::rotl and std::rotr
+// prior to libc++ 18.0.
+//
+#if (defined(__cpp_lib_bitops) && __cpp_lib_bitops >= 201907L)
+
+using std::countl_one;
+using std::countl_zero;
+using std::countr_one;
+using std::countr_zero;
+using std::popcount;
+
+#else
+
 // Counting functions
 //
 // While these functions are typically constexpr, on some platforms, they may
@@ -107,19 +133,18 @@
     popcount(T x) noexcept {
   return numeric_internal::Popcount(x);
 }
-#else  // defined(__cpp_lib_bitops) && __cpp_lib_bitops >= 201907L
-
-using std::countl_one;
-using std::countl_zero;
-using std::countr_one;
-using std::countr_zero;
-using std::popcount;
-using std::rotl;
-using std::rotr;
 
 #endif
 
-#if !(defined(__cpp_lib_int_pow2) && __cpp_lib_int_pow2 >= 202002L)
+#if (defined(__cpp_lib_int_pow2) && __cpp_lib_int_pow2 >= 202002L)
+
+using std::bit_ceil;
+using std::bit_floor;
+using std::bit_width;
+using std::has_single_bit;
+
+#else
+
 // Returns: true if x is an integral power of two; false otherwise.
 template <class T>
 constexpr inline typename std::enable_if<std::is_unsigned<T>::value, bool>::type
@@ -162,12 +187,6 @@
   return has_single_bit(x) ? T{1} << (bit_width(x) - 1)
                            : numeric_internal::BitCeilNonPowerOf2(x);
 }
-#else  // defined(__cpp_lib_int_pow2) && __cpp_lib_int_pow2 >= 202002L
-
-using std::bit_ceil;
-using std::bit_floor;
-using std::bit_width;
-using std::has_single_bit;
 
 #endif
 
diff --git a/absl/profiling/BUILD.bazel b/absl/profiling/BUILD.bazel
index 3392c96..86f205f 100644
--- a/absl/profiling/BUILD.bazel
+++ b/absl/profiling/BUILD.bazel
@@ -19,7 +19,14 @@
     "ABSL_TEST_COPTS",
 )
 
-package(default_visibility = ["//visibility:private"])
+package(
+    default_visibility = ["//visibility:private"],
+    features = [
+        "header_modules",
+        "layering_check",
+        "parse_headers",
+    ],
+)
 
 licenses(["notice"])
 
@@ -52,6 +59,7 @@
         "//absl/synchronization",
         "//absl/synchronization:thread_pool",
         "//absl/time",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -80,6 +88,7 @@
     deps = [
         ":exponential_biased",
         "//absl/strings",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -91,6 +100,7 @@
     copts = ABSL_DEFAULT_COPTS,
     linkopts = ABSL_DEFAULT_LINKOPTS,
     visibility = [
+        # TODO(b/304670045): remove after periodic_sampler moves to //spanner/common.
         "//absl:__subpackages__",
     ],
     deps = [
@@ -109,6 +119,7 @@
     deps = [
         ":periodic_sampler",
         "//absl/base:core_headers",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
diff --git a/absl/random/BUILD.bazel b/absl/random/BUILD.bazel
index 19130ff..80c4f05 100644
--- a/absl/random/BUILD.bazel
+++ b/absl/random/BUILD.bazel
@@ -23,7 +23,14 @@
     "ABSL_TEST_COPTS",
 )
 
-package(default_visibility = ["//visibility:public"])
+package(
+    default_visibility = ["//visibility:public"],
+    features = [
+        "header_modules",
+        "layering_check",
+        "parse_headers",
+    ],
+)
 
 licenses(["notice"])
 
@@ -171,6 +178,7 @@
         ":random",
         "//absl/random/internal:pcg_engine",
         "//absl/random/internal:sequence_urbg",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -196,6 +204,7 @@
         "//absl/random/internal:sequence_urbg",
         "//absl/strings",
         "//absl/strings:str_format",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -213,6 +222,7 @@
         ":distributions",
         ":random",
         "//absl/random/internal:distribution_test_util",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -226,6 +236,7 @@
     deps = [
         ":distributions",
         ":random",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -250,6 +261,7 @@
         "//absl/random/internal:sequence_urbg",
         "//absl/strings",
         "//absl/strings:str_format",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -270,6 +282,7 @@
         "//absl/random/internal:pcg_engine",
         "//absl/random/internal:sequence_urbg",
         "//absl/strings",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -299,6 +312,7 @@
         "//absl/random/internal:sequence_urbg",
         "//absl/strings",
         "//absl/strings:str_format",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -321,6 +335,7 @@
         "//absl/random/internal:sequence_urbg",
         "//absl/strings",
         "//absl/strings:str_format",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -344,6 +359,7 @@
         "//absl/random/internal:sequence_urbg",
         "//absl/strings",
         "//absl/strings:str_format",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -365,6 +381,7 @@
         "//absl/random/internal:pcg_engine",
         "//absl/random/internal:sequence_urbg",
         "//absl/strings",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -391,6 +408,7 @@
         "//absl/random/internal:pcg_engine",
         "//absl/random/internal:sequence_urbg",
         "//absl/strings",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -411,6 +429,7 @@
         "//absl/random/internal:pcg_engine",
         "//absl/random/internal:sequence_urbg",
         "//absl/strings",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -426,6 +445,7 @@
         ":random",
         "//absl/base:fast_type_id",
         "//absl/random/internal:sequence_urbg",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -444,6 +464,7 @@
         ":mock_distributions",
         ":mocking_bit_gen",
         ":random",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -461,6 +482,7 @@
         ":mock_distributions",
         ":mocking_bit_gen",
         ":random",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -476,6 +498,7 @@
     ],
     deps = [
         ":random",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -490,6 +513,7 @@
         ":random",
         ":seed_sequences",
         "//absl/random/internal:nonsecure_base",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
diff --git a/absl/random/distributions.h b/absl/random/distributions.h
index 37fc3aa..4e3b332 100644
--- a/absl/random/distributions.h
+++ b/absl/random/distributions.h
@@ -362,7 +362,7 @@
 // If `lo` is nonzero then this distribution is shifted to the desired interval,
 // so LogUniform(lo, hi, b) is equivalent to LogUniform(0, hi-lo, b)+lo.
 //
-// See http://ecolego.facilia.se/ecolego/show/Log-Uniform%20Distribution
+// See https://en.wikipedia.org/wiki/Log-normal_distribution
 //
 // Example:
 //
diff --git a/absl/random/internal/BUILD.bazel b/absl/random/internal/BUILD.bazel
index 37f4d6e..71a742e 100644
--- a/absl/random/internal/BUILD.bazel
+++ b/absl/random/internal/BUILD.bazel
@@ -28,7 +28,14 @@
     "//absl/random:__pkg__",
 ]
 
-package(default_visibility = default_package_visibility)
+package(
+    default_visibility = default_package_visibility,
+    features = [
+        "header_modules",
+        "layering_check",
+        "parse_headers",
+    ],
+)
 
 licenses(["notice"])
 
@@ -409,6 +416,7 @@
     linkopts = ABSL_DEFAULT_LINKOPTS,
     deps = [
         ":traits",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -425,6 +433,7 @@
         ":generate_real",
         "//absl/flags:flag",
         "//absl/numeric:bits",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -437,6 +446,7 @@
     linkopts = ABSL_DEFAULT_LINKOPTS,
     deps = [
         ":distribution_test_util",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -449,6 +459,7 @@
     linkopts = ABSL_DEFAULT_LINKOPTS,
     deps = [
         ":fastmath",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -462,6 +473,7 @@
     deps = [
         ":explicit_seed_seq",
         "//absl/random:seed_sequences",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -474,6 +486,7 @@
     linkopts = ABSL_DEFAULT_LINKOPTS,
     deps = [
         ":salted_seed_seq",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -489,6 +502,7 @@
     deps = [
         ":distribution_test_util",
         "//absl/base:core_headers",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -503,6 +517,7 @@
     linkopts = ABSL_DEFAULT_LINKOPTS,
     deps = [
         ":fast_uniform_bits",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -543,6 +558,7 @@
         "//absl/random:distributions",
         "//absl/random:seed_sequences",
         "//absl/strings",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -555,6 +571,7 @@
     linkopts = ABSL_DEFAULT_LINKOPTS,
     deps = [
         ":seed_material",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -571,6 +588,7 @@
         ":pool_urbg",
         "//absl/meta:type_traits",
         "//absl/types:span",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -586,6 +604,7 @@
         ":explicit_seed_seq",
         ":pcg_engine",
         "//absl/time",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -604,6 +623,7 @@
         "//absl/log",
         "//absl/strings",
         "//absl/time",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -617,6 +637,7 @@
     deps = [
         ":randen",
         "//absl/meta:type_traits",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -631,6 +652,7 @@
         ":platform",
         ":randen_slow",
         "//absl/base:endian",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -662,6 +684,7 @@
         ":wide_multiply",
         "//absl/numeric:bits",
         "//absl/numeric:int128",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -742,6 +765,7 @@
     linkopts = ABSL_DEFAULT_LINKOPTS,
     deps = [
         ":iostream_state_saver",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -754,6 +778,7 @@
     linkopts = ABSL_DEFAULT_LINKOPTS,
     deps = [
         ":uniform_helper",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
diff --git a/absl/status/BUILD.bazel b/absl/status/BUILD.bazel
index 1f58b30..981b37f 100644
--- a/absl/status/BUILD.bazel
+++ b/absl/status/BUILD.bazel
@@ -24,13 +24,21 @@
     "ABSL_TEST_COPTS",
 )
 
-package(default_visibility = ["//visibility:public"])
+package(
+    default_visibility = ["//visibility:public"],
+    features = [
+        "header_modules",
+        "layering_check",
+        "parse_headers",
+    ],
+)
 
 licenses(["notice"])
 
 cc_library(
     name = "status",
     srcs = [
+        "internal/status_internal.cc",
         "internal/status_internal.h",
         "status.cc",
         "status_payload_printer.cc",
@@ -43,17 +51,22 @@
     linkopts = ABSL_DEFAULT_LINKOPTS,
     deps = [
         "//absl/base:atomic_hook",
+        "//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",
         "//absl/debugging:stacktrace",
         "//absl/debugging:symbolize",
         "//absl/functional:function_ref",
+        "//absl/memory",
         "//absl/strings",
         "//absl/strings:cord",
         "//absl/strings:str_format",
         "//absl/types:optional",
+        "//absl/types:span",
     ],
 )
 
@@ -65,6 +78,9 @@
     deps = [
         ":status",
         "//absl/strings",
+        "//absl/strings:cord",
+        "//absl/strings:str_format",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -83,10 +99,14 @@
     deps = [
         ":status",
         "//absl/base",
+        "//absl/base:config",
         "//absl/base:core_headers",
+        "//absl/base:nullability",
         "//absl/base:raw_logging_internal",
         "//absl/meta:type_traits",
         "//absl/strings",
+        "//absl/strings:has_ostream_operator",
+        "//absl/strings:str_format",
         "//absl/types:variant",
         "//absl/utility",
     ],
@@ -103,7 +123,9 @@
         "//absl/memory",
         "//absl/strings",
         "//absl/types:any",
+        "//absl/types:variant",
         "//absl/utility",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
diff --git a/absl/status/CMakeLists.txt b/absl/status/CMakeLists.txt
index 4a3c5d6..00415ab 100644
--- a/absl/status/CMakeLists.txt
+++ b/absl/status/CMakeLists.txt
@@ -20,6 +20,7 @@
     "status.h"
   SRCS
     "internal/status_internal.h"
+    "internal/status_internal.cc"
     "status.cc"
     "status_payload_printer.h"
     "status_payload_printer.cc"
@@ -34,11 +35,15 @@
     absl::core_headers
     absl::function_ref
     absl::inlined_vector
+    absl::memory
+    absl::no_destructor
+    absl::nullability
     absl::optional
     absl::raw_logging_internal
+    absl::span
     absl::stacktrace
-    absl::str_format
     absl::strerror
+    absl::str_format
     absl::strings
     absl::symbolize
   PUBLIC
@@ -53,6 +58,7 @@
     ${ABSL_TEST_COPTS}
   DEPS
     absl::status
+    absl::str_format
     absl::strings
     GTest::gmock_main
 )
@@ -69,11 +75,15 @@
     ${ABSL_DEFAULT_COPTS}
   DEPS
     absl::base
-    absl::status
+    absl::config
     absl::core_headers
+    absl::has_ostream_operator
+    absl::nullability
     absl::raw_logging_internal
-    absl::type_traits
+    absl::status
+    absl::str_format
     absl::strings
+    absl::type_traits
     absl::utility
     absl::variant
   PUBLIC
@@ -89,5 +99,6 @@
   DEPS
     absl::status
     absl::statusor
+    absl::strings
     GTest::gmock_main
 )
diff --git a/absl/status/internal/status_internal.cc b/absl/status/internal/status_internal.cc
new file mode 100644
index 0000000..a915675
--- /dev/null
+++ b/absl/status/internal/status_internal.cc
@@ -0,0 +1,248 @@
+// 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/status/internal/status_internal.h"
+
+#include <atomic>
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+#include <cstdio>
+#include <cstring>
+#include <memory>
+#include <string>
+#include <utility>
+
+#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"
+#include "absl/status/status.h"
+#include "absl/status/status_payload_printer.h"
+#include "absl/strings/cord.h"
+#include "absl/strings/escaping.h"
+#include "absl/strings/str_cat.h"
+#include "absl/strings/str_format.h"
+#include "absl/strings/str_split.h"
+#include "absl/strings/string_view.h"
+#include "absl/types/optional.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace status_internal {
+
+void StatusRep::Unref() const {
+  // Fast path: if ref==1, there is no need for a RefCountDec (since
+  // this is the only reference and therefore no other thread is
+  // allowed to be mucking with r).
+  if (ref_.load(std::memory_order_acquire) == 1 ||
+      ref_.fetch_sub(1, std::memory_order_acq_rel) - 1 == 0) {
+    delete this;
+  }
+}
+
+static absl::optional<size_t> FindPayloadIndexByUrl(
+    const Payloads* payloads, absl::string_view type_url) {
+  if (payloads == nullptr) return absl::nullopt;
+
+  for (size_t i = 0; i < payloads->size(); ++i) {
+    if ((*payloads)[i].type_url == type_url) return i;
+  }
+
+  return absl::nullopt;
+}
+
+absl::optional<absl::Cord> StatusRep::GetPayload(
+    absl::string_view type_url) const {
+  absl::optional<size_t> index =
+      status_internal::FindPayloadIndexByUrl(payloads_.get(), type_url);
+  if (index.has_value()) return (*payloads_)[index.value()].payload;
+
+  return absl::nullopt;
+}
+
+void StatusRep::SetPayload(absl::string_view type_url, absl::Cord payload) {
+  if (payloads_ == nullptr) {
+    payloads_ = absl::make_unique<status_internal::Payloads>();
+  }
+
+  absl::optional<size_t> index =
+      status_internal::FindPayloadIndexByUrl(payloads_.get(), type_url);
+  if (index.has_value()) {
+    (*payloads_)[index.value()].payload = std::move(payload);
+    return;
+  }
+
+  payloads_->push_back({std::string(type_url), std::move(payload)});
+}
+
+StatusRep::EraseResult StatusRep::ErasePayload(absl::string_view type_url) {
+  absl::optional<size_t> index =
+      status_internal::FindPayloadIndexByUrl(payloads_.get(), type_url);
+  if (!index.has_value()) return {false, Status::PointerToRep(this)};
+  payloads_->erase(payloads_->begin() + index.value());
+  if (payloads_->empty() && message_.empty()) {
+    // Special case: If this can be represented inlined, it MUST be inlined
+    // (== depends on this behavior).
+    EraseResult result = {true, Status::CodeToInlinedRep(code_)};
+    Unref();
+    return result;
+  }
+  return {true, Status::PointerToRep(this)};
+}
+
+void StatusRep::ForEachPayload(
+    absl::FunctionRef<void(absl::string_view, const absl::Cord&)> visitor)
+    const {
+  if (auto* payloads = payloads_.get()) {
+    bool in_reverse =
+        payloads->size() > 1 && reinterpret_cast<uintptr_t>(payloads) % 13 > 6;
+
+    for (size_t index = 0; index < payloads->size(); ++index) {
+      const auto& elem =
+          (*payloads)[in_reverse ? payloads->size() - 1 - index : index];
+
+#ifdef NDEBUG
+      visitor(elem.type_url, elem.payload);
+#else
+      // In debug mode invalidate the type url to prevent users from relying on
+      // this string lifetime.
+
+      // NOLINTNEXTLINE intentional extra conversion to force temporary.
+      visitor(std::string(elem.type_url), elem.payload);
+#endif  // NDEBUG
+    }
+  }
+}
+
+std::string StatusRep::ToString(StatusToStringMode mode) const {
+  std::string text;
+  absl::StrAppend(&text, absl::StatusCodeToString(code()), ": ", message());
+
+  const bool with_payload = (mode & StatusToStringMode::kWithPayload) ==
+                            StatusToStringMode::kWithPayload;
+
+  if (with_payload) {
+    status_internal::StatusPayloadPrinter printer =
+        status_internal::GetStatusPayloadPrinter();
+    this->ForEachPayload([&](absl::string_view type_url,
+                             const absl::Cord& payload) {
+      absl::optional<std::string> result;
+      if (printer) result = printer(type_url, payload);
+      absl::StrAppend(
+          &text, " [", type_url, "='",
+          result.has_value() ? *result : absl::CHexEscape(std::string(payload)),
+          "']");
+    });
+  }
+
+  return text;
+}
+
+bool StatusRep::operator==(const StatusRep& other) const {
+  assert(this != &other);
+  if (code_ != other.code_) return false;
+  if (message_ != other.message_) return false;
+  const status_internal::Payloads* this_payloads = payloads_.get();
+  const status_internal::Payloads* other_payloads = other.payloads_.get();
+
+  const status_internal::Payloads no_payloads;
+  const status_internal::Payloads* larger_payloads =
+      this_payloads ? this_payloads : &no_payloads;
+  const status_internal::Payloads* smaller_payloads =
+      other_payloads ? other_payloads : &no_payloads;
+  if (larger_payloads->size() < smaller_payloads->size()) {
+    std::swap(larger_payloads, smaller_payloads);
+  }
+  if ((larger_payloads->size() - smaller_payloads->size()) > 1) return false;
+  // Payloads can be ordered differently, so we can't just compare payload
+  // vectors.
+  for (const auto& payload : *larger_payloads) {
+
+    bool found = false;
+    for (const auto& other_payload : *smaller_payloads) {
+      if (payload.type_url == other_payload.type_url) {
+        if (payload.payload != other_payload.payload) {
+          return false;
+        }
+        found = true;
+        break;
+      }
+    }
+    if (!found) return false;
+  }
+  return true;
+}
+
+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
+    // const_cast will never cast away const from a stack instance.
+    //
+    // CloneAndUnref is the only method that doesn't involve an external cast to
+    // get a mutable StatusRep* from the uintptr_t rep stored in Status.
+    return const_cast<StatusRep*>(this);
+  }
+  std::unique_ptr<status_internal::Payloads> payloads;
+  if (payloads_) {
+    payloads = absl::make_unique<status_internal::Payloads>(*payloads_);
+  }
+  auto* new_rep = new StatusRep(code_, message_, std::move(payloads));
+  Unref();
+  return new_rep;
+}
+
+// Convert canonical code to a value known to this binary.
+absl::StatusCode MapToLocalCode(int value) {
+  absl::StatusCode code = static_cast<absl::StatusCode>(value);
+  switch (code) {
+    case absl::StatusCode::kOk:
+    case absl::StatusCode::kCancelled:
+    case absl::StatusCode::kUnknown:
+    case absl::StatusCode::kInvalidArgument:
+    case absl::StatusCode::kDeadlineExceeded:
+    case absl::StatusCode::kNotFound:
+    case absl::StatusCode::kAlreadyExists:
+    case absl::StatusCode::kPermissionDenied:
+    case absl::StatusCode::kResourceExhausted:
+    case absl::StatusCode::kFailedPrecondition:
+    case absl::StatusCode::kAborted:
+    case absl::StatusCode::kOutOfRange:
+    case absl::StatusCode::kUnimplemented:
+    case absl::StatusCode::kInternal:
+    case absl::StatusCode::kUnavailable:
+    case absl::StatusCode::kDataLoss:
+    case absl::StatusCode::kUnauthenticated:
+      return code;
+    default:
+      return absl::StatusCode::kUnknown;
+  }
+}
+
+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), ")"));
+}
+
+}  // namespace status_internal
+
+ABSL_NAMESPACE_END
+}  // namespace absl
diff --git a/absl/status/internal/status_internal.h b/absl/status/internal/status_internal.h
index 6198e72..c9f4383 100644
--- a/absl/status/internal/status_internal.h
+++ b/absl/status/internal/status_internal.h
@@ -14,13 +14,19 @@
 #ifndef ABSL_STATUS_INTERNAL_STATUS_INTERNAL_H_
 #define ABSL_STATUS_INTERNAL_STATUS_INTERNAL_H_
 
+#include <atomic>
+#include <cstdint>
 #include <memory>
 #include <string>
 #include <utility>
 
 #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"
+#include "absl/types/optional.h"
 
 #ifndef SWIG
 // Disabled for SWIG as it doesn't parse attributes correctly.
@@ -32,9 +38,9 @@
 // TODO(b/176172494): ABSL_MUST_USE_RESULT should expand to the more strict
 // [[nodiscard]]. For now, just use [[nodiscard]] directly when it is available.
 #if ABSL_HAVE_CPP_ATTRIBUTE(nodiscard)
-class [[nodiscard]] Status;
+class [[nodiscard]] ABSL_ATTRIBUTE_TRIVIAL_ABI Status;
 #else
-class ABSL_MUST_USE_RESULT Status;
+class ABSL_MUST_USE_RESULT ABSL_ATTRIBUTE_TRIVIAL_ABI Status;
 #endif
 ABSL_NAMESPACE_END
 }  // namespace absl
@@ -44,6 +50,7 @@
 ABSL_NAMESPACE_BEGIN
 
 enum class StatusCode : int;
+enum class StatusToStringMode : int;
 
 namespace status_internal {
 
@@ -56,22 +63,54 @@
 using Payloads = absl::InlinedVector<Payload, 1>;
 
 // Reference-counted representation of Status data.
-struct StatusRep {
+class StatusRep {
+ public:
   StatusRep(absl::StatusCode code_arg, absl::string_view message_arg,
             std::unique_ptr<status_internal::Payloads> payloads_arg)
-      : ref(int32_t{1}),
-        code(code_arg),
-        message(message_arg),
-        payloads(std::move(payloads_arg)) {}
+      : ref_(int32_t{1}),
+        code_(code_arg),
+        message_(message_arg),
+        payloads_(std::move(payloads_arg)) {}
 
-  std::atomic<int32_t> ref;
-  absl::StatusCode code;
+  absl::StatusCode code() const { return code_; }
+  const std::string& message() const { return message_; }
+
+  // Ref and unref are const to allow access through a const pointer, and are
+  // used during copying operations.
+  void Ref() const { ref_.fetch_add(1, std::memory_order_relaxed); }
+  void Unref() const;
+
+  // Payload methods correspond to the same methods in absl::Status.
+  absl::optional<absl::Cord> GetPayload(absl::string_view type_url) const;
+  void SetPayload(absl::string_view type_url, absl::Cord payload);
+  struct EraseResult {
+    bool erased;
+    uintptr_t new_rep;
+  };
+  EraseResult ErasePayload(absl::string_view type_url);
+  void ForEachPayload(
+      absl::FunctionRef<void(absl::string_view, const absl::Cord&)> visitor)
+      const;
+
+  std::string ToString(StatusToStringMode mode) const;
+
+  bool operator==(const StatusRep& other) const;
+  bool operator!=(const StatusRep& other) const { return !(*this == other); }
+
+  // Returns an equivalent heap allocated StatusRep with refcount 1.
+  //
+  // `this` is not safe to be used after calling as it may have been deleted.
+  absl::Nonnull<StatusRep*> CloneAndUnref() const;
+
+ private:
+  mutable std::atomic<int32_t> ref_;
+  absl::StatusCode code_;
 
   // As an internal implementation detail, we guarantee that if status.message()
   // is non-empty, then the resulting string_view is null terminated.
   // This is required to implement 'StatusMessageAsCStr(...)'
-  std::string message;
-  std::unique_ptr<status_internal::Payloads> payloads;
+  std::string message_;
+  std::unique_ptr<status_internal::Payloads> payloads_;
 };
 
 absl::StatusCode MapToLocalCode(int value);
@@ -80,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..5be9490 100644
--- a/absl/status/internal/statusor_internal.h
+++ b/absl/status/internal/statusor_internal.h
@@ -14,12 +14,15 @@
 #ifndef ABSL_STATUS_INTERNAL_STATUSOR_INTERNAL_H_
 #define ABSL_STATUS_INTERNAL_STATUSOR_INTERNAL_H_
 
+#include <cstdint>
 #include <type_traits>
 #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/strings/string_view.h"
 #include "absl/utility/utility.h"
 
 namespace absl {
@@ -123,7 +126,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 +134,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)...);
 }
 
@@ -377,6 +381,53 @@
 
 ABSL_ATTRIBUTE_NORETURN void ThrowBadStatusOrAccess(absl::Status status);
 
+// Used to introduce jitter into the output of printing functions for
+// `StatusOr` (i.e. `AbslStringify` and `operator<<`).
+class StringifyRandom {
+  enum BracesType {
+    kBareParens = 0,
+    kSpaceParens,
+    kBareBrackets,
+    kSpaceBrackets,
+  };
+
+  // Returns a random `BracesType` determined once per binary load.
+  static BracesType RandomBraces() {
+    static const BracesType kRandomBraces = static_cast<BracesType>(
+        (reinterpret_cast<uintptr_t>(&kRandomBraces) >> 4) % 4);
+    return kRandomBraces;
+  }
+
+ public:
+  static inline absl::string_view OpenBrackets() {
+    switch (RandomBraces()) {
+      case kBareParens:
+        return "(";
+      case kSpaceParens:
+        return "( ";
+      case kBareBrackets:
+        return "[";
+      case kSpaceBrackets:
+        return "[ ";
+    }
+    return "(";
+  }
+
+  static inline absl::string_view CloseBrackets() {
+    switch (RandomBraces()) {
+      case kBareParens:
+        return ")";
+      case kSpaceParens:
+        return " )";
+      case kBareBrackets:
+        return "]";
+      case kSpaceBrackets:
+        return " ]";
+    }
+    return ")";
+  }
+};
+
 }  // namespace internal_statusor
 ABSL_NAMESPACE_END
 }  // namespace absl
diff --git a/absl/status/status.cc b/absl/status/status.cc
index 26e6829..4dd5ae0 100644
--- a/absl/status/status.cc
+++ b/absl/status/status.cc
@@ -15,23 +15,37 @@
 
 #include <errno.h>
 
-#include <cassert>
-#include <utility>
+#include <atomic>
+#include <cstddef>
+#include <cstdint>
+#include <cstring>
+#include <memory>
+#include <ostream>
+#include <string>
 
+#include "absl/base/attributes.h"
+#include "absl/base/config.h"
 #include "absl/base/internal/raw_logging.h"
 #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/status_payload_printer.h"
-#include "absl/strings/escaping.h"
+#include "absl/status/internal/status_internal.h"
 #include "absl/strings/str_cat.h"
 #include "absl/strings/str_format.h"
 #include "absl/strings/str_split.h"
+#include "absl/strings/string_view.h"
+#include "absl/types/optional.h"
 
 namespace absl {
 ABSL_NAMESPACE_BEGIN
 
+static_assert(
+    alignof(status_internal::StatusRep) >= 4,
+    "absl::Status assumes it can use the bottom 2 bits of a StatusRep*.");
+
 std::string StatusCodeToString(StatusCode code) {
   switch (code) {
     case StatusCode::kOk:
@@ -77,146 +91,18 @@
   return os << StatusCodeToString(code);
 }
 
-namespace status_internal {
-
-static absl::optional<size_t> FindPayloadIndexByUrl(
-    const Payloads* payloads, absl::string_view type_url) {
-  if (payloads == nullptr) return absl::nullopt;
-
-  for (size_t i = 0; i < payloads->size(); ++i) {
-    if ((*payloads)[i].type_url == type_url) return i;
-  }
-
-  return absl::nullopt;
-}
-
-// Convert canonical code to a value known to this binary.
-absl::StatusCode MapToLocalCode(int value) {
-  absl::StatusCode code = static_cast<absl::StatusCode>(value);
-  switch (code) {
-    case absl::StatusCode::kOk:
-    case absl::StatusCode::kCancelled:
-    case absl::StatusCode::kUnknown:
-    case absl::StatusCode::kInvalidArgument:
-    case absl::StatusCode::kDeadlineExceeded:
-    case absl::StatusCode::kNotFound:
-    case absl::StatusCode::kAlreadyExists:
-    case absl::StatusCode::kPermissionDenied:
-    case absl::StatusCode::kResourceExhausted:
-    case absl::StatusCode::kFailedPrecondition:
-    case absl::StatusCode::kAborted:
-    case absl::StatusCode::kOutOfRange:
-    case absl::StatusCode::kUnimplemented:
-    case absl::StatusCode::kInternal:
-    case absl::StatusCode::kUnavailable:
-    case absl::StatusCode::kDataLoss:
-    case absl::StatusCode::kUnauthenticated:
-      return code;
-    default:
-      return absl::StatusCode::kUnknown;
-  }
-}
-}  // namespace status_internal
-
-absl::optional<absl::Cord> Status::GetPayload(
-    absl::string_view type_url) const {
-  const auto* payloads = GetPayloads();
-  absl::optional<size_t> index =
-      status_internal::FindPayloadIndexByUrl(payloads, type_url);
-  if (index.has_value()) return (*payloads)[index.value()].payload;
-
-  return absl::nullopt;
-}
-
-void Status::SetPayload(absl::string_view type_url, absl::Cord payload) {
-  if (ok()) return;
-
-  PrepareToModify();
-
-  status_internal::StatusRep* rep = RepToPointer(rep_);
-  if (!rep->payloads) {
-    rep->payloads = absl::make_unique<status_internal::Payloads>();
-  }
-
-  absl::optional<size_t> index =
-      status_internal::FindPayloadIndexByUrl(rep->payloads.get(), type_url);
-  if (index.has_value()) {
-    (*rep->payloads)[index.value()].payload = std::move(payload);
-    return;
-  }
-
-  rep->payloads->push_back({std::string(type_url), std::move(payload)});
-}
-
-bool Status::ErasePayload(absl::string_view type_url) {
-  absl::optional<size_t> index =
-      status_internal::FindPayloadIndexByUrl(GetPayloads(), type_url);
-  if (index.has_value()) {
-    PrepareToModify();
-    GetPayloads()->erase(GetPayloads()->begin() + index.value());
-    if (GetPayloads()->empty() && message().empty()) {
-      // Special case: If this can be represented inlined, it MUST be
-      // inlined (EqualsSlow depends on this behavior).
-      StatusCode c = static_cast<StatusCode>(raw_code());
-      Unref(rep_);
-      rep_ = CodeToInlinedRep(c);
-    }
-    return true;
-  }
-
-  return false;
-}
-
-void Status::ForEachPayload(
-    absl::FunctionRef<void(absl::string_view, const absl::Cord&)> visitor)
-    const {
-  if (auto* payloads = GetPayloads()) {
-    bool in_reverse =
-        payloads->size() > 1 && reinterpret_cast<uintptr_t>(payloads) % 13 > 6;
-
-    for (size_t index = 0; index < payloads->size(); ++index) {
-      const auto& elem =
-          (*payloads)[in_reverse ? payloads->size() - 1 - index : index];
-
-#ifdef NDEBUG
-      visitor(elem.type_url, elem.payload);
-#else
-      // In debug mode invalidate the type url to prevent users from relying on
-      // this string lifetime.
-
-      // NOLINTNEXTLINE intentional extra conversion to force temporary.
-      visitor(std::string(elem.type_url), elem.payload);
-#endif  // NDEBUG
-    }
-  }
-}
-
-const std::string* Status::EmptyString() {
-  static union EmptyString {
-    std::string str;
-    ~EmptyString() {}
-  } empty = {{}};
-  return &empty.str;
+absl::Nonnull<const std::string*> Status::EmptyString() {
+  static const absl::NoDestructor<std::string> kEmpty;
+  return kEmpty.get();
 }
 
 #ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL
 constexpr const char Status::kMovedFromString[];
 #endif
 
-const std::string* Status::MovedFromString() {
-  static std::string* moved_from_string = new std::string(kMovedFromString);
-  return moved_from_string;
-}
-
-void Status::UnrefNonInlined(uintptr_t rep) {
-  status_internal::StatusRep* r = RepToPointer(rep);
-  // Fast path: if ref==1, there is no need for a RefCountDec (since
-  // this is the only reference and therefore no other thread is
-  // allowed to be mucking with r).
-  if (r->ref.load(std::memory_order_acquire) == 1 ||
-      r->ref.fetch_sub(1, std::memory_order_acq_rel) - 1 == 0) {
-    delete r;
-  }
+absl::Nonnull<const std::string*> Status::MovedFromString() {
+  static const absl::NoDestructor<std::string> kMovedFrom(kMovedFromString);
+  return kMovedFrom.get();
 }
 
 Status::Status(absl::StatusCode code, absl::string_view msg)
@@ -226,97 +112,20 @@
   }
 }
 
-int Status::raw_code() const {
-  if (IsInlined(rep_)) {
-    return static_cast<int>(InlinedRepToCode(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);
   }
-  status_internal::StatusRep* rep = RepToPointer(rep_);
-  return static_cast<int>(rep->code);
+  return RepToPointer(rep)->CloneAndUnref();
 }
 
-absl::StatusCode Status::code() const {
-  return status_internal::MapToLocalCode(raw_code());
-}
-
-void Status::PrepareToModify() {
-  ABSL_RAW_CHECK(!ok(), "PrepareToModify shouldn't be called on OK status.");
-  if (IsInlined(rep_)) {
-    rep_ = PointerToRep(new status_internal::StatusRep(
-        static_cast<absl::StatusCode>(raw_code()), absl::string_view(),
-        nullptr));
-    return;
+std::string Status::ToStringSlow(uintptr_t rep, StatusToStringMode mode) {
+  if (IsInlined(rep)) {
+    return absl::StrCat(absl::StatusCodeToString(InlinedRepToCode(rep)), ": ");
   }
-
-  uintptr_t rep_i = rep_;
-  status_internal::StatusRep* rep = RepToPointer(rep_);
-  if (rep->ref.load(std::memory_order_acquire) != 1) {
-    std::unique_ptr<status_internal::Payloads> payloads;
-    if (rep->payloads) {
-      payloads = absl::make_unique<status_internal::Payloads>(*rep->payloads);
-    }
-    status_internal::StatusRep* const new_rep = new status_internal::StatusRep(
-        rep->code, message(), std::move(payloads));
-    rep_ = PointerToRep(new_rep);
-    UnrefNonInlined(rep_i);
-  }
-}
-
-bool Status::EqualsSlow(const absl::Status& a, const absl::Status& b) {
-  if (IsInlined(a.rep_) != IsInlined(b.rep_)) return false;
-  if (a.message() != b.message()) return false;
-  if (a.raw_code() != b.raw_code()) return false;
-  if (a.GetPayloads() == b.GetPayloads()) return true;
-
-  const status_internal::Payloads no_payloads;
-  const status_internal::Payloads* larger_payloads =
-      a.GetPayloads() ? a.GetPayloads() : &no_payloads;
-  const status_internal::Payloads* smaller_payloads =
-      b.GetPayloads() ? b.GetPayloads() : &no_payloads;
-  if (larger_payloads->size() < smaller_payloads->size()) {
-    std::swap(larger_payloads, smaller_payloads);
-  }
-  if ((larger_payloads->size() - smaller_payloads->size()) > 1) return false;
-  // Payloads can be ordered differently, so we can't just compare payload
-  // vectors.
-  for (const auto& payload : *larger_payloads) {
-
-    bool found = false;
-    for (const auto& other_payload : *smaller_payloads) {
-      if (payload.type_url == other_payload.type_url) {
-        if (payload.payload != other_payload.payload) {
-          return false;
-        }
-        found = true;
-        break;
-      }
-    }
-    if (!found) return false;
-  }
-  return true;
-}
-
-std::string Status::ToStringSlow(StatusToStringMode mode) const {
-  std::string text;
-  absl::StrAppend(&text, absl::StatusCodeToString(code()), ": ", message());
-
-  const bool with_payload = (mode & StatusToStringMode::kWithPayload) ==
-                            StatusToStringMode::kWithPayload;
-
-  if (with_payload) {
-    status_internal::StatusPayloadPrinter printer =
-        status_internal::GetStatusPayloadPrinter();
-    this->ForEachPayload([&](absl::string_view type_url,
-                             const absl::Cord& payload) {
-      absl::optional<std::string> result;
-      if (printer) result = printer(type_url, payload);
-      absl::StrAppend(
-          &text, " [", type_url, "='",
-          result.has_value() ? *result : absl::CHexEscape(std::string(payload)),
-          "']");
-    });
-  }
-
-  return text;
+  return RepToPointer(rep)->ToString(mode);
 }
 
 std::ostream& operator<<(std::ostream& os, const Status& x) {
@@ -605,18 +414,7 @@
                 MessageForErrnoToStatus(error_number, message));
 }
 
-namespace status_internal {
-
-std::string* MakeCheckFailString(const absl::Status* status,
-                                 const char* prefix) {
-  return new std::string(
-      absl::StrCat(prefix, " (",
-                   status->ToString(StatusToStringMode::kWithEverything), ")"));
-}
-
-}  // namespace status_internal
-
-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 595064c..9ce16db 100644
--- a/absl/status/status.h
+++ b/absl/status/status.h
@@ -51,10 +51,17 @@
 #ifndef ABSL_STATUS_STATUS_H_
 #define ABSL_STATUS_STATUS_H_
 
+#include <cassert>
+#include <cstdint>
 #include <ostream>
 #include <string>
 #include <utility>
 
+#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"
 #include "absl/strings/cord.h"
@@ -421,7 +428,7 @@
 // Returned Status objects may not be ignored. status_internal.h has a forward
 // declaration of the form
 // class ABSL_MUST_USE_RESULT Status;
-class Status final {
+class ABSL_ATTRIBUTE_TRIVIAL_ABI Status final {
  public:
   // Constructors
 
@@ -516,6 +523,12 @@
   std::string ToString(
       StatusToStringMode mode = StatusToStringMode::kDefault) const;
 
+  // Support `absl::StrCat`, `absl::StrFormat`, etc.
+  template <typename Sink>
+  friend void AbslStringify(Sink& sink, const Status& status) {
+    sink.Append(status.ToString(StatusToStringMode::kWithEverything));
+  }
+
   // Status::IgnoreError()
   //
   // Ignores any errors. This method does nothing except potentially suppress
@@ -602,56 +615,57 @@
   // code, and an empty error message.
   explicit Status(absl::StatusCode code);
 
-  static void UnrefNonInlined(uintptr_t rep);
+  // Underlying constructor for status from a rep_.
+  explicit Status(uintptr_t rep) : rep_(rep) {}
+
   static void Ref(uintptr_t rep);
   static void Unref(uintptr_t rep);
 
   // REQUIRES: !ok()
-  // Ensures rep_ is not shared with any other Status.
-  void PrepareToModify();
-
-  const status_internal::Payloads* GetPayloads() const;
-  status_internal::Payloads* GetPayloads();
-
-  static bool EqualsSlow(const absl::Status& a, const absl::Status& b);
+  // Ensures rep is not inlined or shared with any other Status.
+  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.
-  static bool IsInlined(uintptr_t rep);
+  static constexpr bool IsInlined(uintptr_t rep);
 
   // Indicates whether this Status was the rhs of a move operation. See rep_
   // for details.
-  static bool IsMovedFrom(uintptr_t rep);
-  static uintptr_t MovedFromRep();
+  static constexpr bool IsMovedFrom(uintptr_t rep);
+  static constexpr uintptr_t MovedFromRep();
 
   // Convert between error::Code and the inlined uintptr_t representation used
   // by rep_. See rep_ for details.
-  static uintptr_t CodeToInlinedRep(absl::StatusCode code);
-  static absl::StatusCode InlinedRepToCode(uintptr_t rep);
+  static constexpr uintptr_t CodeToInlinedRep(absl::StatusCode code);
+  static constexpr absl::StatusCode InlinedRepToCode(uintptr_t rep);
 
   // 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 status_internal::StatusRep* RepToPointer(uintptr_t r);
+  static absl::Nonnull<const status_internal::StatusRep*> RepToPointer(
+      uintptr_t r);
 
-  std::string ToStringSlow(StatusToStringMode mode) const;
+  static std::string ToStringSlow(uintptr_t rep, StatusToStringMode mode);
 
   // Status supports two different representations.
-  //  - When the low bit is off it is an inlined representation.
+  //  - When the low bit is set it is an inlined representation.
   //    It uses the canonical error space, no message or payload.
   //    The error code is (rep_ >> 2).
   //    The (rep_ & 2) bit is the "moved from" indicator, used in IsMovedFrom().
-  //  - When the low bit is on it is an external representation.
+  //  - When the low bit is off it is an external representation.
   //    In this case all the data comes from a heap allocated Rep object.
-  //    (rep_ - 1) is a status_internal::StatusRep* pointer to that structure.
+  //    rep_ is a status_internal::StatusRep* pointer to that structure.
   uintptr_t rep_;
+
+  friend class status_internal::StatusRep;
 };
 
 // OkStatus()
@@ -755,11 +769,11 @@
 // Implementation details follow
 //------------------------------------------------------------------------------
 
-inline Status::Status() : rep_(CodeToInlinedRep(absl::StatusCode::kOk)) {}
+inline Status::Status() : Status(absl::StatusCode::kOk) {}
 
-inline Status::Status(absl::StatusCode code) : rep_(CodeToInlinedRep(code)) {}
+inline Status::Status(absl::StatusCode code) : Status(CodeToInlinedRep(code)) {}
 
-inline Status::Status(const Status& x) : rep_(x.rep_) { Ref(rep_); }
+inline Status::Status(const Status& x) : Status(x.rep_) { Ref(rep_); }
 
 inline Status& Status::operator=(const Status& x) {
   uintptr_t old_rep = rep_;
@@ -771,7 +785,7 @@
   return *this;
 }
 
-inline Status::Status(Status&& x) noexcept : rep_(x.rep_) {
+inline Status::Status(Status&& x) noexcept : Status(x.rep_) {
   x.rep_ = MovedFromRep();
 }
 
@@ -803,15 +817,27 @@
   return rep_ == CodeToInlinedRep(absl::StatusCode::kOk);
 }
 
+inline absl::StatusCode Status::code() const {
+  return status_internal::MapToLocalCode(raw_code());
+}
+
+inline int Status::raw_code() const {
+  if (IsInlined(rep_)) return static_cast<int>(InlinedRepToCode(rep_));
+  return static_cast<int>(RepToPointer(rep_)->code());
+}
+
 inline absl::string_view Status::message() const {
   return !IsInlined(rep_)
-             ? RepToPointer(rep_)->message
+             ? RepToPointer(rep_)->message()
              : (IsMovedFrom(rep_) ? absl::string_view(kMovedFromString)
                                   : absl::string_view());
 }
 
 inline bool operator==(const Status& lhs, const Status& rhs) {
-  return lhs.rep_ == rhs.rep_ || Status::EqualsSlow(lhs, rhs);
+  if (lhs.rep_ == rhs.rep_) return true;
+  if (Status::IsInlined(lhs.rep_)) return false;
+  if (Status::IsInlined(rhs.rep_)) return false;
+  return *Status::RepToPointer(lhs.rep_) == *Status::RepToPointer(rhs.rep_);
 }
 
 inline bool operator!=(const Status& lhs, const Status& rhs) {
@@ -819,7 +845,7 @@
 }
 
 inline std::string Status::ToString(StatusToStringMode mode) const {
-  return ok() ? "OK" : ToStringSlow(mode);
+  return ok() ? "OK" : ToStringSlow(rep_, mode);
 }
 
 inline void Status::IgnoreError() const {
@@ -831,52 +857,68 @@
   swap(a.rep_, b.rep_);
 }
 
-inline const status_internal::Payloads* Status::GetPayloads() const {
-  return IsInlined(rep_) ? nullptr : RepToPointer(rep_)->payloads.get();
+inline absl::optional<absl::Cord> Status::GetPayload(
+    absl::string_view type_url) const {
+  if (IsInlined(rep_)) return absl::nullopt;
+  return RepToPointer(rep_)->GetPayload(type_url);
 }
 
-inline status_internal::Payloads* Status::GetPayloads() {
-  return IsInlined(rep_) ? nullptr : RepToPointer(rep_)->payloads.get();
+inline void Status::SetPayload(absl::string_view type_url, absl::Cord payload) {
+  if (ok()) return;
+  status_internal::StatusRep* rep = PrepareToModify(rep_);
+  rep->SetPayload(type_url, std::move(payload));
+  rep_ = PointerToRep(rep);
 }
 
-inline bool Status::IsInlined(uintptr_t rep) { return (rep & 1) == 0; }
-
-inline bool Status::IsMovedFrom(uintptr_t rep) {
-  return IsInlined(rep) && (rep & 2) != 0;
+inline bool Status::ErasePayload(absl::string_view type_url) {
+  if (IsInlined(rep_)) return false;
+  status_internal::StatusRep* rep = PrepareToModify(rep_);
+  auto res = rep->ErasePayload(type_url);
+  rep_ = res.new_rep;
+  return res.erased;
 }
 
-inline uintptr_t Status::MovedFromRep() {
-  return CodeToInlinedRep(absl::StatusCode::kInternal) | 2;
+inline void Status::ForEachPayload(
+    absl::FunctionRef<void(absl::string_view, const absl::Cord&)> visitor)
+    const {
+  if (IsInlined(rep_)) return;
+  RepToPointer(rep_)->ForEachPayload(visitor);
 }
 
-inline uintptr_t Status::CodeToInlinedRep(absl::StatusCode code) {
-  return static_cast<uintptr_t>(code) << 2;
+constexpr bool Status::IsInlined(uintptr_t rep) { return (rep & 1) != 0; }
+
+constexpr bool Status::IsMovedFrom(uintptr_t rep) { return (rep & 2) != 0; }
+
+constexpr uintptr_t Status::CodeToInlinedRep(absl::StatusCode code) {
+  return (static_cast<uintptr_t>(code) << 2) + 1;
 }
 
-inline absl::StatusCode Status::InlinedRepToCode(uintptr_t rep) {
-  assert(IsInlined(rep));
+constexpr absl::StatusCode Status::InlinedRepToCode(uintptr_t rep) {
+  ABSL_ASSERT(IsInlined(rep));
   return static_cast<absl::StatusCode>(rep >> 2);
 }
 
-inline status_internal::StatusRep* Status::RepToPointer(uintptr_t rep) {
-  assert(!IsInlined(rep));
-  return reinterpret_cast<status_internal::StatusRep*>(rep - 1);
+constexpr uintptr_t Status::MovedFromRep() {
+  return CodeToInlinedRep(absl::StatusCode::kInternal) | 2;
 }
 
-inline uintptr_t Status::PointerToRep(status_internal::StatusRep* rep) {
-  return reinterpret_cast<uintptr_t>(rep) + 1;
+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(
+    absl::Nonnull<status_internal::StatusRep*> rep) {
+  return reinterpret_cast<uintptr_t>(rep);
 }
 
 inline void Status::Ref(uintptr_t rep) {
-  if (!IsInlined(rep)) {
-    RepToPointer(rep)->ref.fetch_add(1, std::memory_order_relaxed);
-  }
+  if (!IsInlined(rep)) RepToPointer(rep)->Ref();
 }
 
 inline void Status::Unref(uintptr_t rep) {
-  if (!IsInlined(rep)) {
-    UnrefNonInlined(rep);
-  }
+  if (!IsInlined(rep)) RepToPointer(rep)->Unref();
 }
 
 inline Status OkStatus() { return Status(); }
@@ -892,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.cc b/absl/status/status_payload_printer.cc
index a47aea1..98401e9 100644
--- a/absl/status/status_payload_printer.cc
+++ b/absl/status/status_payload_printer.cc
@@ -13,9 +13,7 @@
 // limitations under the License.
 #include "absl/status/status_payload_printer.h"
 
-#include <atomic>
-
-#include "absl/base/attributes.h"
+#include "absl/base/config.h"
 #include "absl/base/internal/atomic_hook.h"
 
 namespace absl {
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/status_test.cc b/absl/status/status_test.cc
index 898a9cb..585e780 100644
--- a/absl/status/status_test.cc
+++ b/absl/status/status_test.cc
@@ -16,9 +16,16 @@
 
 #include <errno.h>
 
+#include <array>
+#include <cstddef>
+#include <sstream>
+#include <utility>
+
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
+#include "absl/strings/cord.h"
 #include "absl/strings/str_cat.h"
+#include "absl/strings/str_format.h"
 
 namespace {
 
@@ -299,37 +306,77 @@
 }
 
 TEST(Status, ToString) {
-  absl::Status s(absl::StatusCode::kInternal, "fail");
-  EXPECT_EQ("INTERNAL: fail", s.ToString());
-  s.SetPayload("foo", absl::Cord("bar"));
-  EXPECT_EQ("INTERNAL: fail [foo='bar']", s.ToString());
-  s.SetPayload("bar", absl::Cord("\377"));
-  EXPECT_THAT(s.ToString(),
+  absl::Status status(absl::StatusCode::kInternal, "fail");
+  EXPECT_EQ("INTERNAL: fail", status.ToString());
+  status.SetPayload("foo", absl::Cord("bar"));
+  EXPECT_EQ("INTERNAL: fail [foo='bar']", status.ToString());
+  status.SetPayload("bar", absl::Cord("\377"));
+  EXPECT_THAT(status.ToString(),
               AllOf(HasSubstr("INTERNAL: fail"), HasSubstr("[foo='bar']"),
                     HasSubstr("[bar='\\xff']")));
 }
 
 TEST(Status, ToStringMode) {
-  absl::Status s(absl::StatusCode::kInternal, "fail");
-  s.SetPayload("foo", absl::Cord("bar"));
-  s.SetPayload("bar", absl::Cord("\377"));
+  absl::Status status(absl::StatusCode::kInternal, "fail");
+  status.SetPayload("foo", absl::Cord("bar"));
+  status.SetPayload("bar", absl::Cord("\377"));
 
   EXPECT_EQ("INTERNAL: fail",
-            s.ToString(absl::StatusToStringMode::kWithNoExtraData));
+            status.ToString(absl::StatusToStringMode::kWithNoExtraData));
 
-  EXPECT_THAT(s.ToString(absl::StatusToStringMode::kWithPayload),
+  EXPECT_THAT(status.ToString(absl::StatusToStringMode::kWithPayload),
               AllOf(HasSubstr("INTERNAL: fail"), HasSubstr("[foo='bar']"),
                     HasSubstr("[bar='\\xff']")));
 
-  EXPECT_THAT(s.ToString(absl::StatusToStringMode::kWithEverything),
+  EXPECT_THAT(status.ToString(absl::StatusToStringMode::kWithEverything),
               AllOf(HasSubstr("INTERNAL: fail"), HasSubstr("[foo='bar']"),
                     HasSubstr("[bar='\\xff']")));
 
-  EXPECT_THAT(s.ToString(~absl::StatusToStringMode::kWithPayload),
+  EXPECT_THAT(status.ToString(~absl::StatusToStringMode::kWithPayload),
               AllOf(HasSubstr("INTERNAL: fail"), Not(HasSubstr("[foo='bar']")),
                     Not(HasSubstr("[bar='\\xff']"))));
 }
 
+TEST(Status, OstreamOperator) {
+  absl::Status status(absl::StatusCode::kInternal, "fail");
+  { std::stringstream stream;
+    stream << status;
+    EXPECT_EQ("INTERNAL: fail", stream.str());
+  }
+  status.SetPayload("foo", absl::Cord("bar"));
+  { std::stringstream stream;
+    stream << status;
+    EXPECT_EQ("INTERNAL: fail [foo='bar']", stream.str());
+  }
+  status.SetPayload("bar", absl::Cord("\377"));
+  { std::stringstream stream;
+    stream << status;
+    EXPECT_THAT(stream.str(),
+                AllOf(HasSubstr("INTERNAL: fail"), HasSubstr("[foo='bar']"),
+                      HasSubstr("[bar='\\xff']")));
+  }
+}
+
+TEST(Status, AbslStringify) {
+  absl::Status status(absl::StatusCode::kInternal, "fail");
+  EXPECT_EQ("INTERNAL: fail", absl::StrCat(status));
+  EXPECT_EQ("INTERNAL: fail", absl::StrFormat("%v", status));
+  status.SetPayload("foo", absl::Cord("bar"));
+  EXPECT_EQ("INTERNAL: fail [foo='bar']", absl::StrCat(status));
+  status.SetPayload("bar", absl::Cord("\377"));
+  EXPECT_THAT(absl::StrCat(status),
+              AllOf(HasSubstr("INTERNAL: fail"), HasSubstr("[foo='bar']"),
+                    HasSubstr("[bar='\\xff']")));
+}
+
+TEST(Status, OstreamEqStringify) {
+  absl::Status status(absl::StatusCode::kUnknown, "fail");
+  status.SetPayload("foo", absl::Cord("bar"));
+  std::stringstream stream;
+  stream << status;
+  EXPECT_EQ(stream.str(), absl::StrCat(status));
+}
+
 absl::Status EraseAndReturn(const absl::Status& base) {
   absl::Status copy = base;
   EXPECT_TRUE(copy.ErasePayload(kUrl1));
diff --git a/absl/status/statusor.cc b/absl/status/statusor.cc
index 96642b3..7e6b334 100644
--- a/absl/status/statusor.cc
+++ b/absl/status/statusor.cc
@@ -17,7 +17,10 @@
 #include <utility>
 
 #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"
 
@@ -52,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();
 }
@@ -67,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..cd35e5b 100644
--- a/absl/status/statusor.h
+++ b/absl/status/statusor.h
@@ -39,15 +39,20 @@
 #include <exception>
 #include <initializer_list>
 #include <new>
+#include <ostream>
 #include <string>
 #include <type_traits>
 #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"
 #include "absl/status/status.h"
+#include "absl/strings/has_absl_stringify.h"
+#include "absl/strings/has_ostream_operator.h"
+#include "absl/strings/str_format.h"
 #include "absl/types/variant.h"
 #include "absl/utility/utility.h"
 
@@ -88,7 +93,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()
   //
@@ -650,6 +655,41 @@
   return !(lhs == rhs);
 }
 
+// Prints the `value` or the status in brackets to `os`.
+//
+// Requires `T` supports `operator<<`.  Do not rely on the output format which
+// may change without notice.
+template <typename T, typename std::enable_if<
+                          absl::HasOstreamOperator<T>::value, int>::type = 0>
+std::ostream& operator<<(std::ostream& os, const StatusOr<T>& status_or) {
+  if (status_or.ok()) {
+    os << status_or.value();
+  } else {
+    os << internal_statusor::StringifyRandom::OpenBrackets()
+       << status_or.status()
+       << internal_statusor::StringifyRandom::CloseBrackets();
+  }
+  return os;
+}
+
+// As above, but supports `StrCat`, `StrFormat`, etc.
+//
+// Requires `T` has `AbslStringify`.  Do not rely on the output format which
+// may change without notice.
+template <
+    typename Sink, typename T,
+    typename std::enable_if<absl::HasAbslStringify<T>::value, int>::type = 0>
+void AbslStringify(Sink& sink, const StatusOr<T>& status_or) {
+  if (status_or.ok()) {
+    absl::Format(&sink, "%v", status_or.value());
+  } else {
+    absl::Format(&sink, "%s%v%s",
+                 internal_statusor::StringifyRandom::OpenBrackets(),
+                 status_or.status(),
+                 internal_statusor::StringifyRandom::CloseBrackets());
+  }
+}
+
 //------------------------------------------------------------------------------
 // Implementation details for StatusOr<T>
 //------------------------------------------------------------------------------
@@ -750,13 +790,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/status/statusor_test.cc b/absl/status/statusor_test.cc
index e65f5d2..09ffc65 100644
--- a/absl/status/statusor_test.cc
+++ b/absl/status/statusor_test.cc
@@ -15,31 +15,41 @@
 #include "absl/status/statusor.h"
 
 #include <array>
+#include <cstddef>
 #include <initializer_list>
+#include <map>
 #include <memory>
+#include <ostream>
+#include <sstream>
 #include <string>
 #include <type_traits>
 #include <utility>
+#include <vector>
 
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
 #include "absl/base/casts.h"
 #include "absl/memory/memory.h"
 #include "absl/status/status.h"
+#include "absl/strings/str_cat.h"
 #include "absl/strings/string_view.h"
 #include "absl/types/any.h"
+#include "absl/types/variant.h"
 #include "absl/utility/utility.h"
 
 namespace {
 
 using ::testing::AllOf;
+using ::testing::AnyOf;
 using ::testing::AnyWith;
 using ::testing::ElementsAre;
+using ::testing::EndsWith;
 using ::testing::Field;
 using ::testing::HasSubstr;
 using ::testing::Ne;
 using ::testing::Not;
 using ::testing::Pointee;
+using ::testing::StartsWith;
 using ::testing::VariantWith;
 
 #ifdef GTEST_HAS_STATUS_MATCHERS
@@ -1877,4 +1887,35 @@
   EXPECT_EQ(statusor.status(), static_cast<absl::Status>(v));
 }
 
+struct PrintTestStruct {
+  friend std::ostream& operator<<(std::ostream& os, const PrintTestStruct&) {
+    return os << "ostream";
+  }
+
+  template <typename Sink>
+  friend void AbslStringify(Sink& sink, const PrintTestStruct&) {
+    sink.Append("stringify");
+  }
+};
+
+TEST(StatusOr, OkPrinting) {
+  absl::StatusOr<PrintTestStruct> print_me = PrintTestStruct{};
+  std::stringstream stream;
+  stream << print_me;
+  EXPECT_EQ(stream.str(), "ostream");
+  EXPECT_EQ(absl::StrCat(print_me), "stringify");
+}
+
+TEST(StatusOr, ErrorPrinting) {
+  absl::StatusOr<PrintTestStruct> print_me = absl::UnknownError("error");
+  std::stringstream stream;
+  stream << print_me;
+  const auto error_matcher =
+      AllOf(HasSubstr("UNKNOWN"), HasSubstr("error"),
+            AnyOf(AllOf(StartsWith("("), EndsWith(")")),
+                  AllOf(StartsWith("["), EndsWith("]"))));
+  EXPECT_THAT(stream.str(), error_matcher);
+  EXPECT_THAT(absl::StrCat(print_me), error_matcher);
+}
+
 }  // namespace
diff --git a/absl/strings/BUILD.bazel b/absl/strings/BUILD.bazel
index 819bbe6..8e8371b 100644
--- a/absl/strings/BUILD.bazel
+++ b/absl/strings/BUILD.bazel
@@ -22,7 +22,11 @@
 
 package(
     default_visibility = ["//visibility:public"],
-    features = ["parse_headers"],
+    features = [
+        "header_modules",
+        "layering_check",
+        "parse_headers",
+    ],
 )
 
 licenses(["notice"])
@@ -37,6 +41,7 @@
         "//absl/base",
         "//absl/base:config",
         "//absl/base:core_headers",
+        "//absl/base:nullability",
         "//absl/base:throw_delegate",
     ],
 )
@@ -70,6 +75,7 @@
         "ascii.h",
         "charconv.h",
         "escaping.h",
+        "has_absl_stringify.h",
         "internal/damerau_levenshtein_distance.h",
         "internal/has_absl_stringify.h",
         "internal/string_constant.h",
@@ -92,12 +98,14 @@
         "string_view.h",
     ],
     deps = [
+        ":charset",
         ":internal",
         ":string_view",
         "//absl/base",
         "//absl/base:config",
         "//absl/base:core_headers",
         "//absl/base:endian",
+        "//absl/base:nullability",
         "//absl/base:raw_logging_internal",
         "//absl/base:throw_delegate",
         "//absl/memory",
@@ -115,7 +123,6 @@
         "internal/utf8.cc",
     ],
     hdrs = [
-        "internal/char_map.h",
         "internal/escaping.h",
         "internal/ostringstream.h",
         "internal/resize_uninitialized.h",
@@ -140,6 +147,7 @@
     visibility = ["//visibility:private"],
     deps = [
         ":strings",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -158,6 +166,8 @@
         ":strings",
         "//absl/base:core_headers",
         "//absl/container:fixed_array",
+        "//absl/log:check",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -175,6 +185,45 @@
         ":strings",
         "//absl/base:raw_logging_internal",
         "@com_github_google_benchmark//:benchmark_main",
+        "@com_google_googletest//:gtest",
+    ],
+)
+
+cc_test(
+    name = "has_absl_stringify_test",
+    size = "small",
+    srcs = ["has_absl_stringify_test.cc"],
+    copts = ABSL_TEST_COPTS,
+    visibility = ["//visibility:private"],
+    deps = [
+        ":strings",
+        "//absl/types:optional",
+        "@com_google_googletest//:gtest",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
+
+cc_library(
+    name = "has_ostream_operator",
+    hdrs = ["has_ostream_operator.h"],
+    copts = ABSL_DEFAULT_COPTS,
+    linkopts = ABSL_DEFAULT_LINKOPTS,
+    deps = [
+        "//absl/base:config",
+    ],
+)
+
+cc_test(
+    name = "has_ostream_operator_test",
+    size = "small",
+    srcs = ["has_ostream_operator_test.cc"],
+    copts = ABSL_TEST_COPTS,
+    visibility = ["//visibility:private"],
+    deps = [
+        ":has_ostream_operator",
+        "//absl/types:optional",
+        "@com_google_googletest//:gtest",
+        "@com_google_googletest//:gtest_main",
     ],
 )
 
@@ -187,6 +236,7 @@
     deps = [
         ":strings",
         "//absl/base:core_headers",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -200,6 +250,7 @@
     deps = [
         ":strings",
         "@com_github_google_benchmark//:benchmark_main",
+        "@com_google_googletest//:gtest",
     ],
 )
 
@@ -212,6 +263,7 @@
     copts = ABSL_TEST_COPTS,
     deps = [
         "//absl/strings",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -229,6 +281,7 @@
         ":strings",
         "//absl/base:core_headers",
         "@com_github_google_benchmark//:benchmark_main",
+        "@com_google_googletest//:gtest",
     ],
 )
 
@@ -244,6 +297,7 @@
     deps = [
         ":strings",
         "//absl/base:core_headers",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -259,6 +313,7 @@
     deps = [
         ":internal",
         "//absl/base:core_headers",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -272,6 +327,7 @@
     deps = [
         ":strings",
         "//absl/meta:type_traits",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -288,6 +344,7 @@
         "//absl/base:core_headers",
         "//absl/base:raw_logging_internal",
         "@com_github_google_benchmark//:benchmark_main",
+        "@com_google_googletest//:gtest",
     ],
 )
 
@@ -302,6 +359,53 @@
         "//absl/base:config",
         "//absl/base:core_headers",
         "//absl/base:dynamic_annotations",
+        "@com_google_googletest//:gtest",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
+
+cc_test(
+    name = "charset_benchmark",
+    size = "small",
+    srcs = [
+        "charset_benchmark.cc",
+    ],
+    copts = ABSL_TEST_COPTS,
+    tags = [
+        "benchmark",
+    ],
+    visibility = ["//visibility:private"],
+    deps = [
+        ":charset",
+        "//absl/log:check",
+        "@com_github_google_benchmark//:benchmark_main",
+        "@com_google_googletest//:gtest",
+    ],
+)
+
+cc_library(
+    name = "charset",
+    hdrs = [
+        "charset.h",
+    ],
+    copts = ABSL_DEFAULT_COPTS,
+    linkopts = ABSL_DEFAULT_LINKOPTS,
+    deps = [
+        ":string_view",
+        "//absl/base:core_headers",
+    ],
+)
+
+cc_test(
+    name = "charset_test",
+    size = "small",
+    srcs = ["charset_test.cc"],
+    copts = ABSL_TEST_COPTS,
+    visibility = ["//visibility:private"],
+    deps = [
+        ":charset",
+        ":strings",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -315,7 +419,6 @@
         "internal/cord_rep_btree_reader.cc",
         "internal/cord_rep_consume.cc",
         "internal/cord_rep_crc.cc",
-        "internal/cord_rep_ring.cc",
     ],
     hdrs = [
         "internal/cord_data_edge.h",
@@ -326,8 +429,6 @@
         "internal/cord_rep_consume.h",
         "internal/cord_rep_crc.h",
         "internal/cord_rep_flat.h",
-        "internal/cord_rep_ring.h",
-        "internal/cord_rep_ring_reader.h",
     ],
     copts = ABSL_DEFAULT_COPTS,
     linkopts = ABSL_DEFAULT_LINKOPTS,
@@ -364,6 +465,7 @@
         ":cord_rep_test_util",
         ":strings",
         "//absl/base:config",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -382,6 +484,7 @@
         "//absl/base:config",
         "//absl/base:raw_logging_internal",
         "//absl/cleanup",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -398,6 +501,7 @@
         ":strings",
         "//absl/base:config",
         "//absl/base:raw_logging_internal",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -415,6 +519,7 @@
         ":strings",
         "//absl/base:config",
         "//absl/base:raw_logging_internal",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -430,6 +535,7 @@
         ":cord_rep_test_util",
         "//absl/base:config",
         "//absl/crc:crc_cord_state",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -453,6 +559,7 @@
         "//absl/base:config",
         "//absl/base:core_headers",
         "//absl/synchronization",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -484,9 +591,10 @@
         "//absl/base:config",
         "//absl/base:core_headers",
         "//absl/base:endian",
+        "//absl/base:nullability",
         "//absl/base:raw_logging_internal",
-        "//absl/container:fixed_array",
         "//absl/container:inlined_vector",
+        "//absl/crc:crc32c",
         "//absl/crc:crc_cord_state",
         "//absl/functional:function_ref",
         "//absl/meta:type_traits",
@@ -568,6 +676,7 @@
         ":cordz_update_scope",
         ":cordz_update_tracker",
         "//absl/base:config",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -628,6 +737,7 @@
         ":cordz_functions",
         ":cordz_test_helpers",
         "//absl/base:config",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -646,6 +756,7 @@
         "//absl/synchronization",
         "//absl/synchronization:thread_pool",
         "//absl/time",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -667,6 +778,7 @@
         "//absl/debugging:stacktrace",
         "//absl/debugging:symbolize",
         "//absl/types:span",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -688,6 +800,7 @@
         "//absl/crc:crc_cord_state",
         "//absl/synchronization",
         "//absl/synchronization:thread_pool",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -709,6 +822,7 @@
         "//absl/synchronization",
         "//absl/synchronization:thread_pool",
         "//absl/time",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -759,6 +873,7 @@
         ":strings",
         "//absl/base:config",
         "//absl/base:core_headers",
+        "//absl/base:nullability",
         "@com_google_googletest//:gtest",
     ],
 )
@@ -773,8 +888,10 @@
         ":cord",
         ":cord_internal",
         ":cord_rep_test_util",
+        ":string_view",
         "//absl/base:config",
         "//absl/types:span",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -787,19 +904,25 @@
     visibility = ["//visibility:private"],
     deps = [
         ":cord",
+        ":cord_internal",
         ":cord_test_helpers",
         ":cordz_functions",
+        ":cordz_statistics",
         ":cordz_test_helpers",
+        ":cordz_update_tracker",
         ":str_format",
         ":strings",
         "//absl/base:config",
         "//absl/base:core_headers",
         "//absl/base:endian",
         "//absl/container:fixed_array",
+        "//absl/functional:function_ref",
         "//absl/hash",
         "//absl/log",
         "//absl/log:check",
         "//absl/random",
+        "//absl/types:optional",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -821,6 +944,7 @@
     visibility = ["//visibility:private"],
     deps = [
         ":cord",
+        ":cord_internal",
         ":cord_test_helpers",
         ":cordz_functions",
         ":cordz_info",
@@ -832,38 +956,7 @@
         "//absl/base:config",
         "//absl/base:core_headers",
         "//absl/base:raw_logging_internal",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "cord_ring_test",
-    size = "medium",
-    srcs = ["cord_ring_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    visibility = ["//visibility:private"],
-    deps = [
-        ":cord_internal",
-        ":strings",
-        "//absl/base:config",
-        "//absl/base:core_headers",
-        "//absl/base:raw_logging_internal",
-        "//absl/debugging:leak_check",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "cord_ring_reader_test",
-    size = "medium",
-    srcs = ["cord_ring_reader_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    visibility = ["//visibility:private"],
-    deps = [
-        ":cord_internal",
-        ":strings",
-        "//absl/base:core_headers",
-        "//absl/debugging:leak_check",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -877,6 +970,7 @@
     deps = [
         ":strings",
         "//absl/base:core_headers",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -891,6 +985,7 @@
         ":strings",
         "//absl/base:raw_logging_internal",
         "@com_github_google_benchmark//:benchmark_main",
+        "@com_google_googletest//:gtest",
     ],
 )
 
@@ -902,6 +997,7 @@
     visibility = ["//visibility:private"],
     deps = [
         ":strings",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -918,6 +1014,7 @@
         "//absl/container:btree",
         "//absl/container:flat_hash_map",
         "//absl/container:node_hash_map",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -932,6 +1029,7 @@
         ":strings",
         "//absl/base:raw_logging_internal",
         "@com_github_google_benchmark//:benchmark_main",
+        "@com_google_googletest//:gtest",
     ],
 )
 
@@ -943,6 +1041,7 @@
     visibility = ["//visibility:private"],
     deps = [
         ":internal",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -956,6 +1055,7 @@
     deps = [
         ":internal",
         "@com_github_google_benchmark//:benchmark_main",
+        "@com_google_googletest//:gtest",
     ],
 )
 
@@ -971,6 +1071,7 @@
     deps = [
         "//absl/base:core_headers",
         "//absl/meta:type_traits",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -985,6 +1086,7 @@
         ":strings",
         "//absl/base:core_headers",
         "//absl/memory",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -998,6 +1100,7 @@
     deps = [
         ":strings",
         "@com_github_google_benchmark//:benchmark_main",
+        "@com_google_googletest//:gtest",
     ],
 )
 
@@ -1010,6 +1113,7 @@
     deps = [
         ":str_format",
         ":strings",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -1022,7 +1126,10 @@
     visibility = ["//visibility:private"],
     deps = [
         ":strings",
+        "//absl/random",
+        "//absl/random:distributions",
         "@com_github_google_benchmark//:benchmark_main",
+        "@com_google_googletest//:gtest",
     ],
 )
 
@@ -1041,8 +1148,10 @@
         ":strings",
         "//absl/base:config",
         "//absl/log",
+        "//absl/numeric:int128",
         "//absl/random",
         "//absl/random:distributions",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -1059,6 +1168,7 @@
         "//absl/random",
         "//absl/random:distributions",
         "@com_github_google_benchmark//:benchmark_main",
+        "@com_google_googletest//:gtest",
     ],
 )
 
@@ -1070,32 +1180,12 @@
     visibility = ["//visibility:private"],
     deps = [
         ":strings",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
 
 cc_test(
-    name = "char_map_test",
-    srcs = ["internal/char_map_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    deps = [
-        ":internal",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "char_map_benchmark",
-    srcs = ["internal/char_map_benchmark.cc"],
-    copts = ABSL_TEST_COPTS,
-    tags = ["benchmark"],
-    deps = [
-        ":internal",
-        "@com_github_google_benchmark//:benchmark_main",
-    ],
-)
-
-cc_test(
     name = "charconv_test",
     srcs = ["charconv_test.cc"],
     copts = ABSL_TEST_COPTS,
@@ -1103,6 +1193,7 @@
         ":pow10_helper",
         ":str_format",
         ":strings",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -1118,6 +1209,7 @@
         ":strings",
         "//absl/base:config",
         "//absl/log:check",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -1133,6 +1225,7 @@
     deps = [
         ":strings",
         "//absl/base:config",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -1148,6 +1241,7 @@
     deps = [
         ":strings",
         "@com_github_google_benchmark//:benchmark_main",
+        "@com_google_googletest//:gtest",
     ],
 )
 
@@ -1160,6 +1254,11 @@
     linkopts = ABSL_DEFAULT_LINKOPTS,
     deps = [
         ":str_format_internal",
+        ":string_view",
+        "//absl/base:config",
+        "//absl/base:core_headers",
+        "//absl/base:nullability",
+        "//absl/types:span",
     ],
 )
 
@@ -1190,6 +1289,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",
@@ -1211,6 +1311,10 @@
         ":cord",
         ":str_format",
         ":strings",
+        "//absl/base:config",
+        "//absl/base:core_headers",
+        "//absl/types:span",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -1226,6 +1330,7 @@
         ":str_format",
         ":str_format_internal",
         ":strings",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -1238,6 +1343,8 @@
     deps = [
         ":str_format",
         ":str_format_internal",
+        "//absl/base:config",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -1249,6 +1356,7 @@
     visibility = ["//visibility:private"],
     deps = [
         ":str_format_internal",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -1260,6 +1368,7 @@
     visibility = ["//visibility:private"],
     deps = [
         ":str_format",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -1271,12 +1380,17 @@
     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",
     ],
 )
@@ -1289,6 +1403,7 @@
     deps = [
         ":cord",
         ":str_format_internal",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -1300,7 +1415,10 @@
     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",
     ],
 )
@@ -1323,6 +1441,7 @@
     deps = [
         ":pow10_helper",
         ":str_format",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -1350,6 +1469,7 @@
     deps = [
         ":str_format",
         ":strings",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
diff --git a/absl/strings/CMakeLists.txt b/absl/strings/CMakeLists.txt
index 1959dc9..1b24536 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
 )
@@ -38,6 +39,7 @@
     "ascii.h"
     "charconv.h"
     "escaping.h"
+    "has_absl_stringify.h"
     "internal/damerau_levenshtein_distance.h"
     "internal/string_constant.h"
     "internal/has_absl_stringify.h"
@@ -78,23 +80,49 @@
     absl::strings_internal
     absl::base
     absl::bits
+    absl::charset
     absl::config
     absl::core_headers
     absl::endian
     absl::int128
     absl::memory
+    absl::nullability
     absl::raw_logging_internal
     absl::throw_delegate
     absl::type_traits
   PUBLIC
 )
 
+absl_cc_library(
+  NAME
+    charset
+  HDRS
+    charset.h
+  COPTS
+    ${ABSL_DEFAULT_COPTS}
+  DEPS
+    absl::core_headers
+    absl::string_view
+  PUBLIC
+)
+
+absl_cc_library(
+  NAME
+    has_ostream_operator
+  HDRS
+    "has_ostream_operator.h"
+  COPTS
+    ${ABSL_DEFAULT_COPTS}
+  DEPS
+    absl::config
+  PUBLIC
+)
+
 # Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
     strings_internal
   HDRS
-    "internal/char_map.h"
     "internal/escaping.cc"
     "internal/escaping.h"
     "internal/ostringstream.h"
@@ -138,6 +166,33 @@
     absl::core_headers
     absl::fixed_array
     GTest::gmock_main
+    absl::check
+)
+
+absl_cc_test(
+  NAME
+    has_absl_stringify_test
+  SRCS
+    "has_absl_stringify_test.cc"
+  COPTS
+    ${ABSL_TEST_COPTS}
+  DEPS
+    absl::optional
+    absl::strings
+    GTest::gmock_main
+)
+
+absl_cc_test(
+  NAME
+    has_ostream_operator_test
+  SRCS
+    "has_ostream_operator_test.cc"
+  COPTS
+    ${ABSL_TEST_COPTS}
+  DEPS
+    absl::has_ostream_operator
+    absl::optional
+    GTest::gmock_main
 )
 
 absl_cc_test(
@@ -329,13 +384,14 @@
   COPTS
     ${ABSL_TEST_COPTS}
   DEPS
-    absl::strings
-    absl::core_headers
-    absl::pow10_helper
     absl::config
+    absl::core_headers
+    absl::int128
     absl::log
-    absl::random_random
+    absl::pow10_helper
     absl::random_distributions
+    absl::random_random
+    absl::strings
     absl::strings_internal
     GTest::gmock_main
 )
@@ -355,13 +411,13 @@
 
 absl_cc_test(
   NAME
-    char_map_test
+    charset_test
   SRCS
-    "internal/char_map_test.cc"
+    "charset_test.cc"
   COPTS
     ${ABSL_TEST_COPTS}
   DEPS
-    absl::strings_internal
+    absl::strings
     GTest::gmock_main
 )
 
@@ -417,7 +473,12 @@
   COPTS
     ${ABSL_DEFAULT_COPTS}
   DEPS
+    absl::config
+    absl::core_headers
+    absl::nullability
+    absl::span
     absl::str_format_internal
+    absl::string_view
   PUBLIC
 )
 
@@ -448,6 +509,7 @@
     absl::strings
     absl::config
     absl::core_headers
+    absl::fixed_array
     absl::inlined_vector
     absl::numeric_representation
     absl::type_traits
@@ -464,10 +526,12 @@
   COPTS
     ${ABSL_TEST_COPTS}
   DEPS
-    absl::str_format
+    absl::config
     absl::cord
-    absl::strings
     absl::core_headers
+    absl::span
+    absl::str_format
+    absl::strings
     GTest::gmock_main
 )
 
@@ -493,6 +557,7 @@
   COPTS
     ${ABSL_TEST_COPTS}
   DEPS
+    absl::config
     absl::str_format
     absl::str_format_internal
     GTest::gmock_main
@@ -530,12 +595,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
 )
 
@@ -561,6 +629,8 @@
     ${ABSL_TEST_COPTS}
   DEPS
     absl::str_format_internal
+    absl::string_view
+    absl::config
     absl::core_headers
     GTest::gmock_main
 )
@@ -620,8 +690,6 @@
     "internal/cord_rep_crc.h"
     "internal/cord_rep_consume.h"
     "internal/cord_rep_flat.h"
-    "internal/cord_rep_ring.h"
-    "internal/cord_rep_ring_reader.h"
   SRCS
     "internal/cord_internal.cc"
     "internal/cord_rep_btree.cc"
@@ -629,7 +697,6 @@
     "internal/cord_rep_btree_reader.cc"
     "internal/cord_rep_crc.cc"
     "internal/cord_rep_consume.cc"
-    "internal/cord_rep_ring.cc"
   COPTS
     ${ABSL_DEFAULT_COPTS}
   DEPS
@@ -918,11 +985,12 @@
     absl::cordz_update_scope
     absl::cordz_update_tracker
     absl::core_headers
+    absl::crc32c
     absl::crc_cord_state
     absl::endian
-    absl::fixed_array
     absl::function_ref
     absl::inlined_vector
+    absl::nullability
     absl::optional
     absl::raw_logging_internal
     absl::span
@@ -980,6 +1048,7 @@
     absl::cordz_statistics
     absl::cordz_update_tracker
     absl::core_headers
+    absl::nullability
     absl::strings
   TESTONLY
 )
@@ -1001,8 +1070,10 @@
     absl::core_headers
     absl::endian
     absl::fixed_array
+    absl::function_ref
     absl::hash
     absl::log
+    absl::optional
     absl::random_random
     absl::str_format
     absl::strings
@@ -1098,38 +1169,6 @@
 
 absl_cc_test(
   NAME
-    cord_ring_test
-  SRCS
-    "cord_ring_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::base
-    absl::config
-    absl::cord_internal
-    absl::core_headers
-    absl::raw_logging_internal
-    absl::strings
-    GTest::gmock_main
-)
-
-absl_cc_test(
-  NAME
-    cord_ring_reader_test
-  SRCS
-    "cord_ring_reader_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::base
-    absl::cord_internal
-    absl::core_headers
-    absl::strings
-    GTest::gmock_main
-)
-
-absl_cc_test(
-  NAME
     cordz_test
   SRCS
     "cordz_test.cc"
@@ -1137,6 +1176,7 @@
     ${ABSL_TEST_COPTS}
   DEPS
     absl::cord
+    absl::cord_internal
     absl::cord_test_helpers
     absl::cordz_test_helpers
     absl::cordz_functions
diff --git a/absl/strings/ascii.cc b/absl/strings/ascii.cc
index 16c9689..5460b2c 100644
--- a/absl/strings/ascii.cc
+++ b/absl/strings/ascii.cc
@@ -15,8 +15,13 @@
 #include "absl/strings/ascii.h"
 
 #include <climits>
+#include <cstdint>
 #include <cstring>
 #include <string>
+#include <type_traits>
+
+#include "absl/base/config.h"
+#include "absl/base/nullability.h"
 
 namespace absl {
 ABSL_NAMESPACE_BEGIN
@@ -157,8 +162,74 @@
 };
 // clang-format on
 
+template <class T>
+static constexpr T BroadcastByte(unsigned char value) {
+  static_assert(std::is_integral<T>::value && sizeof(T) <= sizeof(uint64_t) &&
+                    std::is_unsigned<T>::value,
+                "only unsigned integers up to 64-bit allowed");
+  T result = value;
+  constexpr size_t result_bit_width = sizeof(result) * CHAR_BIT;
+  result |= result << ((CHAR_BIT << 0) & (result_bit_width - 1));
+  result |= result << ((CHAR_BIT << 1) & (result_bit_width - 1));
+  result |= result << ((CHAR_BIT << 2) & (result_bit_width - 1));
+  return result;
+}
+
+// Returns whether `c` is in the a-z/A-Z range (w.r.t. `ToUpper`).
+// Implemented by:
+//  1. Pushing the a-z/A-Z range to [SCHAR_MIN, SCHAR_MIN + 26).
+//  2. Comparing to SCHAR_MIN + 26.
 template <bool ToUpper>
-constexpr void AsciiStrCaseFold(char* p, char* end) {
+constexpr bool AsciiInAZRange(unsigned char c) {
+  constexpr unsigned char sub = (ToUpper ? 'a' : 'A') - SCHAR_MIN;
+  constexpr signed char threshold = SCHAR_MIN + 26;  // 26 = alphabet size.
+  // Using unsigned arithmetic as overflows/underflows are well defined.
+  unsigned char u = c - sub;
+  // Using signed cmp, as SIMD unsigned cmp isn't available in many platforms.
+  return static_cast<signed char>(u) < threshold;
+}
+
+template <bool ToUpper>
+static constexpr char* PartialAsciiStrCaseFold(absl::Nonnull<char*> p,
+                                               absl::Nonnull<char*> end) {
+  using vec_t = size_t;
+  const size_t n = static_cast<size_t>(end - p);
+
+  // SWAR algorithm: http://0x80.pl/notesen/2016-01-06-swar-swap-case.html
+  constexpr char ch_a = ToUpper ? 'a' : 'A', ch_z = ToUpper ? 'z' : 'Z';
+  char* const swar_end = p + (n / sizeof(vec_t)) * sizeof(vec_t);
+  while (p < swar_end) {
+    vec_t v = vec_t();
+
+    // memcpy the vector, but constexpr
+    for (size_t i = 0; i < sizeof(vec_t); ++i) {
+      v |= static_cast<vec_t>(static_cast<unsigned char>(p[i]))
+           << (i * CHAR_BIT);
+    }
+
+    constexpr unsigned int msb = 1u << (CHAR_BIT - 1);
+    const vec_t v_msb = v & BroadcastByte<vec_t>(msb);
+    const vec_t v_nonascii_mask = (v_msb << 1) - (v_msb >> (CHAR_BIT - 1));
+    const vec_t v_nonascii = v & v_nonascii_mask;
+    const vec_t v_ascii = v & ~v_nonascii_mask;
+    const vec_t a = v_ascii + BroadcastByte<vec_t>(msb - ch_a - 0),
+                z = v_ascii + BroadcastByte<vec_t>(msb - ch_z - 1);
+    v = v_nonascii | (v_ascii ^ ((a ^ z) & BroadcastByte<vec_t>(msb)) >> 2);
+
+    // memcpy the vector, but constexpr
+    for (size_t i = 0; i < sizeof(vec_t); ++i) {
+      p[i] = static_cast<char>(v >> (i * CHAR_BIT));
+    }
+
+    p += sizeof(v);
+  }
+
+  return p;
+}
+
+template <bool ToUpper>
+static 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
@@ -166,16 +237,17 @@
   // have the same single bit difference.
   constexpr unsigned char kAsciiCaseBitFlip = 'a' ^ 'A';
 
-  constexpr char ch_a = ToUpper ? 'a' : 'A';
-  constexpr char ch_z = ToUpper ? 'z' : 'Z';
-  for (; p < end; ++p) {
+  using vec_t = size_t;
+  // TODO(b/316380338): When FDO becomes able to vectorize these,
+  // revert this manual optimization and just leave the naive loop.
+  if (static_cast<size_t>(end - p) >= sizeof(vec_t)) {
+    p = ascii_internal::PartialAsciiStrCaseFold<ToUpper>(p, end);
+  }
+  while (p < end) {
     unsigned char v = static_cast<unsigned char>(*p);
-    // We use & instead of && to ensure this always stays branchless
-    // We use static_cast<int> to suppress -Wbitwise-instead-of-logical
-    bool is_in_range = static_cast<bool>(static_cast<int>(ch_a <= v) &
-                                         static_cast<int>(v <= ch_z));
-    v ^= is_in_range ? kAsciiCaseBitFlip : 0;
+    v ^= AsciiInAZRange<ToUpper>(v) ? kAsciiCaseBitFlip : 0;
     *p = static_cast<char>(v);
+    ++p;
   }
 }
 
@@ -205,17 +277,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 42eadae..c238f4d 100644
--- a/absl/strings/ascii.h
+++ b/absl/strings/ascii.h
@@ -53,10 +53,12 @@
 #define ABSL_STRINGS_ASCII_H_
 
 #include <algorithm>
+#include <cstddef>
 #include <string>
 
 #include "absl/base/attributes.h"
 #include "absl/base/config.h"
+#include "absl/base/nullability.h"
 #include "absl/strings/string_view.h"
 
 namespace absl {
@@ -165,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) {
@@ -183,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) {
@@ -201,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);
 }
@@ -215,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));
 }
@@ -228,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/ascii_benchmark.cc b/absl/strings/ascii_benchmark.cc
index aca458c..4ae7317 100644
--- a/absl/strings/ascii_benchmark.cc
+++ b/absl/strings/ascii_benchmark.cc
@@ -14,7 +14,9 @@
 
 #include "absl/strings/ascii.h"
 
+#include <algorithm>
 #include <cctype>
+#include <cstddef>
 #include <string>
 #include <array>
 #include <random>
@@ -103,18 +105,28 @@
   const int size = state.range(0);
   std::string s(size, 'X');
   for (auto _ : state) {
-    benchmark::DoNotOptimize(absl::AsciiStrToLower(s));
+    benchmark::DoNotOptimize(s);
+    std::string res = absl::AsciiStrToLower(s);
+    benchmark::DoNotOptimize(res);
   }
 }
-BENCHMARK(BM_StrToLower)->Range(1, 1 << 20);
+BENCHMARK(BM_StrToLower)
+    ->DenseRange(0, 32)
+    ->RangeMultiplier(2)
+    ->Range(64, 1 << 26);
 
 static void BM_StrToUpper(benchmark::State& state) {
   const int size = state.range(0);
   std::string s(size, 'x');
   for (auto _ : state) {
-    benchmark::DoNotOptimize(absl::AsciiStrToUpper(s));
+    benchmark::DoNotOptimize(s);
+    std::string res = absl::AsciiStrToUpper(s);
+    benchmark::DoNotOptimize(res);
   }
 }
-BENCHMARK(BM_StrToUpper)->Range(1, 1 << 20);
+BENCHMARK(BM_StrToUpper)
+    ->DenseRange(0, 32)
+    ->RangeMultiplier(2)
+    ->Range(64, 1 << 26);
 
 }  // namespace
diff --git a/absl/strings/ascii_test.cc b/absl/strings/ascii_test.cc
index 4ea262f..117140c 100644
--- a/absl/strings/ascii_test.cc
+++ b/absl/strings/ascii_test.cc
@@ -22,7 +22,7 @@
 
 #include "gtest/gtest.h"
 #include "absl/base/macros.h"
-#include "absl/base/port.h"
+#include "absl/strings/string_view.h"
 
 namespace {
 
diff --git a/absl/strings/charconv.cc b/absl/strings/charconv.cc
index 778a1c7..0c9227f 100644
--- a/absl/strings/charconv.cc
+++ b/absl/strings/charconv.cc
@@ -16,12 +16,14 @@
 
 #include <algorithm>
 #include <cassert>
-#include <cmath>
-#include <cstring>
+#include <cstddef>
+#include <cstdint>
 #include <limits>
+#include <system_error>  // NOLINT(build/c++11)
 
 #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"
@@ -118,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::Nonnull<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
@@ -191,7 +193,7 @@
   static constexpr int kEiselLemireMinInclusiveExp10 = -46 - 18;
   static constexpr int kEiselLemireMaxExclusiveExp10 = 39;
 
-  static float MakeNan(const char* tagp) {
+  static float MakeNan(absl::Nonnull<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
@@ -343,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;
@@ -355,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
@@ -404,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()
@@ -450,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);
@@ -684,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) {
@@ -857,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
@@ -943,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/charconv_test.cc b/absl/strings/charconv_test.cc
index b83de5a..c16c735 100644
--- a/absl/strings/charconv_test.cc
+++ b/absl/strings/charconv_test.cc
@@ -14,14 +14,19 @@
 
 #include "absl/strings/charconv.h"
 
+#include <cfloat>
+#include <cmath>
 #include <cstdlib>
+#include <functional>
+#include <limits>
 #include <string>
+#include <system_error>  // NOLINT(build/c++11)
 
-#include "gmock/gmock.h"
 #include "gtest/gtest.h"
 #include "absl/strings/internal/pow10_helper.h"
 #include "absl/strings/str_cat.h"
 #include "absl/strings/str_format.h"
+#include "absl/strings/string_view.h"
 
 #ifdef _MSC_FULL_VER
 #define ABSL_COMPILER_DOES_EXACT_ROUNDING 0
diff --git a/absl/strings/charset.h b/absl/strings/charset.h
new file mode 100644
index 0000000..ff4e81a
--- /dev/null
+++ b/absl/strings/charset.h
@@ -0,0 +1,164 @@
+// 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: charset.h
+// -----------------------------------------------------------------------------
+//
+// This file contains absl::CharSet, a fast, bit-vector set of 8-bit unsigned
+// characters.
+//
+// Instances can be initialized as constexpr constants. For example:
+//
+//   constexpr absl::CharSet kJustX = absl::CharSet::Char('x');
+//   constexpr absl::CharSet kMySymbols = absl::CharSet("$@!");
+//   constexpr absl::CharSet kLetters = absl::CharSet::Range('a', 'z');
+//
+// Multiple instances can be combined that still forms a constexpr expression.
+// For example:
+//
+//   constexpr absl::CharSet kLettersAndNumbers =
+//       absl::CharSet::Range('a', 'z') | absl::CharSet::Range('0', '9');
+//
+// Several pre-defined character classes are available that mirror the methods
+// from <cctype>. For example:
+//
+//   constexpr absl::CharSet kLettersAndWhitespace =
+//       absl::CharSet::AsciiAlphabet() | absl::CharSet::AsciiWhitespace();
+//
+// To check membership, use the .contains method, e.g.
+//
+//   absl::CharSet hex_letters("abcdef");
+//   hex_letters.contains('a');  // true
+//   hex_letters.contains('g');  // false
+
+#ifndef ABSL_STRINGS_CHARSET_H_
+#define ABSL_STRINGS_CHARSET_H_
+
+#include <cstddef>
+#include <cstdint>
+#include <cstring>
+
+#include "absl/base/macros.h"
+#include "absl/base/port.h"
+#include "absl/strings/string_view.h"
+
+namespace absl {
+
+class CharSet {
+ public:
+  constexpr CharSet() : m_() {}
+
+  // Initializes with a given string_view.
+  constexpr explicit CharSet(absl::string_view str) : m_() {
+    for (char c : str) {
+      SetChar(static_cast<unsigned char>(c));
+    }
+  }
+
+  constexpr bool contains(char c) const {
+    return ((m_[static_cast<unsigned char>(c) / 64] >>
+             (static_cast<unsigned char>(c) % 64)) &
+            0x1) == 0x1;
+  }
+
+  constexpr bool empty() const {
+    for (uint64_t c : m_) {
+      if (c != 0) return false;
+    }
+    return true;
+  }
+
+  // Containing only a single specified char.
+  static constexpr CharSet Char(char x) {
+    return CharSet(CharMaskForWord(x, 0), CharMaskForWord(x, 1),
+                   CharMaskForWord(x, 2), CharMaskForWord(x, 3));
+  }
+
+  // Containing all the chars in the closed interval [lo,hi].
+  static constexpr CharSet Range(char lo, char hi) {
+    return CharSet(RangeForWord(lo, hi, 0), RangeForWord(lo, hi, 1),
+                   RangeForWord(lo, hi, 2), RangeForWord(lo, hi, 3));
+  }
+
+  friend constexpr CharSet operator&(const CharSet& a, const CharSet& b) {
+    return CharSet(a.m_[0] & b.m_[0], a.m_[1] & b.m_[1], a.m_[2] & b.m_[2],
+                   a.m_[3] & b.m_[3]);
+  }
+
+  friend constexpr CharSet operator|(const CharSet& a, const CharSet& b) {
+    return CharSet(a.m_[0] | b.m_[0], a.m_[1] | b.m_[1], a.m_[2] | b.m_[2],
+                   a.m_[3] | b.m_[3]);
+  }
+
+  friend constexpr CharSet operator~(const CharSet& a) {
+    return CharSet(~a.m_[0], ~a.m_[1], ~a.m_[2], ~a.m_[3]);
+  }
+
+  // Mirrors the char-classifying predicates in <cctype>.
+  static constexpr CharSet AsciiUppercase() { return CharSet::Range('A', 'Z'); }
+  static constexpr CharSet AsciiLowercase() { return CharSet::Range('a', 'z'); }
+  static constexpr CharSet AsciiDigits() { return CharSet::Range('0', '9'); }
+  static constexpr CharSet AsciiAlphabet() {
+    return AsciiLowercase() | AsciiUppercase();
+  }
+  static constexpr CharSet AsciiAlphanumerics() {
+    return AsciiDigits() | AsciiAlphabet();
+  }
+  static constexpr CharSet AsciiHexDigits() {
+    return AsciiDigits() | CharSet::Range('A', 'F') | CharSet::Range('a', 'f');
+  }
+  static constexpr CharSet AsciiPrintable() {
+    return CharSet::Range(0x20, 0x7e);
+  }
+  static constexpr CharSet AsciiWhitespace() { return CharSet("\t\n\v\f\r "); }
+  static constexpr CharSet AsciiPunctuation() {
+    return AsciiPrintable() & ~AsciiWhitespace() & ~AsciiAlphanumerics();
+  }
+
+ private:
+  constexpr CharSet(uint64_t b0, uint64_t b1, uint64_t b2, uint64_t b3)
+      : m_{b0, b1, b2, b3} {}
+
+  static constexpr uint64_t RangeForWord(char lo, char hi, uint64_t word) {
+    return OpenRangeFromZeroForWord(static_cast<unsigned char>(hi) + 1, word) &
+           ~OpenRangeFromZeroForWord(static_cast<unsigned char>(lo), word);
+  }
+
+  // All the chars in the specified word of the range [0, upper).
+  static constexpr uint64_t OpenRangeFromZeroForWord(uint64_t upper,
+                                                     uint64_t word) {
+    return (upper <= 64 * word) ? 0
+           : (upper >= 64 * (word + 1))
+               ? ~static_cast<uint64_t>(0)
+               : (~static_cast<uint64_t>(0) >> (64 - upper % 64));
+  }
+
+  static constexpr uint64_t CharMaskForWord(char x, uint64_t word) {
+    return (static_cast<unsigned char>(x) / 64 == word)
+               ? (static_cast<uint64_t>(1)
+                  << (static_cast<unsigned char>(x) % 64))
+               : 0;
+  }
+
+  constexpr void SetChar(unsigned char c) {
+    m_[c / 64] |= static_cast<uint64_t>(1) << (c % 64);
+  }
+
+  uint64_t m_[4];
+};
+
+}  // namespace absl
+
+#endif  // ABSL_STRINGS_CHARSET_H_
diff --git a/absl/strings/internal/char_map_benchmark.cc b/absl/strings/charset_benchmark.cc
similarity index 71%
rename from absl/strings/internal/char_map_benchmark.cc
rename to absl/strings/charset_benchmark.cc
index 5cef967..bf7ae56 100644
--- a/absl/strings/internal/char_map_benchmark.cc
+++ b/absl/strings/charset_benchmark.cc
@@ -1,4 +1,4 @@
-// Copyright 2017 The Abseil Authors.
+// Copyright 2020 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.
@@ -12,30 +12,30 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "absl/strings/internal/char_map.h"
-
 #include <cstdint>
 
 #include "benchmark/benchmark.h"
+#include "absl/log/check.h"
+#include "absl/strings/charset.h"
 
 namespace {
 
-absl::strings_internal::Charmap MakeBenchmarkMap() {
-  absl::strings_internal::Charmap m;
+absl::CharSet MakeBenchmarkMap() {
+  absl::CharSet m;
   uint32_t x[] = {0x0, 0x1, 0x2, 0x3, 0xf, 0xe, 0xd, 0xc};
   for (uint32_t& t : x) t *= static_cast<uint32_t>(0x11111111UL);
   for (uint32_t i = 0; i < 256; ++i) {
-    if ((x[i / 32] >> (i % 32)) & 1)
-      m = m | absl::strings_internal::Charmap::Char(i);
+    if ((x[i / 32] >> (i % 32)) & 1) m = m | absl::CharSet::Char(i);
   }
   return m;
 }
 
 // Micro-benchmark for Charmap::contains.
-void BM_Contains(benchmark::State& state) {
+static void BM_Contains(benchmark::State& state) {
   // Loop-body replicated 10 times to increase time per iteration.
   // Argument continuously changed to avoid generating common subexpressions.
-  const absl::strings_internal::Charmap benchmark_map = MakeBenchmarkMap();
+  // Final CHECK used to discourage unwanted optimization.
+  const absl::CharSet benchmark_map = MakeBenchmarkMap();
   unsigned char c = 0;
   int ops = 0;
   for (auto _ : state) {
@@ -50,12 +50,8 @@
     ops += benchmark_map.contains(c++);
     ops += benchmark_map.contains(c++);
   }
-  benchmark::DoNotOptimize(ops);
+  CHECK_NE(ops, -1);
 }
 BENCHMARK(BM_Contains);
 
-// We don't bother benchmarking Charmap::IsZero or Charmap::IntersectsWith;
-// their running time is data-dependent and it is not worth characterizing
-// "typical" data.
-
 }  // namespace
diff --git a/absl/strings/charset_test.cc b/absl/strings/charset_test.cc
new file mode 100644
index 0000000..fff943a
--- /dev/null
+++ b/absl/strings/charset_test.cc
@@ -0,0 +1,181 @@
+// Copyright 2020 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/strings/charset.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <string>
+#include <vector>
+
+#include "gtest/gtest.h"
+#include "absl/strings/ascii.h"
+#include "absl/strings/string_view.h"
+
+namespace {
+
+constexpr absl::CharSet everything_map = ~absl::CharSet();
+constexpr absl::CharSet nothing_map = absl::CharSet();
+
+TEST(Charmap, AllTests) {
+  const absl::CharSet also_nothing_map("");
+  EXPECT_TRUE(everything_map.contains('\0'));
+  EXPECT_FALSE(nothing_map.contains('\0'));
+  EXPECT_FALSE(also_nothing_map.contains('\0'));
+  for (unsigned char ch = 1; ch != 0; ++ch) {
+    SCOPED_TRACE(ch);
+    EXPECT_TRUE(everything_map.contains(ch));
+    EXPECT_FALSE(nothing_map.contains(ch));
+    EXPECT_FALSE(also_nothing_map.contains(ch));
+  }
+
+  const absl::CharSet symbols(absl::string_view("&@#@^!@?", 5));
+  EXPECT_TRUE(symbols.contains('&'));
+  EXPECT_TRUE(symbols.contains('@'));
+  EXPECT_TRUE(symbols.contains('#'));
+  EXPECT_TRUE(symbols.contains('^'));
+  EXPECT_FALSE(symbols.contains('!'));
+  EXPECT_FALSE(symbols.contains('?'));
+  int cnt = 0;
+  for (unsigned char ch = 1; ch != 0; ++ch) cnt += symbols.contains(ch);
+  EXPECT_EQ(cnt, 4);
+
+  const absl::CharSet lets(absl::string_view("^abcde", 3));
+  const absl::CharSet lets2(absl::string_view("fghij\0klmnop", 10));
+  const absl::CharSet lets3("fghij\0klmnop");
+  EXPECT_TRUE(lets2.contains('k'));
+  EXPECT_FALSE(lets3.contains('k'));
+
+  EXPECT_FALSE((symbols & lets).empty());
+  EXPECT_TRUE((lets2 & lets).empty());
+  EXPECT_FALSE((lets & symbols).empty());
+  EXPECT_TRUE((lets & lets2).empty());
+
+  EXPECT_TRUE(nothing_map.empty());
+  EXPECT_FALSE(lets.empty());
+}
+
+std::string Members(const absl::CharSet& m) {
+  std::string r;
+  for (size_t i = 0; i < 256; ++i)
+    if (m.contains(i)) r.push_back(i);
+  return r;
+}
+
+std::string ClosedRangeString(unsigned char lo, unsigned char hi) {
+  // Don't depend on lo<hi. Just increment until lo==hi.
+  std::string s;
+  while (true) {
+    s.push_back(lo);
+    if (lo == hi) break;
+    ++lo;
+  }
+  return s;
+}
+
+TEST(Charmap, Constexpr) {
+  constexpr absl::CharSet kEmpty = absl::CharSet();
+  EXPECT_EQ(Members(kEmpty), "");
+  constexpr absl::CharSet kA = absl::CharSet::Char('A');
+  EXPECT_EQ(Members(kA), "A");
+  constexpr absl::CharSet kAZ = absl::CharSet::Range('A', 'Z');
+  EXPECT_EQ(Members(kAZ), "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
+  constexpr absl::CharSet kIdentifier =
+      absl::CharSet::Range('0', '9') | absl::CharSet::Range('A', 'Z') |
+      absl::CharSet::Range('a', 'z') | absl::CharSet::Char('_');
+  EXPECT_EQ(Members(kIdentifier),
+            "0123456789"
+            "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+            "_"
+            "abcdefghijklmnopqrstuvwxyz");
+  constexpr absl::CharSet kAll = ~absl::CharSet();
+  for (size_t i = 0; i < 256; ++i) {
+    SCOPED_TRACE(i);
+    EXPECT_TRUE(kAll.contains(i));
+  }
+  constexpr absl::CharSet kHello = absl::CharSet("Hello, world!");
+  EXPECT_EQ(Members(kHello), " !,Hdelorw");
+
+  // test negation and intersection
+  constexpr absl::CharSet kABC =
+      absl::CharSet::Range('A', 'Z') & ~absl::CharSet::Range('D', 'Z');
+  EXPECT_EQ(Members(kABC), "ABC");
+
+  // contains
+  constexpr bool kContainsA = absl::CharSet("abc").contains('a');
+  EXPECT_TRUE(kContainsA);
+  constexpr bool kContainsD = absl::CharSet("abc").contains('d');
+  EXPECT_FALSE(kContainsD);
+
+  // empty
+  constexpr bool kEmptyIsEmpty = absl::CharSet().empty();
+  EXPECT_TRUE(kEmptyIsEmpty);
+  constexpr bool kNotEmptyIsEmpty = absl::CharSet("abc").empty();
+  EXPECT_FALSE(kNotEmptyIsEmpty);
+}
+
+TEST(Charmap, Range) {
+  // Exhaustive testing takes too long, so test some of the boundaries that
+  // are perhaps going to cause trouble.
+  std::vector<size_t> poi = {0,   1,   2,   3,   4,   7,   8,   9,  15,
+                             16,  17,  30,  31,  32,  33,  63,  64, 65,
+                             127, 128, 129, 223, 224, 225, 254, 255};
+  for (auto lo = poi.begin(); lo != poi.end(); ++lo) {
+    SCOPED_TRACE(*lo);
+    for (auto hi = lo; hi != poi.end(); ++hi) {
+      SCOPED_TRACE(*hi);
+      EXPECT_EQ(Members(absl::CharSet::Range(*lo, *hi)),
+                ClosedRangeString(*lo, *hi));
+    }
+  }
+}
+
+TEST(Charmap, NullByteWithStringView) {
+  char characters[5] = {'a', 'b', '\0', 'd', 'x'};
+  absl::string_view view(characters, 5);
+  absl::CharSet tester(view);
+  EXPECT_TRUE(tester.contains('a'));
+  EXPECT_TRUE(tester.contains('b'));
+  EXPECT_TRUE(tester.contains('\0'));
+  EXPECT_TRUE(tester.contains('d'));
+  EXPECT_TRUE(tester.contains('x'));
+  EXPECT_FALSE(tester.contains('c'));
+}
+
+TEST(CharmapCtype, Match) {
+  for (int c = 0; c < 256; ++c) {
+    SCOPED_TRACE(c);
+    SCOPED_TRACE(static_cast<char>(c));
+    EXPECT_EQ(absl::ascii_isupper(c),
+              absl::CharSet::AsciiUppercase().contains(c));
+    EXPECT_EQ(absl::ascii_islower(c),
+              absl::CharSet::AsciiLowercase().contains(c));
+    EXPECT_EQ(absl::ascii_isdigit(c), absl::CharSet::AsciiDigits().contains(c));
+    EXPECT_EQ(absl::ascii_isalpha(c),
+              absl::CharSet::AsciiAlphabet().contains(c));
+    EXPECT_EQ(absl::ascii_isalnum(c),
+              absl::CharSet::AsciiAlphanumerics().contains(c));
+    EXPECT_EQ(absl::ascii_isxdigit(c),
+              absl::CharSet::AsciiHexDigits().contains(c));
+    EXPECT_EQ(absl::ascii_isprint(c),
+              absl::CharSet::AsciiPrintable().contains(c));
+    EXPECT_EQ(absl::ascii_isspace(c),
+              absl::CharSet::AsciiWhitespace().contains(c));
+    EXPECT_EQ(absl::ascii_ispunct(c),
+              absl::CharSet::AsciiPunctuation().contains(c));
+  }
+}
+
+}  // namespace
diff --git a/absl/strings/cord.cc b/absl/strings/cord.cc
index 14976ae..f67326f 100644
--- a/absl/strings/cord.cc
+++ b/absl/strings/cord.cc
@@ -15,27 +15,33 @@
 #include "absl/strings/cord.h"
 
 #include <algorithm>
-#include <atomic>
+#include <cassert>
 #include <cstddef>
+#include <cstdint>
 #include <cstdio>
 #include <cstdlib>
+#include <cstring>
 #include <iomanip>
 #include <ios>
 #include <iostream>
 #include <limits>
+#include <memory>
 #include <ostream>
 #include <sstream>
-#include <type_traits>
-#include <unordered_set>
-#include <vector>
+#include <string>
+#include <utility>
 
-#include "absl/base/casts.h"
+#include "absl/base/attributes.h"
+#include "absl/base/config.h"
+#include "absl/base/internal/endian.h"
 #include "absl/base/internal/raw_logging.h"
 #include "absl/base/macros.h"
-#include "absl/base/port.h"
-#include "absl/container/fixed_array.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"
+#include "absl/functional/function_ref.h"
 #include "absl/strings/cord_buffer.h"
 #include "absl/strings/escaping.h"
 #include "absl/strings/internal/cord_data_edge.h"
@@ -43,13 +49,14 @@
 #include "absl/strings/internal/cord_rep_btree.h"
 #include "absl/strings/internal/cord_rep_crc.h"
 #include "absl/strings/internal/cord_rep_flat.h"
-#include "absl/strings/internal/cordz_statistics.h"
-#include "absl/strings/internal/cordz_update_scope.h"
 #include "absl/strings/internal/cordz_update_tracker.h"
 #include "absl/strings/internal/resize_uninitialized.h"
+#include "absl/strings/match.h"
 #include "absl/strings/str_cat.h"
-#include "absl/strings/str_join.h"
 #include "absl/strings/string_view.h"
+#include "absl/strings/strip.h"
+#include "absl/types/optional.h"
+#include "absl/types/span.h"
 
 namespace absl {
 ABSL_NAMESPACE_BEGIN
@@ -68,29 +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,
-                       bool full_validation);
+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) {
-  // Verification is expensive, so only do it in debug mode.
-  // Even in debug mode we normally do only light validation.
-  // If you are debugging Cord itself, you should define the
-  // macro EXTRA_CORD_VALIDATION, e.g. by adding
-  // --copt=-DEXTRA_CORD_VALIDATION to the blaze line.
-#ifdef EXTRA_CORD_VALIDATION
-  assert(node == nullptr || VerifyNode(node, node, /*full_validation=*/true));
-#else   // EXTRA_CORD_VALIDATION
-  assert(node == nullptr || VerifyNode(node, node, /*full_validation=*/false));
-#endif  // EXTRA_CORD_VALIDATION
+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);
@@ -99,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);
   }
@@ -112,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;
@@ -133,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.
@@ -165,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);
@@ -194,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()) {
@@ -210,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());
@@ -228,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()) {
@@ -238,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);
@@ -246,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());
@@ -261,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()) {
@@ -465,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;
@@ -527,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:
@@ -573,13 +580,9 @@
   return CreateAppendBuffer(contents_.data_, block_size, capacity);
 }
 
-void Cord::Append(const Cord& src) {
-  AppendImpl(src);
-}
+void Cord::Append(const Cord& src) { AppendImpl(src); }
 
-void Cord::Append(Cord&& src) {
-  AppendImpl(std::move(src));
-}
+void Cord::Append(Cord&& src) { AppendImpl(std::move(src)); }
 
 template <typename T, Cord::EnableIfString<T>>
 void Cord::Append(T&& src) {
@@ -778,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;
@@ -877,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;
   }
@@ -894,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;
@@ -924,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;
@@ -1046,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 {
@@ -1055,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)) {
@@ -1165,6 +1172,194 @@
   }
 }
 
+namespace {
+
+// Tests whether the sequence of chunks beginning at `position` starts with
+// `needle`.
+//
+// REQUIRES: remaining `absl::Cord` starting at `position` is greater than or
+// equal to `needle.size()`.
+bool IsSubstringInCordAt(absl::Cord::CharIterator position,
+                         absl::string_view needle) {
+  auto haystack_chunk = absl::Cord::ChunkRemaining(position);
+  while (true) {
+    // Precondition is that `absl::Cord::ChunkRemaining(position)` is not
+    // empty. This assert will trigger if that is not true.
+    assert(!haystack_chunk.empty());
+    auto min_length = std::min(haystack_chunk.size(), needle.size());
+    if (!absl::ConsumePrefix(&needle, haystack_chunk.substr(0, min_length))) {
+      return false;
+    }
+    if (needle.empty()) {
+      return true;
+    }
+    absl::Cord::Advance(&position, min_length);
+    haystack_chunk = absl::Cord::ChunkRemaining(position);
+  }
+}
+
+}  // namespace
+
+// A few options how this could be implemented:
+// (a) Flatten the Cord and find, i.e.
+//       haystack.Flatten().find(needle)
+//     For large 'haystack' (where Cord makes sense to be used), this copies
+//     the whole 'haystack' and can be slow.
+// (b) Use std::search, i.e.
+//       std::search(haystack.char_begin(), haystack.char_end(),
+//                   needle.begin(), needle.end())
+//     This avoids the copy, but compares one byte at a time, and branches a
+//     lot every time it has to advance. It is also not possible to use
+//     std::search as is, because CharIterator is only an input iterator, not a
+//     forward iterator.
+// (c) Use string_view::find in each fragment, and specifically handle fragment
+//     boundaries.
+//
+// This currently implements option (b).
+absl::Cord::CharIterator absl::Cord::FindImpl(CharIterator it,
+                                              absl::string_view needle) const {
+  // Ensure preconditions are met by callers first.
+
+  // Needle must not be empty.
+  assert(!needle.empty());
+  // Haystack must be at least as large as needle.
+  assert(it.chunk_iterator_.bytes_remaining_ >= needle.size());
+
+  // Cord is a sequence of chunks. To find `needle` we go chunk by chunk looking
+  // for the first char of needle, up until we have advanced `N` defined as
+  // `haystack.size() - needle.size()`. If we find the first char of needle at
+  // `P` and `P` is less than `N`, we then call `IsSubstringInCordAt` to
+  // see if this is the needle. If not, we advance to `P + 1` and try again.
+  while (it.chunk_iterator_.bytes_remaining_ >= needle.size()) {
+    auto haystack_chunk = Cord::ChunkRemaining(it);
+    assert(!haystack_chunk.empty());
+    // Look for the first char of `needle` in the current chunk.
+    auto idx = haystack_chunk.find(needle.front());
+    if (idx == absl::string_view::npos) {
+      // No potential match in this chunk, advance past it.
+      Cord::Advance(&it, haystack_chunk.size());
+      continue;
+    }
+    // We found the start of a potential match in the chunk. Advance the
+    // iterator and haystack chunk to the match the position.
+    Cord::Advance(&it, idx);
+    // Check if there is enough haystack remaining to actually have a match.
+    if (it.chunk_iterator_.bytes_remaining_ < needle.size()) {
+      break;
+    }
+    // Check if this is `needle`.
+    if (IsSubstringInCordAt(it, needle)) {
+      return it;
+    }
+    // No match, increment the iterator for the next attempt.
+    Cord::Advance(&it, 1);
+  }
+  // If we got here, we did not find `needle`.
+  return char_end();
+}
+
+absl::Cord::CharIterator absl::Cord::Find(absl::string_view needle) const {
+  if (needle.empty()) {
+    return char_begin();
+  }
+  if (needle.size() > size()) {
+    return char_end();
+  }
+  if (needle.size() == size()) {
+    return *this == needle ? char_begin() : char_end();
+  }
+  return FindImpl(char_begin(), needle);
+}
+
+namespace {
+
+// Tests whether the sequence of chunks beginning at `haystack` starts with the
+// sequence of chunks beginning at `needle_begin` and extending to `needle_end`.
+//
+// REQUIRES: remaining `absl::Cord` starting at `position` is greater than or
+// equal to `needle_end - needle_begin` and `advance`.
+bool IsSubcordInCordAt(absl::Cord::CharIterator haystack,
+                       absl::Cord::CharIterator needle_begin,
+                       absl::Cord::CharIterator needle_end) {
+  while (needle_begin != needle_end) {
+    auto haystack_chunk = absl::Cord::ChunkRemaining(haystack);
+    assert(!haystack_chunk.empty());
+    auto needle_chunk = absl::Cord::ChunkRemaining(needle_begin);
+    auto min_length = std::min(haystack_chunk.size(), needle_chunk.size());
+    if (haystack_chunk.substr(0, min_length) !=
+        needle_chunk.substr(0, min_length)) {
+      return false;
+    }
+    absl::Cord::Advance(&haystack, min_length);
+    absl::Cord::Advance(&needle_begin, min_length);
+  }
+  return true;
+}
+
+// Tests whether the sequence of chunks beginning at `position` starts with the
+// cord `needle`.
+//
+// REQUIRES: remaining `absl::Cord` starting at `position` is greater than or
+// equal to `needle.size()`.
+bool IsSubcordInCordAt(absl::Cord::CharIterator position,
+                       const absl::Cord& needle) {
+  return IsSubcordInCordAt(position, needle.char_begin(), needle.char_end());
+}
+
+}  // namespace
+
+absl::Cord::CharIterator absl::Cord::Find(const absl::Cord& needle) const {
+  if (needle.empty()) {
+    return char_begin();
+  }
+  const auto needle_size = needle.size();
+  if (needle_size > size()) {
+    return char_end();
+  }
+  if (needle_size == size()) {
+    return *this == needle ? char_begin() : char_end();
+  }
+  const auto needle_chunk = Cord::ChunkRemaining(needle.char_begin());
+  auto haystack_it = char_begin();
+  while (true) {
+    haystack_it = FindImpl(haystack_it, needle_chunk);
+    if (haystack_it == char_end() ||
+        haystack_it.chunk_iterator_.bytes_remaining_ < needle_size) {
+      break;
+    }
+    // We found the first chunk of `needle` at `haystack_it` but not the entire
+    // subcord. Advance past the first chunk and check for the remainder.
+    auto haystack_advanced_it = haystack_it;
+    auto needle_it = needle.char_begin();
+    Cord::Advance(&haystack_advanced_it, needle_chunk.size());
+    Cord::Advance(&needle_it, needle_chunk.size());
+    if (IsSubcordInCordAt(haystack_advanced_it, needle_it, needle.char_end())) {
+      return haystack_it;
+    }
+    Cord::Advance(&haystack_it, 1);
+    if (haystack_it.chunk_iterator_.bytes_remaining_ < needle_size) {
+      break;
+    }
+    if (haystack_it.chunk_iterator_.bytes_remaining_ == needle_size) {
+      // Special case, if there is exactly `needle_size` bytes remaining, the
+      // subcord is either at `haystack_it` or not at all.
+      if (IsSubcordInCordAt(haystack_it, needle)) {
+        return haystack_it;
+      }
+      break;
+    }
+  }
+  return char_end();
+}
+
+bool Cord::Contains(absl::string_view rhs) const {
+  return rhs.empty() || Find(rhs) != char_end();
+}
+
+bool Cord::Contains(const absl::Cord& rhs) const {
+  return rhs.empty() || Find(rhs) != char_end();
+}
+
 absl::string_view Cord::FlattenSlowPath() {
   assert(contents_.is_tree());
   size_t total_size = size();
@@ -1193,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();
@@ -1227,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;
@@ -1252,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;
@@ -1289,7 +1485,7 @@
           *os << absl::CEscape(std::string(rep->flat()->Data(), rep->length));
         *os << "]\n";
       } else {
-        CordRepBtree::Dump(rep, /*label=*/ "", include_data, *os);
+        CordRepBtree::Dump(rep, /*label=*/"", include_data, *os);
       }
     }
     if (leaf) {
@@ -1303,16 +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,
-                       bool /* full_validation */) {
-  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 457ccf0..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"
@@ -86,7 +87,6 @@
 #include "absl/strings/internal/cord_rep_btree.h"
 #include "absl/strings/internal/cord_rep_btree_reader.h"
 #include "absl/strings/internal/cord_rep_crc.h"
-#include "absl/strings/internal/cord_rep_ring.h"
 #include "absl/strings/internal/cordz_functions.h"
 #include "absl/strings/internal/cordz_info.h"
 #include "absl/strings/internal/cordz_statistics.h"
@@ -103,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 {
@@ -120,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:
@@ -362,7 +362,7 @@
 
   // Cord::empty()
   //
-  // Determines whether the given Cord is empty, returning `true` is so.
+  // Determines whether the given Cord is empty, returning `true` if so.
   bool empty() const;
 
   // Cord::EstimatedMemoryUsage()
@@ -396,6 +396,12 @@
   bool EndsWith(absl::string_view rhs) const;
   bool EndsWith(const Cord& rhs) const;
 
+  // Cord::Contains()
+  //
+  // Determines whether the Cord contains the passed string data `rhs`.
+  bool Contains(absl::string_view rhs) const;
+  bool Contains(const Cord& rhs) const;
+
   // Cord::operator std::string()
   //
   // Converts a Cord into a `std::string()`. This operator is marked explicit to
@@ -411,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;
 
@@ -448,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;
@@ -468,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()`.
@@ -493,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;
 
@@ -515,7 +522,7 @@
   //                                         absl::string_view s) {
   //     return std::find(c.chunk_begin(), c.chunk_end(), s);
   //   }
-  ChunkIterator chunk_begin() const;
+  ChunkIterator chunk_begin() const ABSL_ATTRIBUTE_LIFETIME_BOUND;
 
   // Cord::chunk_end()
   //
@@ -524,7 +531,7 @@
   // Generally, prefer using `Cord::Chunks()` within a range-based for loop for
   // iterating over the chunks of a Cord. This method may be useful for getting
   // a `ChunkIterator` where range-based for-loops may not be available.
-  ChunkIterator chunk_end() const;
+  ChunkIterator chunk_end() const ABSL_ATTRIBUTE_LIFETIME_BOUND;
 
   //----------------------------------------------------------------------------
   // Cord::ChunkRange
@@ -550,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()
@@ -578,7 +585,7 @@
   //       // The temporary Cord returned by CordFactory has been destroyed!
   //     }
   //   }
-  ChunkRange Chunks() const;
+  ChunkRange Chunks() const ABSL_ATTRIBUTE_LIFETIME_BOUND;
 
   //----------------------------------------------------------------------------
   // Cord::CharIterator
@@ -609,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;
@@ -624,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_;
   };
@@ -635,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()
   //
@@ -658,7 +666,7 @@
   // Generally, prefer using `Cord::Chars()` within a range-based for loop for
   // iterating over the chunks of a Cord. This method may be useful for getting
   // a `CharIterator` where range-based for-loops may not be available.
-  CharIterator char_begin() const;
+  CharIterator char_begin() const ABSL_ATTRIBUTE_LIFETIME_BOUND;
 
   // Cord::char_end()
   //
@@ -667,7 +675,7 @@
   // Generally, prefer using `Cord::Chars()` within a range-based for loop for
   // iterating over the chunks of a Cord. This method may be useful for getting
   // a `CharIterator` where range-based for-loops are not useful.
-  CharIterator char_end() const;
+  CharIterator char_end() const ABSL_ATTRIBUTE_LIFETIME_BOUND;
 
   // Cord::CharRange
   //
@@ -691,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()
@@ -719,7 +727,7 @@
   //       // The temporary Cord returned by CordFactory has been destroyed!
   //     }
   //   }
-  CharRange Chars() const;
+  CharRange Chars() const ABSL_ATTRIBUTE_LIFETIME_BOUND;
 
   // Cord::operator[]
   //
@@ -737,20 +745,38 @@
   //
   // If this cord's representation is a single flat array, returns a
   // string_view referencing that array.  Otherwise returns nullopt.
-  absl::optional<absl::string_view> TryFlat() const;
+  absl::optional<absl::string_view> TryFlat() const
+      ABSL_ATTRIBUTE_LIFETIME_BOUND;
 
   // Cord::Flatten()
   //
   // Flattens the cord into a single array and returns a view of the data.
   //
   // If the cord was already flat, the contents are not modified.
-  absl::string_view Flatten();
+  absl::string_view Flatten() ABSL_ATTRIBUTE_LIFETIME_BOUND;
+
+  // Cord::Find()
+  //
+  // Returns an iterator to the first occurrance of the substring `needle`.
+  //
+  // If the substring `needle` does not occur, `Cord::char_end()` is returned.
+  CharIterator Find(absl::string_view needle) const;
+  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);
   }
 
+  // Support automatic stringification with absl::StrCat and absl::StrFormat.
+  template <typename Sink>
+  friend void AbslStringify(Sink& sink, const absl::Cord& cord) {
+    for (absl::string_view chunk : cord.Chunks()) {
+      sink.Append(chunk);
+    }
+  }
+
   // Cord::SetExpectedChecksum()
   //
   // Stores a checksum value with this non-empty cord instance, for later
@@ -809,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.
@@ -836,20 +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);
-    bool empty() const;
+    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
@@ -858,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.
@@ -909,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(); }
 
@@ -922,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);
     }
@@ -959,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,
@@ -988,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>
@@ -1026,7 +1061,10 @@
 
   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;
 };
 
 ABSL_NAMESPACE_END
@@ -1045,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>(
@@ -1063,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);
 }
@@ -1086,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)
@@ -1125,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 {
@@ -1154,14 +1197,12 @@
   }
 }
 
-inline bool Cord::InlineRep::empty() const { return data_.is_empty(); }
-
 inline size_t Cord::InlineRep::size() const {
   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);
@@ -1170,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());
@@ -1191,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) {
@@ -1202,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) {
@@ -1212,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());
   }
@@ -1221,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);
@@ -1402,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());
@@ -1412,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)) {
@@ -1557,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);
 }
@@ -1618,7 +1664,7 @@
 // Nonmember Cord-to-absl::string_view relational operators.
 //
 // Due to implicit conversions, these also enable comparisons of Cord with
-// with std::string, ::string, and const char*.
+// std::string and const char*.
 inline bool operator==(const Cord& lhs, absl::string_view rhs) {
   size_t lhs_size = lhs.size();
   size_t rhs_size = rhs.size();
diff --git a/absl/strings/cord_analysis.cc b/absl/strings/cord_analysis.cc
index e859b0d..19b0fa4 100644
--- a/absl/strings/cord_analysis.cc
+++ b/absl/strings/cord_analysis.cc
@@ -14,23 +14,17 @@
 
 #include "absl/strings/cord_analysis.h"
 
+#include <cassert>
 #include <cstddef>
 #include <cstdint>
 #include <unordered_set>
 
-#include "absl/base/attributes.h"
 #include "absl/base/config.h"
-#include "absl/container/inlined_vector.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"
 #include "absl/strings/internal/cord_rep_crc.h"
-#include "absl/strings/internal/cord_rep_flat.h"
-#include "absl/strings/internal/cord_rep_ring.h"
-//
-#include "absl/base/macros.h"
-#include "absl/base/port.h"
-#include "absl/functional/function_ref.h"
 
 namespace absl {
 ABSL_NAMESPACE_BEGIN
@@ -45,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.
@@ -70,11 +66,10 @@
 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.find(repref.rep) == counted.end()) {
-      counted.insert(repref.rep);
+    if (counted.insert(repref.rep).second) {
       total += size;
     }
   }
@@ -95,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;
 };
 
@@ -138,16 +133,6 @@
   raw_usage.Add(size, rep);
 }
 
-// Computes the memory size of the provided Ring tree.
-template <Mode mode>
-void AnalyzeRing(CordRepRef<mode> rep, RawUsage<mode>& raw_usage) {
-  const CordRepRing* ring = rep.rep->ring();
-  raw_usage.Add(CordRepRing::AllocSize(ring->capacity()), rep);
-  ring->ForEach([&](CordRepRing::index_type pos) {
-    AnalyzeDataEdge(rep.Child(ring->entry_child(pos)), raw_usage);
-  });
-}
-
 // Computes the memory size of the provided Btree tree.
 template <Mode mode>
 void AnalyzeBtree(CordRepRef<mode> rep, RawUsage<mode>& raw_usage) {
@@ -165,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;
 
@@ -175,6 +160,9 @@
   // Consume the top level CRC node if present.
   if (repref.rep->tag == CRC) {
     raw_usage.Add(sizeof(CordRepCrc), repref);
+    if (repref.rep->crc()->child == nullptr) {
+      return static_cast<size_t>(raw_usage.total);
+    }
     repref = repref.Child(repref.rep->crc()->child);
   }
 
@@ -182,8 +170,6 @@
     AnalyzeDataEdge(repref, raw_usage);
   } else if (repref.rep->tag == BTREE) {
     AnalyzeBtree(repref, raw_usage);
-  } else if (repref.rep->tag == RING) {
-    AnalyzeRing(repref, raw_usage);
   } else {
     assert(false);
   }
@@ -193,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/cord_buffer_test.cc b/absl/strings/cord_buffer_test.cc
index 5c7437a..ab62808 100644
--- a/absl/strings/cord_buffer_test.cc
+++ b/absl/strings/cord_buffer_test.cc
@@ -16,16 +16,18 @@
 
 
 #include <algorithm>
-#include <climits>
 #include <cstring>
+#include <limits>
 #include <string>
 #include <utility>
 
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
 #include "absl/base/config.h"
+#include "absl/strings/internal/cord_internal.h"
 #include "absl/strings/internal/cord_rep_flat.h"
 #include "absl/strings/internal/cord_rep_test_util.h"
+#include "absl/strings/string_view.h"
 #include "absl/types/span.h"
 
 using testing::Eq;
diff --git a/absl/strings/cord_ring_reader_test.cc b/absl/strings/cord_ring_reader_test.cc
deleted file mode 100644
index 8e7183b..0000000
--- a/absl/strings/cord_ring_reader_test.cc
+++ /dev/null
@@ -1,180 +0,0 @@
-// Copyright 2020 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 <cstdlib>
-#include <ctime>
-#include <memory>
-#include <random>
-#include <sstream>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/debugging/leak_check.h"
-#include "absl/strings/internal/cord_internal.h"
-#include "absl/strings/internal/cord_rep_ring.h"
-#include "absl/strings/internal/cord_rep_ring_reader.h"
-#include "absl/strings/string_view.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace cord_internal {
-namespace {
-
-using testing::Eq;
-
-// Creates a flat for testing
-CordRep* MakeFlat(absl::string_view s) {
-  CordRepFlat* flat = CordRepFlat::New(s.length());
-  memcpy(flat->Data(), s.data(), s.length());
-  flat->length = s.length();
-  return flat;
-}
-
-CordRepRing* FromFlats(Span<absl::string_view const> flats) {
-  CordRepRing* ring = CordRepRing::Create(MakeFlat(flats[0]), flats.size() - 1);
-  for (int i = 1; i < flats.size(); ++i) {
-    ring = CordRepRing::Append(ring, MakeFlat(flats[i]));
-  }
-  return ring;
-}
-
-std::array<absl::string_view, 12> TestFlats() {
-  return {"abcdefghij", "klmnopqrst", "uvwxyz",     "ABCDEFGHIJ",
-          "KLMNOPQRST", "UVWXYZ",     "1234567890", "~!@#$%^&*()_",
-          "+-=",        "[]\\{}|;':", ",/<>?",      "."};
-}
-
-TEST(CordRingReaderTest, DefaultInstance) {
-  CordRepRingReader reader;
-  EXPECT_FALSE(static_cast<bool>(reader));
-  EXPECT_THAT(reader.ring(), Eq(nullptr));
-#ifndef NDEBUG
-  EXPECT_DEATH_IF_SUPPORTED(reader.length(), ".*");
-  EXPECT_DEATH_IF_SUPPORTED(reader.consumed(), ".*");
-  EXPECT_DEATH_IF_SUPPORTED(reader.remaining(), ".*");
-  EXPECT_DEATH_IF_SUPPORTED(reader.Next(), ".*");
-  EXPECT_DEATH_IF_SUPPORTED(reader.Seek(0), ".*");
-#endif
-}
-
-TEST(CordRingReaderTest, Reset) {
-  CordRepRingReader reader;
-  auto flats = TestFlats();
-  CordRepRing* ring = FromFlats(flats);
-
-  absl::string_view first = reader.Reset(ring);
-  EXPECT_THAT(first, Eq(flats[0]));
-  EXPECT_TRUE(static_cast<bool>(reader));
-  EXPECT_THAT(reader.ring(), Eq(ring));
-  EXPECT_THAT(reader.index(), Eq(ring->head()));
-  EXPECT_THAT(reader.node(), Eq(ring->entry_child(ring->head())));
-  EXPECT_THAT(reader.length(), Eq(ring->length));
-  EXPECT_THAT(reader.consumed(), Eq(flats[0].length()));
-  EXPECT_THAT(reader.remaining(), Eq(ring->length - reader.consumed()));
-
-  reader.Reset();
-  EXPECT_FALSE(static_cast<bool>(reader));
-  EXPECT_THAT(reader.ring(), Eq(nullptr));
-
-  CordRep::Unref(ring);
-}
-
-TEST(CordRingReaderTest, Next) {
-  CordRepRingReader reader;
-  auto flats = TestFlats();
-  CordRepRing* ring = FromFlats(flats);
-  CordRepRing::index_type head = ring->head();
-
-  reader.Reset(ring);
-  size_t consumed = reader.consumed();
-  size_t remaining = reader.remaining();
-  for (int i = 1; i < flats.size(); ++i) {
-    CordRepRing::index_type index = ring->advance(head, i);
-    consumed += flats[i].length();
-    remaining -= flats[i].length();
-    absl::string_view next = reader.Next();
-    ASSERT_THAT(next, Eq(flats[i]));
-    ASSERT_THAT(reader.index(), Eq(index));
-    ASSERT_THAT(reader.node(), Eq(ring->entry_child(index)));
-    ASSERT_THAT(reader.consumed(), Eq(consumed));
-    ASSERT_THAT(reader.remaining(), Eq(remaining));
-  }
-
-#ifndef NDEBUG
-  EXPECT_DEATH_IF_SUPPORTED(reader.Next(), ".*");
-#endif
-
-  CordRep::Unref(ring);
-}
-
-TEST(CordRingReaderTest, SeekForward) {
-  CordRepRingReader reader;
-  auto flats = TestFlats();
-  CordRepRing* ring = FromFlats(flats);
-  CordRepRing::index_type head = ring->head();
-
-  reader.Reset(ring);
-  size_t consumed = 0;
-  size_t remaining = ring->length;
-  for (int i = 0; i < flats.size(); ++i) {
-    CordRepRing::index_type index = ring->advance(head, i);
-    size_t offset = consumed;
-    consumed += flats[i].length();
-    remaining -= flats[i].length();
-    for (int off = 0; off < flats[i].length(); ++off) {
-      absl::string_view chunk = reader.Seek(offset + off);
-      ASSERT_THAT(chunk, Eq(flats[i].substr(off)));
-      ASSERT_THAT(reader.index(), Eq(index));
-      ASSERT_THAT(reader.node(), Eq(ring->entry_child(index)));
-      ASSERT_THAT(reader.consumed(), Eq(consumed));
-      ASSERT_THAT(reader.remaining(), Eq(remaining));
-    }
-  }
-
-  CordRep::Unref(ring);
-}
-
-TEST(CordRingReaderTest, SeekBackward) {
-  CordRepRingReader reader;
-  auto flats = TestFlats();
-  CordRepRing* ring = FromFlats(flats);
-  CordRepRing::index_type head = ring->head();
-
-  reader.Reset(ring);
-  size_t consumed = ring->length;
-  size_t remaining = 0;
-  for (int i = flats.size() - 1; i >= 0; --i) {
-    CordRepRing::index_type index = ring->advance(head, i);
-    size_t offset = consumed - flats[i].length();
-    for (int off = 0; off < flats[i].length(); ++off) {
-      absl::string_view chunk = reader.Seek(offset + off);
-      ASSERT_THAT(chunk, Eq(flats[i].substr(off)));
-      ASSERT_THAT(reader.index(), Eq(index));
-      ASSERT_THAT(reader.node(), Eq(ring->entry_child(index)));
-      ASSERT_THAT(reader.consumed(), Eq(consumed));
-      ASSERT_THAT(reader.remaining(), Eq(remaining));
-    }
-    consumed -= flats[i].length();
-    remaining += flats[i].length();
-  }
-#ifndef NDEBUG
-  EXPECT_DEATH_IF_SUPPORTED(reader.Seek(ring->length), ".*");
-#endif
-  CordRep::Unref(ring);
-}
-
-}  // namespace
-}  // namespace cord_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/absl/strings/cord_ring_test.cc b/absl/strings/cord_ring_test.cc
deleted file mode 100644
index f39a0a4..0000000
--- a/absl/strings/cord_ring_test.cc
+++ /dev/null
@@ -1,1454 +0,0 @@
-// Copyright 2020 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 <cstdlib>
-#include <ctime>
-#include <memory>
-#include <random>
-#include <sstream>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/base/config.h"
-#include "absl/base/internal/raw_logging.h"
-#include "absl/base/macros.h"
-#include "absl/debugging/leak_check.h"
-#include "absl/strings/internal/cord_internal.h"
-#include "absl/strings/internal/cord_rep_ring.h"
-#include "absl/strings/str_cat.h"
-#include "absl/strings/string_view.h"
-
-extern thread_local bool cord_ring;
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace {
-
-using RandomEngine = std::mt19937_64;
-
-using ::absl::cord_internal::CordRep;
-using ::absl::cord_internal::CordRepConcat;
-using ::absl::cord_internal::CordRepExternal;
-using ::absl::cord_internal::CordRepFlat;
-using ::absl::cord_internal::CordRepRing;
-using ::absl::cord_internal::CordRepSubstring;
-
-using ::absl::cord_internal::EXTERNAL;
-using ::absl::cord_internal::SUBSTRING;
-
-using testing::ElementsAre;
-using testing::ElementsAreArray;
-using testing::Eq;
-using testing::Ge;
-using testing::Le;
-using testing::Lt;
-using testing::Ne;
-using testing::SizeIs;
-
-using index_type = CordRepRing::index_type;
-
-enum InputShareMode { kPrivate, kShared, kSharedIndirect };
-
-// TestParam class used by all test fixtures.
-// Not all fixtures use all possible input combinations
-struct TestParam {
-  TestParam() = default;
-  explicit TestParam(InputShareMode input_share_mode)
-      : input_share_mode(input_share_mode) {}
-
-  // Run the test with the 'rep under test' to be privately owned.
-  // Otherwise, the rep has a shared ref count of 2 or higher.
-  bool refcount_is_one = true;
-
-  // Run the test with the 'rep under test' being allocated with enough capacity
-  // to accommodate any modifications made to it. Otherwise, the rep has zero
-  // extra (reserve) capacity.
-  bool with_capacity = true;
-
-  // For test providing possibly shared input such as Append(.., CordpRep*),
-  // this field defines if that input is adopted with a refcount of one
-  // (privately owned / donated), or shared. For composite inputs such as
-  // 'substring of flat', we also have the 'shared indirect' value which means
-  // the top level node is not shared, but the contained child node is shared.
-  InputShareMode input_share_mode = kPrivate;
-
-  std::string ToString() const {
-    return absl::StrCat(refcount_is_one ? "Private" : "Shared",
-                        with_capacity ? "" : "_NoCapacity",
-                        (input_share_mode == kPrivate) ? ""
-                        : (input_share_mode == kShared)
-                            ? "_SharedInput"
-                            : "_IndirectSharedInput");
-  }
-};
-using TestParams = std::vector<TestParam>;
-
-// Matcher validating when mutable copies are required / performed.
-MATCHER_P2(EqIfPrivate, param, rep,
-           absl::StrCat("Equal 0x", absl::Hex(rep), " if private")) {
-  return param.refcount_is_one ? arg == rep : true;
-}
-
-// Matcher validating when mutable copies are required / performed.
-MATCHER_P2(EqIfPrivateAndCapacity, param, rep,
-           absl::StrCat("Equal 0x", absl::Hex(rep),
-                        " if private and capacity")) {
-  return (param.refcount_is_one && param.with_capacity) ? arg == rep : true;
-}
-
-// Matcher validating a shared ring was re-allocated. Should only be used for
-// tests doing exactly one update as subsequent updates could return the
-// original (freed and re-used) pointer.
-MATCHER_P2(NeIfShared, param, rep,
-           absl::StrCat("Not equal 0x", absl::Hex(rep), " if shared")) {
-  return param.refcount_is_one ? true : arg != rep;
-}
-
-MATCHER_P2(EqIfInputPrivate, param, rep, "Equal if input is private") {
-  return param.input_share_mode == kPrivate ? arg == rep : arg != rep;
-}
-
-// Matcher validating the core in-variants of the CordRepRing instance.
-MATCHER(IsValidRingBuffer, "RingBuffer is valid") {
-  std::stringstream ss;
-  if (!arg->IsValid(ss)) {
-    *result_listener << "\nERROR: " << ss.str() << "\nRING = " << *arg;
-    return false;
-  }
-  return true;
-}
-
-// Returns the flats contained in the provided CordRepRing
-std::vector<string_view> ToFlats(const CordRepRing* r) {
-  std::vector<string_view> flats;
-  flats.reserve(r->entries());
-  index_type pos = r->head();
-  do {
-    flats.push_back(r->entry_data(pos));
-  } while ((pos = r->advance(pos)) != r->tail());
-  return flats;
-}
-
-class not_a_string_view {
- public:
-  explicit not_a_string_view(absl::string_view s)
-      : data_(s.data()), size_(s.size()) {}
-  explicit not_a_string_view(const void* data, size_t size)
-      : data_(data), size_(size) {}
-
-  not_a_string_view remove_prefix(size_t n) const {
-    return not_a_string_view(static_cast<const char*>(data_) + n, size_ - n);
-  }
-
-  not_a_string_view remove_suffix(size_t n) const {
-    return not_a_string_view(data_, size_ - n);
-  }
-
-  const void* data() const { return data_; }
-  size_t size() const { return size_; }
-
- private:
-  const void* data_;
-  size_t size_;
-};
-
-bool operator==(not_a_string_view lhs, not_a_string_view rhs) {
-  return lhs.data() == rhs.data() && lhs.size() == rhs.size();
-}
-
-std::ostream& operator<<(std::ostream& s, not_a_string_view rhs) {
-  return s << "{ data: " << rhs.data() << " size: " << rhs.size() << "}";
-}
-
-std::vector<not_a_string_view> ToRawFlats(const CordRepRing* r) {
-  std::vector<not_a_string_view> flats;
-  flats.reserve(r->entries());
-  index_type pos = r->head();
-  do {
-    flats.emplace_back(r->entry_data(pos));
-  } while ((pos = r->advance(pos)) != r->tail());
-  return flats;
-}
-
-// Returns the value contained in the provided CordRepRing
-std::string ToString(const CordRepRing* r) {
-  std::string value;
-  value.reserve(r->length);
-  index_type pos = r->head();
-  do {
-    absl::string_view sv = r->entry_data(pos);
-    value.append(sv.data(), sv.size());
-  } while ((pos = r->advance(pos)) != r->tail());
-  return value;
-}
-
-// Creates a flat for testing
-CordRep* MakeFlat(absl::string_view s, size_t extra = 0) {
-  CordRepFlat* flat = CordRepFlat::New(s.length() + extra);
-  memcpy(flat->Data(), s.data(), s.length());
-  flat->length = s.length();
-  return flat;
-}
-
-// Creates an external node for testing
-CordRepExternal* MakeExternal(absl::string_view s) {
-  struct Rep : public CordRepExternal {
-    std::string s;
-    explicit Rep(absl::string_view s) : s(s) {
-      this->tag = EXTERNAL;
-      this->base = s.data();
-      this->length = s.length();
-      this->releaser_invoker = [](CordRepExternal* self) {
-        delete static_cast<Rep*>(self);
-      };
-    }
-  };
-  return new Rep(s);
-}
-
-CordRepExternal* MakeFakeExternal(size_t length) {
-  struct Rep : public CordRepExternal {
-    std::string s;
-    explicit Rep(size_t len) {
-      this->tag = EXTERNAL;
-      this->base = reinterpret_cast<const char*>(this->storage);
-      this->length = len;
-      this->releaser_invoker = [](CordRepExternal* self) {
-        delete static_cast<Rep*>(self);
-      };
-    }
-  };
-  return new Rep(length);
-}
-
-// Creates a flat or an external node for testing depending on the size.
-CordRep* MakeLeaf(absl::string_view s, size_t extra = 0) {
-  if (s.size() <= absl::cord_internal::kMaxFlatLength) {
-    return MakeFlat(s, extra);
-  } else {
-    return MakeExternal(s);
-  }
-}
-
-// Creates a substring node
-CordRepSubstring* MakeSubstring(size_t start, size_t len, CordRep* rep) {
-  auto* sub = new CordRepSubstring;
-  sub->tag = SUBSTRING;
-  sub->start = start;
-  sub->length = (len <= 0) ? rep->length - start + len : len;
-  sub->child = rep;
-  return sub;
-}
-
-// Creates a substring node removing the specified prefix
-CordRepSubstring* RemovePrefix(size_t start, CordRep* rep) {
-  return MakeSubstring(start, rep->length - start, rep);
-}
-
-// Creates a substring node removing the specified suffix
-CordRepSubstring* RemoveSuffix(size_t length, CordRep* rep) {
-  return MakeSubstring(0, rep->length - length, rep);
-}
-
-enum Composition { kMix, kAppend, kPrepend };
-
-Composition RandomComposition() {
-  RandomEngine rng(GTEST_FLAG_GET(random_seed));
-  return (rng() & 1) ? kMix : ((rng() & 1) ? kAppend : kPrepend);
-}
-
-absl::string_view ToString(Composition composition) {
-  switch (composition) {
-    case kAppend:
-      return "Append";
-    case kPrepend:
-      return "Prepend";
-    case kMix:
-      return "Mix";
-  }
-  assert(false);
-  return "???";
-}
-
-constexpr const char* kFox = "The quick brown fox jumps over the lazy dog";
-constexpr const char* kFoxFlats[] = {"The ", "quick ", "brown ",
-                                     "fox ", "jumps ", "over ",
-                                     "the ", "lazy ",  "dog"};
-
-CordRepRing* FromFlats(Span<const char* const> flats,
-                       Composition composition = kAppend) {
-  if (flats.empty()) return nullptr;
-  CordRepRing* ring = nullptr;
-  switch (composition) {
-    case kAppend:
-      ring = CordRepRing::Create(MakeLeaf(flats.front()), flats.size() - 1);
-      for (int i = 1; i < flats.size(); ++i) {
-        ring = CordRepRing::Append(ring, MakeLeaf(flats[i]));
-      }
-      break;
-    case kPrepend:
-      ring = CordRepRing::Create(MakeLeaf(flats.back()), flats.size() - 1);
-      for (int i = static_cast<int>(flats.size() - 2); i >= 0; --i) {
-        ring = CordRepRing::Prepend(ring, MakeLeaf(flats[i]));
-      }
-      break;
-    case kMix:
-      size_t middle1 = flats.size() / 2, middle2 = middle1;
-      ring = CordRepRing::Create(MakeLeaf(flats[middle1]), flats.size() - 1);
-      if (!flats.empty()) {
-        if ((flats.size() & 1) == 0) {
-          ring = CordRepRing::Prepend(ring, MakeLeaf(flats[--middle1]));
-        }
-        for (int i = 1; i <= middle1; ++i) {
-          ring = CordRepRing::Prepend(ring, MakeLeaf(flats[middle1 - i]));
-          ring = CordRepRing::Append(ring, MakeLeaf(flats[middle2 + i]));
-        }
-      }
-      break;
-  }
-  EXPECT_THAT(ToFlats(ring), ElementsAreArray(flats));
-  return ring;
-}
-
-std::ostream& operator<<(std::ostream& s, const TestParam& param) {
-  return s << param.ToString();
-}
-
-std::string TestParamToString(const testing::TestParamInfo<TestParam>& info) {
-  return info.param.ToString();
-}
-
-class CordRingTest : public testing::Test {
- public:
-  ~CordRingTest() override {
-    for (CordRep* rep : unrefs_) {
-      CordRep::Unref(rep);
-    }
-  }
-
-  template <typename CordRepType>
-  CordRepType* NeedsUnref(CordRepType* rep) {
-    assert(rep);
-    unrefs_.push_back(rep);
-    return rep;
-  }
-
-  template <typename CordRepType>
-  CordRepType* Ref(CordRepType* rep) {
-    CordRep::Ref(rep);
-    return NeedsUnref(rep);
-  }
-
- private:
-  std::vector<CordRep*> unrefs_;
-};
-
-class CordRingTestWithParam : public testing::TestWithParam<TestParam> {
- public:
-  ~CordRingTestWithParam() override {
-    for (CordRep* rep : unrefs_) {
-      CordRep::Unref(rep);
-    }
-  }
-
-  CordRepRing* CreateWithCapacity(CordRep* child, size_t extra_capacity) {
-    if (!GetParam().with_capacity) extra_capacity = 0;
-    CordRepRing* ring = CordRepRing::Create(child, extra_capacity);
-    ring->SetCapacityForTesting(1 + extra_capacity);
-    return RefIfShared(ring);
-  }
-
-  bool Shared() const { return !GetParam().refcount_is_one; }
-  bool InputShared() const { return GetParam().input_share_mode == kShared; }
-  bool InputSharedIndirect() const {
-    return GetParam().input_share_mode == kSharedIndirect;
-  }
-
-  template <typename CordRepType>
-  CordRepType* NeedsUnref(CordRepType* rep) {
-    assert(rep);
-    unrefs_.push_back(rep);
-    return rep;
-  }
-
-  template <typename CordRepType>
-  CordRepType* Ref(CordRepType* rep) {
-    CordRep::Ref(rep);
-    return NeedsUnref(rep);
-  }
-
-  template <typename CordRepType>
-  CordRepType* RefIfShared(CordRepType* rep) {
-    return Shared() ? Ref(rep) : rep;
-  }
-
-  template <typename CordRepType>
-  CordRepType* RefIfInputShared(CordRepType* rep) {
-    return InputShared() ? Ref(rep) : rep;
-  }
-
-  template <typename CordRepType>
-  CordRepType* RefIfInputSharedIndirect(CordRepType* rep) {
-    return InputSharedIndirect() ? Ref(rep) : rep;
-  }
-
- private:
-  std::vector<CordRep*> unrefs_;
-};
-
-class CordRingCreateTest : public CordRingTestWithParam {
- public:
-  static TestParams CreateTestParams() {
-    TestParams params;
-    params.emplace_back(InputShareMode::kPrivate);
-    params.emplace_back(InputShareMode::kShared);
-    return params;
-  }
-};
-
-class CordRingSubTest : public CordRingTestWithParam {
- public:
-  static TestParams CreateTestParams() {
-    TestParams params;
-    for (bool refcount_is_one : {true, false}) {
-      TestParam param;
-      param.refcount_is_one = refcount_is_one;
-      params.push_back(param);
-    }
-    return params;
-  }
-};
-
-class CordRingBuildTest : public CordRingTestWithParam {
- public:
-  static TestParams CreateTestParams() {
-    TestParams params;
-    for (bool refcount_is_one : {true, false}) {
-      for (bool with_capacity : {true, false}) {
-        TestParam param;
-        param.refcount_is_one = refcount_is_one;
-        param.with_capacity = with_capacity;
-        params.push_back(param);
-      }
-    }
-    return params;
-  }
-};
-
-class CordRingCreateFromTreeTest : public CordRingTestWithParam {
- public:
-  static TestParams CreateTestParams() {
-    TestParams params;
-    params.emplace_back(InputShareMode::kPrivate);
-    params.emplace_back(InputShareMode::kShared);
-    params.emplace_back(InputShareMode::kSharedIndirect);
-    return params;
-  }
-};
-
-class CordRingBuildInputTest : public CordRingTestWithParam {
- public:
-  static TestParams CreateTestParams() {
-    TestParams params;
-    for (bool refcount_is_one : {true, false}) {
-      for (bool with_capacity : {true, false}) {
-        for (InputShareMode share_mode : {kPrivate, kShared, kSharedIndirect}) {
-          TestParam param;
-          param.refcount_is_one = refcount_is_one;
-          param.with_capacity = with_capacity;
-          param.input_share_mode = share_mode;
-          params.push_back(param);
-        }
-      }
-    }
-    return params;
-  }
-};
-
-INSTANTIATE_TEST_SUITE_P(WithParam, CordRingSubTest,
-                         testing::ValuesIn(CordRingSubTest::CreateTestParams()),
-                         TestParamToString);
-
-INSTANTIATE_TEST_SUITE_P(
-    WithParam, CordRingCreateTest,
-    testing::ValuesIn(CordRingCreateTest::CreateTestParams()),
-    TestParamToString);
-
-INSTANTIATE_TEST_SUITE_P(
-    WithParam, CordRingCreateFromTreeTest,
-    testing::ValuesIn(CordRingCreateFromTreeTest::CreateTestParams()),
-    TestParamToString);
-
-INSTANTIATE_TEST_SUITE_P(
-    WithParam, CordRingBuildTest,
-    testing::ValuesIn(CordRingBuildTest::CreateTestParams()),
-    TestParamToString);
-
-INSTANTIATE_TEST_SUITE_P(
-    WithParam, CordRingBuildInputTest,
-    testing::ValuesIn(CordRingBuildInputTest::CreateTestParams()),
-    TestParamToString);
-
-TEST_P(CordRingCreateTest, CreateFromFlat) {
-  absl::string_view str1 = "abcdefghijklmnopqrstuvwxyz";
-  CordRepRing* result = NeedsUnref(CordRepRing::Create(MakeFlat(str1)));
-  ASSERT_THAT(result, IsValidRingBuffer());
-  EXPECT_THAT(result->length, Eq(str1.size()));
-  EXPECT_THAT(ToFlats(result), ElementsAre(str1));
-}
-
-TEST_P(CordRingCreateTest, CreateFromRing) {
-  CordRepRing* ring = RefIfShared(FromFlats(kFoxFlats));
-  CordRepRing* result = NeedsUnref(CordRepRing::Create(ring));
-  ASSERT_THAT(result, IsValidRingBuffer());
-  EXPECT_THAT(result, EqIfPrivate(GetParam(), ring));
-  EXPECT_THAT(result, NeIfShared(GetParam(), ring));
-  EXPECT_THAT(ToFlats(result), ElementsAreArray(kFoxFlats));
-}
-
-TEST_P(CordRingCreateFromTreeTest, CreateFromSubstringRing) {
-  CordRepRing* ring = RefIfInputSharedIndirect(FromFlats(kFoxFlats));
-  CordRep* sub = RefIfInputShared(MakeSubstring(2, 11, ring));
-  CordRepRing* result = NeedsUnref(CordRepRing::Create(sub));
-  ASSERT_THAT(result, IsValidRingBuffer());
-  EXPECT_THAT(result, EqIfInputPrivate(GetParam(), ring));
-  EXPECT_THAT(ToString(result), string_view(kFox).substr(2, 11));
-}
-
-TEST_F(CordRingTest, CreateWithIllegalExtraCapacity) {
-#if defined(ABSL_HAVE_EXCEPTIONS)
-  CordRep* flat = NeedsUnref(MakeFlat("Hello world"));
-  try {
-    CordRepRing::Create(flat, CordRepRing::kMaxCapacity);
-    GTEST_FAIL() << "expected std::length_error exception";
-  } catch (const std::length_error&) {
-  }
-#elif defined(GTEST_HAS_DEATH_TEST)
-  CordRep* flat = NeedsUnref(MakeFlat("Hello world"));
-  EXPECT_DEATH(CordRepRing::Create(flat, CordRepRing::kMaxCapacity), ".*");
-#endif
-}
-
-TEST_P(CordRingCreateFromTreeTest, CreateFromSubstringOfFlat) {
-  absl::string_view str1 = "abcdefghijklmnopqrstuvwxyz";
-  auto* flat = RefIfInputShared(MakeFlat(str1));
-  auto* child = RefIfInputSharedIndirect(MakeSubstring(4, 20, flat));
-  CordRepRing* result = NeedsUnref(CordRepRing::Create(child));
-  ASSERT_THAT(result, IsValidRingBuffer());
-  EXPECT_THAT(result->length, Eq(20));
-  EXPECT_THAT(ToFlats(result), ElementsAre(str1.substr(4, 20)));
-}
-
-TEST_P(CordRingCreateTest, CreateFromExternal) {
-  absl::string_view str1 = "abcdefghijklmnopqrstuvwxyz";
-  auto* child = RefIfInputShared(MakeExternal(str1));
-  CordRepRing* result = NeedsUnref(CordRepRing::Create(child));
-  ASSERT_THAT(result, IsValidRingBuffer());
-  EXPECT_THAT(result->length, Eq(str1.size()));
-  EXPECT_THAT(ToFlats(result), ElementsAre(str1));
-}
-
-TEST_P(CordRingCreateFromTreeTest, CreateFromSubstringOfExternal) {
-  absl::string_view str1 = "abcdefghijklmnopqrstuvwxyz";
-  auto* external = RefIfInputShared(MakeExternal(str1));
-  auto* child = RefIfInputSharedIndirect(MakeSubstring(1, 24, external));
-  CordRepRing* result = NeedsUnref(CordRepRing::Create(child));
-  ASSERT_THAT(result, IsValidRingBuffer());
-  EXPECT_THAT(result->length, Eq(24));
-  EXPECT_THAT(ToFlats(result), ElementsAre(str1.substr(1, 24)));
-}
-
-TEST_P(CordRingCreateFromTreeTest, CreateFromSubstringOfLargeExternal) {
-  auto* external = RefIfInputShared(MakeFakeExternal(1 << 20));
-  auto str = not_a_string_view(external->base, 1 << 20)
-                 .remove_prefix(1 << 19)
-                 .remove_suffix(6);
-  auto* child =
-      RefIfInputSharedIndirect(MakeSubstring(1 << 19, (1 << 19) - 6, external));
-  CordRepRing* result = NeedsUnref(CordRepRing::Create(child));
-  ASSERT_THAT(result, IsValidRingBuffer());
-  EXPECT_THAT(result->length, Eq(str.size()));
-  EXPECT_THAT(ToRawFlats(result), ElementsAre(str));
-}
-
-TEST_P(CordRingCreateTest, Properties) {
-  absl::string_view str1 = "abcdefghijklmnopqrstuvwxyz";
-  CordRepRing* result = NeedsUnref(CordRepRing::Create(MakeFlat(str1), 120));
-  ASSERT_THAT(result, IsValidRingBuffer());
-  EXPECT_THAT(result->head(), Eq(0));
-  EXPECT_THAT(result->tail(), Eq(1));
-  EXPECT_THAT(result->capacity(), Ge(120 + 1));
-  EXPECT_THAT(result->capacity(), Le(2 * 120 + 1));
-  EXPECT_THAT(result->entries(), Eq(1));
-  EXPECT_THAT(result->begin_pos(), Eq(0));
-}
-
-TEST_P(CordRingCreateTest, EntryForNewFlat) {
-  absl::string_view str1 = "abcdefghijklmnopqrstuvwxyz";
-  CordRep* child = MakeFlat(str1);
-  CordRepRing* result = NeedsUnref(CordRepRing::Create(child, 120));
-  ASSERT_THAT(result, IsValidRingBuffer());
-  EXPECT_THAT(result->entry_child(0), Eq(child));
-  EXPECT_THAT(result->entry_end_pos(0), Eq(str1.length()));
-  EXPECT_THAT(result->entry_data_offset(0), Eq(0));
-}
-
-TEST_P(CordRingCreateTest, EntryForNewFlatSubstring) {
-  absl::string_view str1 = "1234567890abcdefghijklmnopqrstuvwxyz";
-  CordRep* child = MakeFlat(str1);
-  CordRep* substring = MakeSubstring(10, 26, child);
-  CordRepRing* result = NeedsUnref(CordRepRing::Create(substring, 1));
-  ASSERT_THAT(result, IsValidRingBuffer());
-  EXPECT_THAT(result->entry_child(0), Eq(child));
-  EXPECT_THAT(result->entry_end_pos(0), Eq(26));
-  EXPECT_THAT(result->entry_data_offset(0), Eq(10));
-}
-
-TEST_P(CordRingBuildTest, AppendFlat) {
-  absl::string_view str1 = "abcdefghijklmnopqrstuvwxyz";
-  absl::string_view str2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
-  CordRepRing* ring = CreateWithCapacity(MakeExternal(str1), 1);
-  CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, MakeFlat(str2)));
-  ASSERT_THAT(result, IsValidRingBuffer());
-  EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
-  EXPECT_THAT(result, NeIfShared(GetParam(), ring));
-  EXPECT_THAT(result->length, Eq(str1.size() + str2.size()));
-  EXPECT_THAT(ToFlats(result), ElementsAre(str1, str2));
-}
-
-TEST_P(CordRingBuildTest, PrependFlat) {
-  absl::string_view str1 = "abcdefghijklmnopqrstuvwxyz";
-  absl::string_view str2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
-  CordRepRing* ring = CreateWithCapacity(MakeExternal(str1), 1);
-  CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, MakeFlat(str2)));
-  ASSERT_THAT(result, IsValidRingBuffer());
-  EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
-  EXPECT_THAT(result, NeIfShared(GetParam(), ring));
-  EXPECT_THAT(result->length, Eq(str1.size() + str2.size()));
-  EXPECT_THAT(ToFlats(result), ElementsAre(str2, str1));
-}
-
-TEST_P(CordRingBuildTest, AppendString) {
-  absl::string_view str1 = "abcdefghijklmnopqrstuvwxyz";
-  absl::string_view str2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
-  CordRepRing* ring = CreateWithCapacity(MakeExternal(str1), 1);
-  CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, str2));
-  ASSERT_THAT(result, IsValidRingBuffer());
-  EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
-  EXPECT_THAT(result, NeIfShared(GetParam(), ring));
-  EXPECT_THAT(result->length, Eq(str1.size() + str2.size()));
-  EXPECT_THAT(ToFlats(result), ElementsAre(str1, str2));
-}
-
-TEST_P(CordRingBuildTest, AppendStringHavingExtra) {
-  absl::string_view str1 = "1234";
-  absl::string_view str2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
-  CordRepRing* ring = CreateWithCapacity(MakeFlat(str1, 26), 0);
-  CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, str2));
-  ASSERT_THAT(result, IsValidRingBuffer());
-  EXPECT_THAT(result->length, Eq(str1.size() + str2.size()));
-  EXPECT_THAT(result, EqIfPrivate(GetParam(), ring));
-  EXPECT_THAT(result, NeIfShared(GetParam(), ring));
-}
-
-TEST_P(CordRingBuildTest, AppendStringHavingPartialExtra) {
-  absl::string_view str1 = "1234";
-  absl::string_view str2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
-
-  // Create flat with at least one extra byte. We don't expect to have sized
-  // alloc and capacity rounding to grant us enough to not make it partial.
-  auto* flat = MakeFlat(str1, 1);
-  size_t avail = flat->flat()->Capacity() - flat->length;
-  ASSERT_THAT(avail, Lt(str2.size())) << " adjust test for larger flats!";
-
-  // Construct the flats we do expect using all of `avail`.
-  absl::string_view str1a = str2.substr(0, avail);
-  absl::string_view str2a = str2.substr(avail);
-
-  CordRepRing* ring = CreateWithCapacity(flat, 1);
-  CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, str2));
-  ASSERT_THAT(result, IsValidRingBuffer());
-  EXPECT_THAT(result->length, Eq(str1.size() + str2.size()));
-  EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
-  EXPECT_THAT(result, NeIfShared(GetParam(), ring));
-  if (GetParam().refcount_is_one) {
-    EXPECT_THAT(ToFlats(result), ElementsAre(StrCat(str1, str1a), str2a));
-  } else {
-    EXPECT_THAT(ToFlats(result), ElementsAre(str1, str2));
-  }
-}
-
-TEST_P(CordRingBuildTest, AppendStringHavingExtraInSubstring) {
-  absl::string_view str1 = "123456789_1234";
-  absl::string_view str2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
-  CordRep* flat = RemovePrefix(10, MakeFlat(str1, 26));
-  CordRepRing* ring = CreateWithCapacity(flat, 0);
-  CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, str2));
-  ASSERT_THAT(result, IsValidRingBuffer());
-  EXPECT_THAT(result, EqIfPrivate(GetParam(), ring));
-  EXPECT_THAT(result, NeIfShared(GetParam(), ring));
-  EXPECT_THAT(result->length, Eq(4 + str2.size()));
-  if (GetParam().refcount_is_one) {
-    EXPECT_THAT(ToFlats(result), ElementsAre(StrCat("1234", str2)));
-  } else {
-    EXPECT_THAT(ToFlats(result), ElementsAre("1234", str2));
-  }
-}
-
-TEST_P(CordRingBuildTest, AppendStringHavingSharedExtra) {
-  absl::string_view str1 = "123456789_1234";
-  absl::string_view str2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
-  for (int shared_type = 0; shared_type < 2; ++shared_type) {
-    SCOPED_TRACE(absl::StrCat("Shared extra type ", shared_type));
-
-    // Create a flat that is shared in some way.
-    CordRep* flat = nullptr;
-    CordRep* flat1 = nullptr;
-    if (shared_type == 0) {
-      // Shared flat
-      flat = CordRep::Ref(MakeFlat(str1.substr(10), 100));
-    } else if (shared_type == 1) {
-      // Shared flat inside private substring
-      flat1 = CordRep::Ref(MakeFlat(str1));
-      flat = RemovePrefix(10, flat1);
-    } else {
-      // Private flat inside shared substring
-      flat = CordRep::Ref(RemovePrefix(10, MakeFlat(str1, 100)));
-    }
-
-    CordRepRing* ring = CreateWithCapacity(flat, 1);
-    CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, str2));
-    ASSERT_THAT(result, IsValidRingBuffer());
-    EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
-    EXPECT_THAT(result, NeIfShared(GetParam(), ring));
-    EXPECT_THAT(result->length, Eq(4 + str2.size()));
-    EXPECT_THAT(ToFlats(result), ElementsAre("1234", str2));
-
-    CordRep::Unref(shared_type == 1 ? flat1 : flat);
-  }
-}
-
-TEST_P(CordRingBuildTest, AppendStringWithExtra) {
-  absl::string_view str1 = "1234";
-  absl::string_view str2 = "1234567890";
-  absl::string_view str3 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
-  CordRepRing* ring = CreateWithCapacity(MakeExternal(str1), 1);
-  CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, str2, 26));
-  result = CordRepRing::Append(result, str3);
-  ASSERT_THAT(result, IsValidRingBuffer());
-  EXPECT_THAT(result->length, Eq(str1.size() + str2.size() + str3.size()));
-  EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
-  EXPECT_THAT(ToFlats(result), ElementsAre(str1, StrCat(str2, str3)));
-}
-
-TEST_P(CordRingBuildTest, PrependString) {
-  absl::string_view str1 = "abcdefghijklmnopqrstuvwxyz";
-  absl::string_view str2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
-  // Use external rep to avoid appending to first flat
-  CordRepRing* ring = CreateWithCapacity(MakeExternal(str1), 1);
-  CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, str2));
-  ASSERT_THAT(result, IsValidRingBuffer());
-  if (GetParam().with_capacity && GetParam().refcount_is_one) {
-    EXPECT_THAT(result, Eq(ring));
-  } else {
-    EXPECT_THAT(result, Ne(ring));
-  }
-  EXPECT_THAT(result->length, Eq(str1.size() + str2.size()));
-  EXPECT_THAT(ToFlats(result), ElementsAre(str2, str1));
-}
-
-TEST_P(CordRingBuildTest, PrependStringHavingExtra) {
-  absl::string_view str1 = "abcdefghijklmnopqrstuvwxyz1234";
-  absl::string_view str2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
-  CordRep* flat = RemovePrefix(26, MakeFlat(str1));
-  CordRepRing* ring = CreateWithCapacity(flat, 0);
-  CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, str2));
-  ASSERT_THAT(result, IsValidRingBuffer());
-  EXPECT_THAT(result, EqIfPrivate(GetParam(), ring));
-  EXPECT_THAT(result, NeIfShared(GetParam(), ring));
-  EXPECT_THAT(result->length, Eq(4 + str2.size()));
-  if (GetParam().refcount_is_one) {
-    EXPECT_THAT(ToFlats(result), ElementsAre(StrCat(str2, "1234")));
-  } else {
-    EXPECT_THAT(ToFlats(result), ElementsAre(str2, "1234"));
-  }
-}
-
-TEST_P(CordRingBuildTest, PrependStringHavingSharedExtra) {
-  absl::string_view str1 = "123456789_ABCDEFGHIJKLMNOPQRSTUVWXYZ";
-  absl::string_view str2 = "abcdefghij";
-  absl::string_view str1a = str1.substr(10);
-  for (int shared_type = 1; shared_type < 2; ++shared_type) {
-    SCOPED_TRACE(absl::StrCat("Shared extra type ", shared_type));
-
-    // Create a flat that is shared in some way.
-    CordRep* flat = nullptr;
-    CordRep* flat1 = nullptr;
-    if (shared_type == 1) {
-      // Shared flat inside private substring
-      flat = RemovePrefix(10, flat1 = CordRep::Ref(MakeFlat(str1)));
-    } else {
-      // Private flat inside shared substring
-      flat = CordRep::Ref(RemovePrefix(10, MakeFlat(str1, 100)));
-    }
-
-    CordRepRing* ring = CreateWithCapacity(flat, 1);
-    CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, str2));
-    ASSERT_THAT(result, IsValidRingBuffer());
-    EXPECT_THAT(result->length, Eq(str1a.size() + str2.size()));
-    EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
-    EXPECT_THAT(result, NeIfShared(GetParam(), ring));
-    EXPECT_THAT(ToFlats(result), ElementsAre(str2, str1a));
-    CordRep::Unref(shared_type == 1 ? flat1 : flat);
-  }
-}
-
-TEST_P(CordRingBuildTest, PrependStringWithExtra) {
-  absl::string_view str1 = "1234";
-  absl::string_view str2 = "1234567890";
-  absl::string_view str3 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
-  CordRepRing* ring = CreateWithCapacity(MakeExternal(str1), 1);
-  CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, str2, 26));
-  ASSERT_THAT(result, IsValidRingBuffer());
-  result = CordRepRing::Prepend(result, str3);
-  EXPECT_THAT(result->length, Eq(str1.size() + str2.size() + str3.size()));
-  EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
-  EXPECT_THAT(ToFlats(result), ElementsAre(StrCat(str3, str2), str1));
-}
-
-TEST_P(CordRingBuildTest, AppendPrependStringMix) {
-  const auto& flats = kFoxFlats;
-  CordRepRing* ring = CreateWithCapacity(MakeFlat(flats[4]), 8);
-  CordRepRing* result = ring;
-  for (int i = 1; i <= 4; ++i) {
-    result = CordRepRing::Prepend(result, flats[4 - i]);
-    result = CordRepRing::Append(result, flats[4 + i]);
-  }
-  NeedsUnref(result);
-  ASSERT_THAT(result, IsValidRingBuffer());
-  EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
-  EXPECT_THAT(ToString(result), kFox);
-}
-
-TEST_P(CordRingBuildTest, AppendPrependStringMixWithExtra) {
-  const auto& flats = kFoxFlats;
-  CordRepRing* ring = CreateWithCapacity(MakeFlat(flats[4], 100), 8);
-  CordRepRing* result = ring;
-  for (int i = 1; i <= 4; ++i) {
-    result = CordRepRing::Prepend(result, flats[4 - i], 100);
-    result = CordRepRing::Append(result, flats[4 + i], 100);
-  }
-  NeedsUnref(result);
-  ASSERT_THAT(result, IsValidRingBuffer());
-  EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
-  if (GetParam().refcount_is_one) {
-    EXPECT_THAT(ToFlats(result),
-                ElementsAre("The quick brown fox ", "jumps over the lazy dog"));
-  } else {
-    EXPECT_THAT(ToFlats(result), ElementsAre("The quick brown fox ", "jumps ",
-                                             "over the lazy dog"));
-  }
-}
-
-TEST_P(CordRingBuildTest, AppendPrependStringMixWithPrependedExtra) {
-  const auto& flats = kFoxFlats;
-  CordRep* flat = MakeFlat(StrCat(std::string(50, '.'), flats[4]), 50);
-  CordRepRing* ring = CreateWithCapacity(RemovePrefix(50, flat), 0);
-  CordRepRing* result = ring;
-  for (int i = 1; i <= 4; ++i) {
-    result = CordRepRing::Prepend(result, flats[4 - i], 100);
-    result = CordRepRing::Append(result, flats[4 + i], 100);
-  }
-  result = NeedsUnref(result);
-  ASSERT_THAT(result, IsValidRingBuffer());
-  EXPECT_THAT(result, EqIfPrivate(GetParam(), ring));
-  if (GetParam().refcount_is_one) {
-    EXPECT_THAT(ToFlats(result), ElementsAre(kFox));
-  } else {
-    EXPECT_THAT(ToFlats(result), ElementsAre("The quick brown fox ", "jumps ",
-                                             "over the lazy dog"));
-  }
-}
-
-TEST_P(CordRingSubTest, SubRing) {
-  auto composition = RandomComposition();
-  SCOPED_TRACE(ToString(composition));
-  auto flats = MakeSpan(kFoxFlats);
-  string_view all = kFox;
-  for (size_t offset = 0; offset < all.size() - 1; ++offset) {
-    CordRepRing* ring = RefIfShared(FromFlats(flats, composition));
-    CordRepRing* result = CordRepRing::SubRing(ring, offset, 0);
-    EXPECT_THAT(result, nullptr);
-
-    for (size_t len = 1; len < all.size() - offset; ++len) {
-      ring = RefIfShared(FromFlats(flats, composition));
-      result = NeedsUnref(CordRepRing::SubRing(ring, offset, len));
-      ASSERT_THAT(result, IsValidRingBuffer());
-      ASSERT_THAT(result, EqIfPrivate(GetParam(), ring));
-      ASSERT_THAT(result, NeIfShared(GetParam(), ring));
-      ASSERT_THAT(ToString(result), Eq(all.substr(offset, len)));
-    }
-  }
-}
-
-TEST_P(CordRingSubTest, SubRingFromLargeExternal) {
-  auto composition = RandomComposition();
-  std::string large_string(1 << 20, '.');
-  const char* flats[] = {
-      "abcdefghijklmnopqrstuvwxyz",
-      large_string.c_str(),
-      "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
-  };
-  std::string buffer = absl::StrCat(flats[0], flats[1], flats[2]);
-  absl::string_view all = buffer;
-  for (size_t offset = 0; offset < 30; ++offset) {
-    CordRepRing* ring = RefIfShared(FromFlats(flats, composition));
-    CordRepRing* result = CordRepRing::SubRing(ring, offset, 0);
-    EXPECT_THAT(result, nullptr);
-
-    for (size_t len = all.size() - 30; len < all.size() - offset; ++len) {
-      ring = RefIfShared(FromFlats(flats, composition));
-      result = NeedsUnref(CordRepRing::SubRing(ring, offset, len));
-      ASSERT_THAT(result, IsValidRingBuffer());
-      ASSERT_THAT(result, EqIfPrivate(GetParam(), ring));
-      ASSERT_THAT(result, NeIfShared(GetParam(), ring));
-      auto str = ToString(result);
-      ASSERT_THAT(str, SizeIs(len));
-      ASSERT_THAT(str, Eq(all.substr(offset, len)));
-    }
-  }
-}
-
-TEST_P(CordRingSubTest, RemovePrefix) {
-  auto composition = RandomComposition();
-  SCOPED_TRACE(ToString(composition));
-  auto flats = MakeSpan(kFoxFlats);
-  string_view all = kFox;
-  CordRepRing* ring = RefIfShared(FromFlats(flats, composition));
-  CordRepRing* result = CordRepRing::RemovePrefix(ring, all.size());
-  EXPECT_THAT(result, nullptr);
-
-  for (size_t len = 1; len < all.size(); ++len) {
-    ring = RefIfShared(FromFlats(flats, composition));
-    result = NeedsUnref(CordRepRing::RemovePrefix(ring, len));
-    ASSERT_THAT(result, IsValidRingBuffer());
-    EXPECT_THAT(result, EqIfPrivate(GetParam(), ring));
-    ASSERT_THAT(result, NeIfShared(GetParam(), ring));
-    EXPECT_THAT(ToString(result), Eq(all.substr(len)));
-  }
-}
-
-TEST_P(CordRingSubTest, RemovePrefixFromLargeExternal) {
-  CordRepExternal* external1 = MakeFakeExternal(1 << 20);
-  CordRepExternal* external2 = MakeFakeExternal(1 << 20);
-  CordRepRing* ring = CordRepRing::Create(external1, 1);
-  ring = CordRepRing::Append(ring, external2);
-  CordRepRing* result = NeedsUnref(CordRepRing::RemovePrefix(ring, 1 << 16));
-  EXPECT_THAT(
-      ToRawFlats(result),
-      ElementsAre(
-          not_a_string_view(external1->base, 1 << 20).remove_prefix(1 << 16),
-          not_a_string_view(external2->base, 1 << 20)));
-}
-
-TEST_P(CordRingSubTest, RemoveSuffix) {
-  auto composition = RandomComposition();
-  SCOPED_TRACE(ToString(composition));
-  auto flats = MakeSpan(kFoxFlats);
-  string_view all = kFox;
-  CordRepRing* ring = RefIfShared(FromFlats(flats, composition));
-  CordRepRing* result = CordRepRing::RemoveSuffix(ring, all.size());
-  EXPECT_THAT(result, nullptr);
-
-  for (size_t len = 1; len < all.size(); ++len) {
-    ring = RefIfShared(FromFlats(flats, composition));
-    result = NeedsUnref(CordRepRing::RemoveSuffix(ring, len));
-    ASSERT_THAT(result, IsValidRingBuffer());
-    ASSERT_THAT(result, EqIfPrivate(GetParam(), ring));
-    ASSERT_THAT(result, NeIfShared(GetParam(), ring));
-    ASSERT_THAT(ToString(result), Eq(all.substr(0, all.size() - len)));
-  }
-}
-
-TEST_P(CordRingSubTest, AppendRing) {
-  auto composition = RandomComposition();
-  SCOPED_TRACE(ToString(composition));
-  auto flats = MakeSpan(kFoxFlats).subspan(1);
-  CordRepRing* ring = CreateWithCapacity(MakeFlat(kFoxFlats[0]), flats.size());
-  CordRepRing* child = FromFlats(flats, composition);
-  CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, child));
-  ASSERT_THAT(result, IsValidRingBuffer());
-  EXPECT_THAT(result, EqIfPrivate(GetParam(), ring));
-  EXPECT_THAT(result, NeIfShared(GetParam(), ring));
-  EXPECT_THAT(ToFlats(result), ElementsAreArray(kFoxFlats));
-}
-
-TEST_P(CordRingBuildInputTest, AppendRingWithFlatOffset) {
-  auto composition = RandomComposition();
-  SCOPED_TRACE(ToString(composition));
-  auto flats = MakeSpan(kFoxFlats);
-  CordRepRing* ring = CreateWithCapacity(MakeFlat("Head"), flats.size());
-  CordRep* child = RefIfInputSharedIndirect(FromFlats(flats, composition));
-  CordRep* stripped = RemovePrefix(10, child);
-  CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, stripped));
-  ASSERT_THAT(result, IsValidRingBuffer());
-  EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
-  EXPECT_THAT(result, NeIfShared(GetParam(), ring));
-  EXPECT_THAT(ToFlats(result), ElementsAre("Head", "brown ", "fox ", "jumps ",
-                                           "over ", "the ", "lazy ", "dog"));
-}
-
-TEST_P(CordRingBuildInputTest, AppendRingWithBrokenOffset) {
-  auto composition = RandomComposition();
-  SCOPED_TRACE(ToString(composition));
-  auto flats = MakeSpan(kFoxFlats);
-  CordRepRing* ring = CreateWithCapacity(MakeFlat("Head"), flats.size());
-  CordRep* child = RefIfInputSharedIndirect(FromFlats(flats, composition));
-  CordRep* stripped = RemovePrefix(21, child);
-  CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, stripped));
-  ASSERT_THAT(result, IsValidRingBuffer());
-  EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
-  EXPECT_THAT(result, NeIfShared(GetParam(), ring));
-  EXPECT_THAT(ToFlats(result),
-              ElementsAre("Head", "umps ", "over ", "the ", "lazy ", "dog"));
-}
-
-TEST_P(CordRingBuildInputTest, AppendRingWithFlatLength) {
-  auto composition = RandomComposition();
-  SCOPED_TRACE(ToString(composition));
-  auto flats = MakeSpan(kFoxFlats);
-  CordRepRing* ring = CreateWithCapacity(MakeFlat("Head"), flats.size());
-  CordRep* child = RefIfInputSharedIndirect(FromFlats(flats, composition));
-  CordRep* stripped = RemoveSuffix(8, child);
-  CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, stripped));
-  ASSERT_THAT(result, IsValidRingBuffer());
-  EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
-  EXPECT_THAT(result, NeIfShared(GetParam(), ring));
-  EXPECT_THAT(ToFlats(result), ElementsAre("Head", "The ", "quick ", "brown ",
-                                           "fox ", "jumps ", "over ", "the "));
-}
-
-TEST_P(CordRingBuildTest, AppendRingWithBrokenFlatLength) {
-  auto composition = RandomComposition();
-  SCOPED_TRACE(ToString(composition));
-  auto flats = MakeSpan(kFoxFlats);
-  CordRepRing* ring = CreateWithCapacity(MakeFlat("Head"), flats.size());
-  CordRep* child = RefIfInputSharedIndirect(FromFlats(flats, composition));
-  CordRep* stripped = RemoveSuffix(15, child);
-  CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, stripped));
-  ASSERT_THAT(result, IsValidRingBuffer());
-  EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
-  EXPECT_THAT(result, NeIfShared(GetParam(), ring));
-  EXPECT_THAT(ToFlats(result), ElementsAre("Head", "The ", "quick ", "brown ",
-                                           "fox ", "jumps ", "ov"));
-}
-
-TEST_P(CordRingBuildTest, AppendRingMiddlePiece) {
-  auto composition = RandomComposition();
-  SCOPED_TRACE(ToString(composition));
-  auto flats = MakeSpan(kFoxFlats);
-  CordRepRing* ring = CreateWithCapacity(MakeFlat("Head"), flats.size());
-  CordRep* child = RefIfInputSharedIndirect(FromFlats(flats, composition));
-  CordRep* stripped = MakeSubstring(7, child->length - 27, child);
-  CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, stripped));
-  ASSERT_THAT(result, IsValidRingBuffer());
-  EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
-  EXPECT_THAT(result, NeIfShared(GetParam(), ring));
-  EXPECT_THAT(ToFlats(result),
-              ElementsAre("Head", "ck ", "brown ", "fox ", "jum"));
-}
-
-TEST_P(CordRingBuildTest, AppendRingSinglePiece) {
-  auto composition = RandomComposition();
-  SCOPED_TRACE(ToString(composition));
-  auto flats = MakeSpan(kFoxFlats);
-  CordRepRing* ring = CreateWithCapacity(MakeFlat("Head"), flats.size());
-  CordRep* child = RefIfInputSharedIndirect(FromFlats(flats, composition));
-  CordRep* stripped = RefIfInputShared(MakeSubstring(11, 3, child));
-  CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, stripped));
-  ASSERT_THAT(result, IsValidRingBuffer());
-  EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
-  EXPECT_THAT(result, NeIfShared(GetParam(), ring));
-  EXPECT_THAT(ToFlats(result), ElementsAre("Head", "row"));
-}
-
-TEST_P(CordRingBuildInputTest, AppendRingSinglePieceWithPrefix) {
-  auto composition = RandomComposition();
-  SCOPED_TRACE(ToString(composition));
-  auto flats = MakeSpan(kFoxFlats);
-  size_t extra_capacity = 1 + (GetParam().with_capacity ? flats.size() : 0);
-  CordRepRing* ring = CordRepRing::Create(MakeFlat("Head"), extra_capacity);
-  ring->SetCapacityForTesting(1 + extra_capacity);
-  ring = RefIfShared(CordRepRing::Prepend(ring, MakeFlat("Prepend")));
-  assert(ring->IsValid(std::cout));
-  CordRepRing* child = RefIfInputSharedIndirect(FromFlats(flats, composition));
-  CordRep* stripped = RefIfInputShared(MakeSubstring(11, 3, child));
-  CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, stripped));
-  ASSERT_THAT(result, IsValidRingBuffer());
-  EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
-  EXPECT_THAT(result, NeIfShared(GetParam(), ring));
-  EXPECT_THAT(ToFlats(result), ElementsAre("Prepend", "Head", "row"));
-}
-
-TEST_P(CordRingBuildInputTest, PrependRing) {
-  auto composition = RandomComposition();
-  SCOPED_TRACE(ToString(composition));
-  auto fox = MakeSpan(kFoxFlats);
-  auto flats = MakeSpan(fox).subspan(0, fox.size() - 1);
-  CordRepRing* ring = CreateWithCapacity(MakeFlat(fox.back()), flats.size());
-  CordRepRing* child = RefIfInputShared(FromFlats(flats, composition));
-  CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, child));
-  ASSERT_THAT(result, IsValidRingBuffer());
-  EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
-  EXPECT_THAT(result, NeIfShared(GetParam(), ring));
-  EXPECT_THAT(ToFlats(result), ElementsAreArray(kFoxFlats));
-}
-
-TEST_P(CordRingBuildInputTest, PrependRingWithFlatOffset) {
-  auto composition = RandomComposition();
-  SCOPED_TRACE(ToString(composition));
-  auto flats = MakeSpan(kFoxFlats);
-  CordRepRing* ring = CreateWithCapacity(MakeFlat("Tail"), flats.size());
-  CordRep* child = RefIfInputShared(FromFlats(flats, composition));
-  CordRep* stripped = RefIfInputSharedIndirect(RemovePrefix(10, child));
-  CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, stripped));
-  ASSERT_THAT(result, IsValidRingBuffer());
-  EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
-  EXPECT_THAT(result, NeIfShared(GetParam(), ring));
-  EXPECT_THAT(ToFlats(result), ElementsAre("brown ", "fox ", "jumps ", "over ",
-                                           "the ", "lazy ", "dog", "Tail"));
-}
-
-TEST_P(CordRingBuildInputTest, PrependRingWithBrokenOffset) {
-  auto composition = RandomComposition();
-  SCOPED_TRACE(ToString(composition));
-  auto flats = MakeSpan(kFoxFlats);
-  CordRepRing* ring = CreateWithCapacity(MakeFlat("Tail"), flats.size());
-  CordRep* child = RefIfInputShared(FromFlats(flats, composition));
-  CordRep* stripped = RefIfInputSharedIndirect(RemovePrefix(21, child));
-  CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, stripped));
-  EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
-  EXPECT_THAT(result, NeIfShared(GetParam(), ring));
-  EXPECT_THAT(ToFlats(result),
-              ElementsAre("umps ", "over ", "the ", "lazy ", "dog", "Tail"));
-}
-
-TEST_P(CordRingBuildInputTest, PrependRingWithFlatLength) {
-  auto composition = RandomComposition();
-  SCOPED_TRACE(ToString(composition));
-  auto flats = MakeSpan(kFoxFlats);
-  CordRepRing* ring = CreateWithCapacity(MakeFlat("Tail"), flats.size());
-  CordRep* child = RefIfInputShared(FromFlats(flats, composition));
-  CordRep* stripped = RefIfInputSharedIndirect(RemoveSuffix(8, child));
-  CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, stripped));
-  ASSERT_THAT(result, IsValidRingBuffer());
-  EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
-  EXPECT_THAT(result, NeIfShared(GetParam(), ring));
-  EXPECT_THAT(ToFlats(result), ElementsAre("The ", "quick ", "brown ", "fox ",
-                                           "jumps ", "over ", "the ", "Tail"));
-}
-
-TEST_P(CordRingBuildInputTest, PrependRingWithBrokenFlatLength) {
-  auto composition = RandomComposition();
-  SCOPED_TRACE(ToString(composition));
-  auto flats = MakeSpan(kFoxFlats);
-  CordRepRing* ring = CreateWithCapacity(MakeFlat("Tail"), flats.size());
-  CordRep* child = RefIfInputShared(FromFlats(flats, composition));
-  CordRep* stripped = RefIfInputSharedIndirect(RemoveSuffix(15, child));
-  CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, stripped));
-  ASSERT_THAT(result, IsValidRingBuffer());
-  EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
-  EXPECT_THAT(result, NeIfShared(GetParam(), ring));
-  EXPECT_THAT(ToFlats(result), ElementsAre("The ", "quick ", "brown ", "fox ",
-                                           "jumps ", "ov", "Tail"));
-}
-
-TEST_P(CordRingBuildInputTest, PrependRingMiddlePiece) {
-  auto composition = RandomComposition();
-  SCOPED_TRACE(ToString(composition));
-  auto flats = MakeSpan(kFoxFlats);
-  CordRepRing* ring = CreateWithCapacity(MakeFlat("Tail"), flats.size());
-  CordRep* child = RefIfInputShared(FromFlats(flats, composition));
-  CordRep* stripped =
-      RefIfInputSharedIndirect(MakeSubstring(7, child->length - 27, child));
-  CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, stripped));
-  ASSERT_THAT(result, IsValidRingBuffer());
-  EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
-  EXPECT_THAT(result, NeIfShared(GetParam(), ring));
-  EXPECT_THAT(ToFlats(result),
-              ElementsAre("ck ", "brown ", "fox ", "jum", "Tail"));
-}
-
-TEST_P(CordRingBuildInputTest, PrependRingSinglePiece) {
-  auto composition = RandomComposition();
-  SCOPED_TRACE(ToString(composition));
-  auto flats = MakeSpan(kFoxFlats);
-  CordRepRing* ring = CreateWithCapacity(MakeFlat("Tail"), flats.size());
-  CordRep* child = RefIfInputShared(FromFlats(flats, composition));
-  CordRep* stripped = RefIfInputSharedIndirect(MakeSubstring(11, 3, child));
-  CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, stripped));
-  ASSERT_THAT(result, IsValidRingBuffer());
-  EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
-  EXPECT_THAT(result, NeIfShared(GetParam(), ring));
-  EXPECT_THAT(ToFlats(result), ElementsAre("row", "Tail"));
-}
-
-TEST_P(CordRingBuildInputTest, PrependRingSinglePieceWithPrefix) {
-  auto composition = RandomComposition();
-  SCOPED_TRACE(ToString(composition));
-  auto flats = MakeSpan(kFoxFlats);
-  size_t extra_capacity = 1 + (GetParam().with_capacity ? flats.size() : 0);
-  CordRepRing* ring = CordRepRing::Create(MakeFlat("Tail"), extra_capacity);
-  ring->SetCapacityForTesting(1 + extra_capacity);
-  ring = RefIfShared(CordRepRing::Prepend(ring, MakeFlat("Prepend")));
-  CordRep* child = RefIfInputShared(FromFlats(flats, composition));
-  CordRep* stripped = RefIfInputSharedIndirect(MakeSubstring(11, 3, child));
-  CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, stripped));
-  ASSERT_THAT(result, IsValidRingBuffer());
-  EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
-  EXPECT_THAT(result, NeIfShared(GetParam(), ring));
-  EXPECT_THAT(ToFlats(result), ElementsAre("row", "Prepend", "Tail"));
-}
-
-TEST_F(CordRingTest, Find) {
-  constexpr const char* flats[] = {
-      "abcdefghij", "klmnopqrst", "uvwxyz",     "ABCDEFGHIJ",
-      "KLMNOPQRST", "UVWXYZ",     "1234567890", "~!@#$%^&*()_",
-      "+-=",        "[]\\{}|;':", ",/<>?",      "."};
-  auto composition = RandomComposition();
-  SCOPED_TRACE(ToString(composition));
-  CordRepRing* ring = NeedsUnref(FromFlats(flats, composition));
-  std::string value = ToString(ring);
-  for (int i = 0; i < value.length(); ++i) {
-    CordRepRing::Position found = ring->Find(i);
-    auto data = ring->entry_data(found.index);
-    ASSERT_THAT(found.offset, Lt(data.length()));
-    ASSERT_THAT(data[found.offset], Eq(value[i]));
-  }
-}
-
-TEST_F(CordRingTest, FindWithHint) {
-  constexpr const char* flats[] = {
-      "abcdefghij", "klmnopqrst", "uvwxyz",     "ABCDEFGHIJ",
-      "KLMNOPQRST", "UVWXYZ",     "1234567890", "~!@#$%^&*()_",
-      "+-=",        "[]\\{}|;':", ",/<>?",      "."};
-  auto composition = RandomComposition();
-  SCOPED_TRACE(ToString(composition));
-  CordRepRing* ring = NeedsUnref(FromFlats(flats, composition));
-  std::string value = ToString(ring);
-
-#if defined(GTEST_HAS_DEATH_TEST)
-  // Test hint beyond valid position
-  index_type head = ring->head();
-  EXPECT_DEBUG_DEATH(ring->Find(ring->advance(head), 0), ".*");
-  EXPECT_DEBUG_DEATH(ring->Find(ring->advance(head), 9), ".*");
-  EXPECT_DEBUG_DEATH(ring->Find(ring->advance(head, 3), 24), ".*");
-#endif
-
-  int flat_pos = 0;
-  size_t flat_offset = 0;
-  for (auto sflat : flats) {
-    string_view flat(sflat);
-    for (int offset = 0; offset < flat.length(); ++offset) {
-      for (int start = 0; start <= flat_pos; ++start) {
-        index_type hint = ring->advance(ring->head(), start);
-        CordRepRing::Position found = ring->Find(hint, flat_offset + offset);
-        ASSERT_THAT(found.index, Eq(ring->advance(ring->head(), flat_pos)));
-        ASSERT_THAT(found.offset, Eq(offset));
-      }
-    }
-    ++flat_pos;
-    flat_offset += flat.length();
-  }
-}
-
-TEST_F(CordRingTest, FindInLargeRing) {
-  constexpr const char* flats[] = {
-      "abcdefghij", "klmnopqrst", "uvwxyz",     "ABCDEFGHIJ",
-      "KLMNOPQRST", "UVWXYZ",     "1234567890", "~!@#$%^&*()_",
-      "+-=",        "[]\\{}|;':", ",/<>?",      "."};
-  auto composition = RandomComposition();
-  SCOPED_TRACE(ToString(composition));
-  CordRepRing* ring = FromFlats(flats, composition);
-  for (int i = 0; i < 13; ++i) {
-    ring = CordRepRing::Append(ring, FromFlats(flats, composition));
-  }
-  NeedsUnref(ring);
-  std::string value = ToString(ring);
-  for (int i = 0; i < value.length(); ++i) {
-    CordRepRing::Position pos = ring->Find(i);
-    auto data = ring->entry_data(pos.index);
-    ASSERT_THAT(pos.offset, Lt(data.length()));
-    ASSERT_THAT(data[pos.offset], Eq(value[i]));
-  }
-}
-
-TEST_F(CordRingTest, FindTail) {
-  constexpr const char* flats[] = {
-      "abcdefghij", "klmnopqrst", "uvwxyz",     "ABCDEFGHIJ",
-      "KLMNOPQRST", "UVWXYZ",     "1234567890", "~!@#$%^&*()_",
-      "+-=",        "[]\\{}|;':", ",/<>?",      "."};
-  auto composition = RandomComposition();
-  SCOPED_TRACE(ToString(composition));
-  CordRepRing* ring = NeedsUnref(FromFlats(flats, composition));
-  std::string value = ToString(ring);
-
-  for (int i = 0; i < value.length(); ++i) {
-    CordRepRing::Position pos = ring->FindTail(i + 1);
-    auto data = ring->entry_data(ring->retreat(pos.index));
-    ASSERT_THAT(pos.offset, Lt(data.length()));
-    ASSERT_THAT(data[data.length() - pos.offset - 1], Eq(value[i]));
-  }
-}
-
-TEST_F(CordRingTest, FindTailWithHint) {
-  constexpr const char* flats[] = {
-      "abcdefghij", "klmnopqrst", "uvwxyz",     "ABCDEFGHIJ",
-      "KLMNOPQRST", "UVWXYZ",     "1234567890", "~!@#$%^&*()_",
-      "+-=",        "[]\\{}|;':", ",/<>?",      "."};
-  auto composition = RandomComposition();
-  SCOPED_TRACE(ToString(composition));
-  CordRepRing* ring = NeedsUnref(FromFlats(flats, composition));
-  std::string value = ToString(ring);
-
-  // Test hint beyond valid position
-#if defined(GTEST_HAS_DEATH_TEST)
-  index_type head = ring->head();
-  EXPECT_DEBUG_DEATH(ring->FindTail(ring->advance(head), 1), ".*");
-  EXPECT_DEBUG_DEATH(ring->FindTail(ring->advance(head), 10), ".*");
-  EXPECT_DEBUG_DEATH(ring->FindTail(ring->advance(head, 3), 26), ".*");
-#endif
-
-  for (int i = 0; i < value.length(); ++i) {
-    CordRepRing::Position pos = ring->FindTail(i + 1);
-    auto data = ring->entry_data(ring->retreat(pos.index));
-    ASSERT_THAT(pos.offset, Lt(data.length()));
-    ASSERT_THAT(data[data.length() - pos.offset - 1], Eq(value[i]));
-  }
-}
-
-TEST_F(CordRingTest, FindTailInLargeRing) {
-  constexpr const char* flats[] = {
-      "abcdefghij", "klmnopqrst", "uvwxyz",     "ABCDEFGHIJ",
-      "KLMNOPQRST", "UVWXYZ",     "1234567890", "~!@#$%^&*()_",
-      "+-=",        "[]\\{}|;':", ",/<>?",      "."};
-  auto composition = RandomComposition();
-  SCOPED_TRACE(ToString(composition));
-  CordRepRing* ring = FromFlats(flats, composition);
-  for (int i = 0; i < 13; ++i) {
-    ring = CordRepRing::Append(ring, FromFlats(flats, composition));
-  }
-  NeedsUnref(ring);
-  std::string value = ToString(ring);
-  for (int i = 0; i < value.length(); ++i) {
-    CordRepRing::Position pos = ring->FindTail(i + 1);
-    auto data = ring->entry_data(ring->retreat(pos.index));
-    ASSERT_THAT(pos.offset, Lt(data.length()));
-    ASSERT_THAT(data[data.length() - pos.offset - 1], Eq(value[i]));
-  }
-}
-
-TEST_F(CordRingTest, GetCharacter) {
-  auto flats = MakeSpan(kFoxFlats);
-  CordRepRing* ring = CordRepRing::Create(MakeFlat("Tail"), flats.size());
-  CordRep* child = FromFlats(flats, kAppend);
-  CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, child));
-  std::string value = ToString(result);
-  for (int i = 0; i < value.length(); ++i) {
-    ASSERT_THAT(result->GetCharacter(i), Eq(value[i]));
-  }
-}
-
-TEST_F(CordRingTest, GetCharacterWithSubstring) {
-  absl::string_view str1 = "abcdefghijklmnopqrstuvwxyz";
-  auto* child = MakeSubstring(4, 20, MakeFlat(str1));
-  CordRepRing* result = NeedsUnref(CordRepRing::Create(child));
-  ASSERT_THAT(result, IsValidRingBuffer());
-  std::string value = ToString(result);
-  for (int i = 0; i < value.length(); ++i) {
-    ASSERT_THAT(result->GetCharacter(i), Eq(value[i]));
-  }
-}
-
-TEST_F(CordRingTest, IsFlatSingleFlat) {
-  for (bool external : {false, true}) {
-    SCOPED_TRACE(external ? "With External" : "With Flat");
-    absl::string_view str = "Hello world";
-    CordRep* rep = external ? MakeExternal(str) : MakeFlat(str);
-    CordRepRing* ring = NeedsUnref(CordRepRing::Create(rep));
-
-    // The ring is a single non-fragmented flat:
-    absl::string_view fragment;
-    EXPECT_TRUE(ring->IsFlat(nullptr));
-    EXPECT_TRUE(ring->IsFlat(&fragment));
-    EXPECT_THAT(fragment, Eq("Hello world"));
-    fragment = "";
-    EXPECT_TRUE(ring->IsFlat(0, 11, nullptr));
-    EXPECT_TRUE(ring->IsFlat(0, 11, &fragment));
-    EXPECT_THAT(fragment, Eq("Hello world"));
-
-    // Arbitrary ranges must check true as well.
-    EXPECT_TRUE(ring->IsFlat(1, 4, &fragment));
-    EXPECT_THAT(fragment, Eq("ello"));
-    EXPECT_TRUE(ring->IsFlat(6, 5, &fragment));
-    EXPECT_THAT(fragment, Eq("world"));
-  }
-}
-
-TEST_F(CordRingTest, IsFlatMultiFlat) {
-  for (bool external : {false, true}) {
-    SCOPED_TRACE(external ? "With External" : "With Flat");
-    absl::string_view str1 = "Hello world";
-    absl::string_view str2 = "Halt and catch fire";
-    CordRep* rep1 = external ? MakeExternal(str1) : MakeFlat(str1);
-    CordRep* rep2 = external ? MakeExternal(str2) : MakeFlat(str2);
-    CordRepRing* ring = CordRepRing::Append(CordRepRing::Create(rep1), rep2);
-    NeedsUnref(ring);
-
-    // The ring is fragmented, IsFlat() on the entire cord must be false.
-    EXPECT_FALSE(ring->IsFlat(nullptr));
-    absl::string_view fragment = "Don't touch this";
-    EXPECT_FALSE(ring->IsFlat(&fragment));
-    EXPECT_THAT(fragment, Eq("Don't touch this"));
-
-    // Check for ranges exactly within both flats.
-    EXPECT_TRUE(ring->IsFlat(0, 11, &fragment));
-    EXPECT_THAT(fragment, Eq("Hello world"));
-    EXPECT_TRUE(ring->IsFlat(11, 19, &fragment));
-    EXPECT_THAT(fragment, Eq("Halt and catch fire"));
-
-    // Check for arbitrary partial range inside each flat.
-    EXPECT_TRUE(ring->IsFlat(1, 4, &fragment));
-    EXPECT_THAT(fragment, "ello");
-    EXPECT_TRUE(ring->IsFlat(26, 4, &fragment));
-    EXPECT_THAT(fragment, "fire");
-
-    // Check ranges spanning across both flats
-    fragment = "Don't touch this";
-    EXPECT_FALSE(ring->IsFlat(1, 18, &fragment));
-    EXPECT_FALSE(ring->IsFlat(10, 2, &fragment));
-    EXPECT_THAT(fragment, Eq("Don't touch this"));
-  }
-}
-
-TEST_F(CordRingTest, Dump) {
-  std::stringstream ss;
-  auto flats = MakeSpan(kFoxFlats);
-  CordRepRing* ring = NeedsUnref(FromFlats(flats, kPrepend));
-  ss << *ring;
-}
-
-}  // namespace
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/absl/strings/cord_test.cc b/absl/strings/cord_test.cc
index 36e397e..f1a5f39 100644
--- a/absl/strings/cord_test.cc
+++ b/absl/strings/cord_test.cc
@@ -15,33 +15,50 @@
 #include "absl/strings/cord.h"
 
 #include <algorithm>
-#include <climits>
+#include <array>
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
 #include <cstdio>
+#include <cstring>
+#include <iostream>
 #include <iterator>
-#include <map>
-#include <numeric>
+#include <limits>
 #include <random>
+#include <set>
 #include <sstream>
+#include <string>
 #include <type_traits>
 #include <utility>
 #include <vector>
 
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
+#include "absl/base/attributes.h"
 #include "absl/base/config.h"
 #include "absl/base/internal/endian.h"
 #include "absl/base/macros.h"
+#include "absl/base/options.h"
 #include "absl/container/fixed_array.h"
+#include "absl/functional/function_ref.h"
 #include "absl/hash/hash.h"
 #include "absl/log/check.h"
 #include "absl/log/log.h"
 #include "absl/random/random.h"
+#include "absl/strings/cord_buffer.h"
 #include "absl/strings/cord_test_helpers.h"
 #include "absl/strings/cordz_test_helpers.h"
+#include "absl/strings/internal/cord_internal.h"
+#include "absl/strings/internal/cord_rep_crc.h"
+#include "absl/strings/internal/cord_rep_flat.h"
+#include "absl/strings/internal/cordz_statistics.h"
+#include "absl/strings/internal/cordz_update_tracker.h"
+#include "absl/strings/internal/string_constant.h"
 #include "absl/strings/match.h"
 #include "absl/strings/str_cat.h"
 #include "absl/strings/str_format.h"
 #include "absl/strings/string_view.h"
+#include "absl/types/optional.h"
 
 // convenience local constants
 static constexpr auto FLAT = absl::cord_internal::FLAT;
@@ -484,6 +501,93 @@
   ASSERT_TRUE(!empty.EndsWith("xyz"));
 }
 
+TEST_P(CordTest, Contains) {
+  auto flat_haystack = absl::Cord("this is a flat cord");
+  auto fragmented_haystack = absl::MakeFragmentedCord(
+      {"this", " ", "is", " ", "a", " ", "fragmented", " ", "cord"});
+
+  EXPECT_TRUE(flat_haystack.Contains(""));
+  EXPECT_TRUE(fragmented_haystack.Contains(""));
+  EXPECT_TRUE(flat_haystack.Contains(absl::Cord("")));
+  EXPECT_TRUE(fragmented_haystack.Contains(absl::Cord("")));
+  EXPECT_TRUE(absl::Cord("").Contains(""));
+  EXPECT_TRUE(absl::Cord("").Contains(absl::Cord("")));
+  EXPECT_FALSE(absl::Cord("").Contains(flat_haystack));
+  EXPECT_FALSE(absl::Cord("").Contains(fragmented_haystack));
+
+  EXPECT_FALSE(flat_haystack.Contains("z"));
+  EXPECT_FALSE(fragmented_haystack.Contains("z"));
+  EXPECT_FALSE(flat_haystack.Contains(absl::Cord("z")));
+  EXPECT_FALSE(fragmented_haystack.Contains(absl::Cord("z")));
+
+  EXPECT_FALSE(flat_haystack.Contains("is an"));
+  EXPECT_FALSE(fragmented_haystack.Contains("is an"));
+  EXPECT_FALSE(flat_haystack.Contains(absl::Cord("is an")));
+  EXPECT_FALSE(fragmented_haystack.Contains(absl::Cord("is an")));
+  EXPECT_FALSE(
+      flat_haystack.Contains(absl::MakeFragmentedCord({"is", " ", "an"})));
+  EXPECT_FALSE(fragmented_haystack.Contains(
+      absl::MakeFragmentedCord({"is", " ", "an"})));
+
+  EXPECT_TRUE(flat_haystack.Contains("is a"));
+  EXPECT_TRUE(fragmented_haystack.Contains("is a"));
+  EXPECT_TRUE(flat_haystack.Contains(absl::Cord("is a")));
+  EXPECT_TRUE(fragmented_haystack.Contains(absl::Cord("is a")));
+  EXPECT_TRUE(
+      flat_haystack.Contains(absl::MakeFragmentedCord({"is", " ", "a"})));
+  EXPECT_TRUE(
+      fragmented_haystack.Contains(absl::MakeFragmentedCord({"is", " ", "a"})));
+}
+
+TEST_P(CordTest, Find) {
+  auto flat_haystack = absl::Cord("this is a flat cord");
+  auto fragmented_haystack = absl::MakeFragmentedCord(
+      {"this", " ", "is", " ", "a", " ", "fragmented", " ", "cord"});
+  auto empty_haystack = absl::Cord("");
+
+  EXPECT_EQ(flat_haystack.Find(""), flat_haystack.char_begin());
+  EXPECT_EQ(fragmented_haystack.Find(""), fragmented_haystack.char_begin());
+  EXPECT_EQ(flat_haystack.Find(absl::Cord("")), flat_haystack.char_begin());
+  EXPECT_EQ(fragmented_haystack.Find(absl::Cord("")),
+            fragmented_haystack.char_begin());
+  EXPECT_EQ(empty_haystack.Find(""), empty_haystack.char_begin());
+  EXPECT_EQ(empty_haystack.Find(absl::Cord("")), empty_haystack.char_begin());
+  EXPECT_EQ(empty_haystack.Find(flat_haystack), empty_haystack.char_end());
+  EXPECT_EQ(empty_haystack.Find(fragmented_haystack),
+            empty_haystack.char_end());
+
+  EXPECT_EQ(flat_haystack.Find("z"), flat_haystack.char_end());
+  EXPECT_EQ(fragmented_haystack.Find("z"), fragmented_haystack.char_end());
+  EXPECT_EQ(flat_haystack.Find(absl::Cord("z")), flat_haystack.char_end());
+  EXPECT_EQ(fragmented_haystack.Find(absl::Cord("z")),
+            fragmented_haystack.char_end());
+
+  EXPECT_EQ(flat_haystack.Find("is an"), flat_haystack.char_end());
+  EXPECT_EQ(fragmented_haystack.Find("is an"), fragmented_haystack.char_end());
+  EXPECT_EQ(flat_haystack.Find(absl::Cord("is an")), flat_haystack.char_end());
+  EXPECT_EQ(fragmented_haystack.Find(absl::Cord("is an")),
+            fragmented_haystack.char_end());
+  EXPECT_EQ(flat_haystack.Find(absl::MakeFragmentedCord({"is", " ", "an"})),
+            flat_haystack.char_end());
+  EXPECT_EQ(
+      fragmented_haystack.Find(absl::MakeFragmentedCord({"is", " ", "an"})),
+      fragmented_haystack.char_end());
+
+  EXPECT_EQ(flat_haystack.Find("is a"),
+            std::next(flat_haystack.char_begin(), 5));
+  EXPECT_EQ(fragmented_haystack.Find("is a"),
+            std::next(fragmented_haystack.char_begin(), 5));
+  EXPECT_EQ(flat_haystack.Find(absl::Cord("is a")),
+            std::next(flat_haystack.char_begin(), 5));
+  EXPECT_EQ(fragmented_haystack.Find(absl::Cord("is a")),
+            std::next(fragmented_haystack.char_begin(), 5));
+  EXPECT_EQ(flat_haystack.Find(absl::MakeFragmentedCord({"is", " ", "a"})),
+            std::next(flat_haystack.char_begin(), 5));
+  EXPECT_EQ(
+      fragmented_haystack.Find(absl::MakeFragmentedCord({"is", " ", "a"})),
+      std::next(fragmented_haystack.char_begin(), 5));
+}
+
 TEST_P(CordTest, Subcord) {
   RandomEngine rng(GTEST_FLAG_GET(random_seed));
   const std::string s = RandomLowercaseString(&rng, 1024);
@@ -2006,8 +2110,6 @@
   // This test exercises a diabolical Append(<one char>) on a cord, making the
   // cord shared before each Append call resulting in a terribly fragmented
   // resulting cord.
-  // TODO(b/183983616): Apply some minimum compaction when copying a shared
-  // source cord into a mutable copy for updates in CordRepRing.
   RandomEngine rng(GTEST_FLAG_GET(random_seed));
   const std::string expected = RandomLowercaseString(&rng, 5000);
   absl::Cord cord;
@@ -2554,6 +2656,13 @@
   EXPECT_EQ(c, "There were 0003 little pigs.And 1   bad wolf!");
 }
 
+TEST_P(CordTest, Stringify) {
+  absl::Cord c =
+      absl::MakeFragmentedCord({"A ", "small ", "fragmented ", "Cord", "."});
+  MaybeHarden(c);
+  EXPECT_EQ(absl::StrCat(c), "A small fragmented Cord.");
+}
+
 TEST_P(CordTest, Hardening) {
   absl::Cord cord("hello");
   MaybeHarden(cord);
@@ -3138,6 +3247,12 @@
   EXPECT_EQ(absl::HashOf(c3), absl::HashOf(absl::string_view()));
 }
 
+TEST(CrcCordTest, ChecksummedEmptyCordEstimateMemoryUsage) {
+  absl::Cord cord;
+  cord.SetExpectedChecksum(0);
+  EXPECT_NE(cord.EstimatedMemoryUsage(), 0);
+}
+
 #if defined(GTEST_HAS_DEATH_TEST) && defined(ABSL_INTERNAL_CORD_HAVE_SANITIZER)
 
 // Returns an expected poison / uninitialized death message expression.
diff --git a/absl/strings/cordz_test.cc b/absl/strings/cordz_test.cc
index 2b7d30b..a35493e 100644
--- a/absl/strings/cordz_test.cc
+++ b/absl/strings/cordz_test.cc
@@ -12,18 +12,20 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <cstdint>
+#include <cstddef>
+#include <cstring>
+#include <ostream>
 #include <string>
+#include <utility>
 
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
 #include "absl/base/config.h"
-#include "absl/base/internal/raw_logging.h"
-#include "absl/base/macros.h"
 #include "absl/strings/cord.h"
+#include "absl/strings/cord_buffer.h"
 #include "absl/strings/cord_test_helpers.h"
 #include "absl/strings/cordz_test_helpers.h"
-#include "absl/strings/internal/cordz_functions.h"
+#include "absl/strings/internal/cord_internal.h"
 #include "absl/strings/internal/cordz_info.h"
 #include "absl/strings/internal/cordz_sample_token.h"
 #include "absl/strings/internal/cordz_statistics.h"
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/escaping.cc b/absl/strings/escaping.cc
index 2827fba..1c0eac4 100644
--- a/absl/strings/escaping.cc
+++ b/absl/strings/escaping.cc
@@ -16,21 +16,22 @@
 
 #include <algorithm>
 #include <cassert>
+#include <cstddef>
 #include <cstdint>
 #include <cstring>
-#include <iterator>
 #include <limits>
 #include <string>
 
-#include "absl/base/internal/endian.h"
+#include "absl/base/config.h"
 #include "absl/base/internal/raw_logging.h"
 #include "absl/base/internal/unaligned_access.h"
-#include "absl/strings/internal/char_map.h"
+#include "absl/strings/ascii.h"
+#include "absl/strings/charset.h"
 #include "absl/strings/internal/escaping.h"
 #include "absl/strings/internal/resize_uninitialized.h"
 #include "absl/strings/internal/utf8.h"
+#include "absl/strings/numbers.h"
 #include "absl/strings/str_cat.h"
-#include "absl/strings/str_join.h"
 #include "absl/strings/string_view.h"
 
 namespace absl {
diff --git a/absl/strings/escaping_benchmark.cc b/absl/strings/escaping_benchmark.cc
index 10d5b03..f792226 100644
--- a/absl/strings/escaping_benchmark.cc
+++ b/absl/strings/escaping_benchmark.cc
@@ -14,13 +14,17 @@
 
 #include "absl/strings/escaping.h"
 
+#include <cstdint>
 #include <cstdio>
 #include <cstring>
+#include <memory>
 #include <random>
+#include <string>
 
 #include "benchmark/benchmark.h"
 #include "absl/base/internal/raw_logging.h"
 #include "absl/strings/internal/escaping_test_common.h"
+#include "absl/strings/str_cat.h"
 
 namespace {
 
diff --git a/absl/strings/escaping_test.cc b/absl/strings/escaping_test.cc
index 9f62c1e..ca1ee45 100644
--- a/absl/strings/escaping_test.cc
+++ b/absl/strings/escaping_test.cc
@@ -15,17 +15,20 @@
 #include "absl/strings/escaping.h"
 
 #include <array>
+#include <cstddef>
 #include <cstdio>
 #include <cstring>
+#include <initializer_list>
 #include <memory>
+#include <string>
 #include <vector>
 
-#include "gmock/gmock.h"
 #include "gtest/gtest.h"
-#include "absl/container/fixed_array.h"
+#include "absl/log/check.h"
 #include "absl/strings/str_cat.h"
 
 #include "absl/strings/internal/escaping_test_common.h"
+#include "absl/strings/string_view.h"
 
 namespace {
 
diff --git a/absl/strings/has_absl_stringify.h b/absl/strings/has_absl_stringify.h
new file mode 100644
index 0000000..274a786
--- /dev/null
+++ b/absl/strings/has_absl_stringify.h
@@ -0,0 +1,63 @@
+// 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.
+
+#ifndef ABSL_STRINGS_HAS_ABSL_STRINGIFY_H_
+#define ABSL_STRINGS_HAS_ABSL_STRINGIFY_H_
+
+#include <type_traits>
+#include <utility>
+
+#include "absl/strings/string_view.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+
+namespace strings_internal {
+
+// This is an empty class not intended to be used. It exists so that
+// `HasAbslStringify` can reference a universal class rather than needing to be
+// copied for each new sink.
+class UnimplementedSink {
+ public:
+  void Append(size_t count, char ch);
+
+  void Append(string_view v);
+
+  // Support `absl::Format(&sink, format, args...)`.
+  friend void AbslFormatFlush(UnimplementedSink* sink, absl::string_view v);
+};
+
+}  // namespace strings_internal
+
+// `HasAbslStringify<T>` detects if type `T` supports the `AbslStringify()`
+// customization point (see
+// https://abseil.io/docs/cpp/guides/format#abslstringify for the
+// documentation).
+//
+// Note that there are types that can be `StrCat`-ed that do not use the
+// `AbslStringify` customization point (for example, `int`).
+
+template <typename T, typename = void>
+struct HasAbslStringify : std::false_type {};
+
+template <typename T>
+struct HasAbslStringify<
+    T, std::enable_if_t<std::is_void<decltype(AbslStringify(
+           std::declval<strings_internal::UnimplementedSink&>(),
+           std::declval<const T&>()))>::value>> : std::true_type {};
+
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif  // ABSL_STRINGS_HAS_ABSL_STRINGIFY_H_
diff --git a/absl/strings/has_absl_stringify_test.cc b/absl/strings/has_absl_stringify_test.cc
new file mode 100644
index 0000000..59e7e1d
--- /dev/null
+++ b/absl/strings/has_absl_stringify_test.cc
@@ -0,0 +1,40 @@
+// 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/strings/has_absl_stringify.h"
+
+#include <string>
+
+#include "gtest/gtest.h"
+#include "absl/types/optional.h"
+
+namespace {
+
+struct TypeWithoutAbslStringify {};
+
+struct TypeWithAbslStringify {
+  template <typename Sink>
+  friend void AbslStringify(Sink&, const TypeWithAbslStringify&) {}
+};
+
+TEST(HasAbslStringifyTest, Works) {
+  EXPECT_FALSE(absl::HasAbslStringify<int>::value);
+  EXPECT_FALSE(absl::HasAbslStringify<std::string>::value);
+  EXPECT_FALSE(absl::HasAbslStringify<TypeWithoutAbslStringify>::value);
+  EXPECT_TRUE(absl::HasAbslStringify<TypeWithAbslStringify>::value);
+  EXPECT_FALSE(
+      absl::HasAbslStringify<absl::optional<TypeWithAbslStringify>>::value);
+}
+
+}  // namespace
diff --git a/absl/strings/has_ostream_operator.h b/absl/strings/has_ostream_operator.h
new file mode 100644
index 0000000..156ffc7
--- /dev/null
+++ b/absl/strings/has_ostream_operator.h
@@ -0,0 +1,42 @@
+// 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.
+
+#ifndef ABSL_STRINGS_HAS_OSTREAM_OPERATOR_H_
+#define ABSL_STRINGS_HAS_OSTREAM_OPERATOR_H_
+
+#include <ostream>
+#include <type_traits>
+#include <utility>
+
+#include "absl/base/config.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+
+// Detects if type `T` supports streaming to `std::ostream`s with `operator<<`.
+
+template <typename T, typename = void>
+struct HasOstreamOperator : std::false_type {};
+
+template <typename T>
+struct HasOstreamOperator<
+    T, std::enable_if_t<std::is_same<
+           std::ostream&, decltype(std::declval<std::ostream&>()
+                                   << std::declval<const T&>())>::value>>
+    : std::true_type {};
+
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif  // ABSL_STRINGS_HAS_OSTREAM_OPERATOR_H_
diff --git a/absl/strings/has_ostream_operator_test.cc b/absl/strings/has_ostream_operator_test.cc
new file mode 100644
index 0000000..9ef4136
--- /dev/null
+++ b/absl/strings/has_ostream_operator_test.cc
@@ -0,0 +1,41 @@
+// 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/strings/has_ostream_operator.h"
+
+#include <ostream>
+#include <string>
+
+#include "gtest/gtest.h"
+#include "absl/types/optional.h"
+
+namespace {
+
+struct TypeWithoutOstreamOp {};
+
+struct TypeWithOstreamOp {
+  friend std::ostream& operator<<(std::ostream& os, const TypeWithOstreamOp&) {
+    return os;
+  }
+};
+
+TEST(HasOstreamOperatorTest, Works) {
+  EXPECT_TRUE(absl::HasOstreamOperator<int>::value);
+  EXPECT_TRUE(absl::HasOstreamOperator<std::string>::value);
+  EXPECT_FALSE(absl::HasOstreamOperator<absl::optional<int>>::value);
+  EXPECT_FALSE(absl::HasOstreamOperator<TypeWithoutOstreamOp>::value);
+  EXPECT_TRUE(absl::HasOstreamOperator<TypeWithOstreamOp>::value);
+}
+
+}  // namespace
diff --git a/absl/strings/internal/char_map.h b/absl/strings/internal/char_map.h
deleted file mode 100644
index 70a9034..0000000
--- a/absl/strings/internal/char_map.h
+++ /dev/null
@@ -1,158 +0,0 @@
-// Copyright 2017 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.
-//
-// Character Map Class
-//
-// A fast, bit-vector map for 8-bit unsigned characters.
-// This class is useful for non-character purposes as well.
-
-#ifndef ABSL_STRINGS_INTERNAL_CHAR_MAP_H_
-#define ABSL_STRINGS_INTERNAL_CHAR_MAP_H_
-
-#include <cstddef>
-#include <cstdint>
-#include <cstring>
-
-#include "absl/base/macros.h"
-#include "absl/base/port.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace strings_internal {
-
-class Charmap {
- public:
-  constexpr Charmap() : m_() {}
-
-  // Initializes with a given char*.  Note that NUL is not treated as
-  // a terminator, but rather a char to be flicked.
-  Charmap(const char* str, int len) : m_() {
-    while (len--) SetChar(*str++);
-  }
-
-  // Initializes with a given char*.  NUL is treated as a terminator
-  // and will not be in the charmap.
-  explicit Charmap(const char* str) : m_() {
-    while (*str) SetChar(*str++);
-  }
-
-  constexpr bool contains(unsigned char c) const {
-    return (m_[c / 64] >> (c % 64)) & 0x1;
-  }
-
-  // Returns true if and only if a character exists in both maps.
-  bool IntersectsWith(const Charmap& c) const {
-    for (size_t i = 0; i < ABSL_ARRAYSIZE(m_); ++i) {
-      if ((m_[i] & c.m_[i]) != 0) return true;
-    }
-    return false;
-  }
-
-  bool IsZero() const {
-    for (uint64_t c : m_) {
-      if (c != 0) return false;
-    }
-    return true;
-  }
-
-  // Containing only a single specified char.
-  static constexpr Charmap Char(char x) {
-    return Charmap(CharMaskForWord(x, 0), CharMaskForWord(x, 1),
-                   CharMaskForWord(x, 2), CharMaskForWord(x, 3));
-  }
-
-  // Containing all the chars in the C-string 's'.
-  static constexpr Charmap FromString(const char* s) {
-    Charmap ret;
-    while (*s) ret = ret | Char(*s++);
-    return ret;
-  }
-
-  // Containing all the chars in the closed interval [lo,hi].
-  static constexpr Charmap Range(char lo, char hi) {
-    return Charmap(RangeForWord(lo, hi, 0), RangeForWord(lo, hi, 1),
-                   RangeForWord(lo, hi, 2), RangeForWord(lo, hi, 3));
-  }
-
-  friend constexpr Charmap operator&(const Charmap& a, const Charmap& b) {
-    return Charmap(a.m_[0] & b.m_[0], a.m_[1] & b.m_[1], a.m_[2] & b.m_[2],
-                   a.m_[3] & b.m_[3]);
-  }
-
-  friend constexpr Charmap operator|(const Charmap& a, const Charmap& b) {
-    return Charmap(a.m_[0] | b.m_[0], a.m_[1] | b.m_[1], a.m_[2] | b.m_[2],
-                   a.m_[3] | b.m_[3]);
-  }
-
-  friend constexpr Charmap operator~(const Charmap& a) {
-    return Charmap(~a.m_[0], ~a.m_[1], ~a.m_[2], ~a.m_[3]);
-  }
-
- private:
-  constexpr Charmap(uint64_t b0, uint64_t b1, uint64_t b2, uint64_t b3)
-      : m_{b0, b1, b2, b3} {}
-
-  static constexpr uint64_t RangeForWord(char lo, char hi, uint64_t word) {
-    return OpenRangeFromZeroForWord(static_cast<unsigned char>(hi) + 1, word) &
-           ~OpenRangeFromZeroForWord(static_cast<unsigned char>(lo), word);
-  }
-
-  // All the chars in the specified word of the range [0, upper).
-  static constexpr uint64_t OpenRangeFromZeroForWord(uint64_t upper,
-                                                     uint64_t word) {
-    return (upper <= 64 * word)
-               ? 0
-               : (upper >= 64 * (word + 1))
-                     ? ~static_cast<uint64_t>(0)
-                     : (~static_cast<uint64_t>(0) >> (64 - upper % 64));
-  }
-
-  static constexpr uint64_t CharMaskForWord(char x, uint64_t word) {
-    const auto unsigned_x = static_cast<unsigned char>(x);
-    return (unsigned_x / 64 == word)
-               ? (static_cast<uint64_t>(1) << (unsigned_x % 64))
-               : 0;
-  }
-
-  void SetChar(char c) {
-    const auto unsigned_c = static_cast<unsigned char>(c);
-    m_[unsigned_c / 64] |= static_cast<uint64_t>(1) << (unsigned_c % 64);
-  }
-
-  uint64_t m_[4];
-};
-
-// Mirror the char-classifying predicates in <cctype>
-constexpr Charmap UpperCharmap() { return Charmap::Range('A', 'Z'); }
-constexpr Charmap LowerCharmap() { return Charmap::Range('a', 'z'); }
-constexpr Charmap DigitCharmap() { return Charmap::Range('0', '9'); }
-constexpr Charmap AlphaCharmap() { return LowerCharmap() | UpperCharmap(); }
-constexpr Charmap AlnumCharmap() { return DigitCharmap() | AlphaCharmap(); }
-constexpr Charmap XDigitCharmap() {
-  return DigitCharmap() | Charmap::Range('A', 'F') | Charmap::Range('a', 'f');
-}
-constexpr Charmap PrintCharmap() { return Charmap::Range(0x20, 0x7e); }
-constexpr Charmap SpaceCharmap() { return Charmap::FromString("\t\n\v\f\r "); }
-constexpr Charmap CntrlCharmap() {
-  return Charmap::Range(0, 0x7f) & ~PrintCharmap();
-}
-constexpr Charmap BlankCharmap() { return Charmap::FromString("\t "); }
-constexpr Charmap GraphCharmap() { return PrintCharmap() & ~SpaceCharmap(); }
-constexpr Charmap PunctCharmap() { return GraphCharmap() & ~AlnumCharmap(); }
-
-}  // namespace strings_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_STRINGS_INTERNAL_CHAR_MAP_H_
diff --git a/absl/strings/internal/char_map_test.cc b/absl/strings/internal/char_map_test.cc
deleted file mode 100644
index d330624..0000000
--- a/absl/strings/internal/char_map_test.cc
+++ /dev/null
@@ -1,172 +0,0 @@
-// Copyright 2017 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/strings/internal/char_map.h"
-
-#include <cctype>
-#include <string>
-#include <vector>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-
-namespace {
-
-constexpr absl::strings_internal::Charmap everything_map =
-    ~absl::strings_internal::Charmap();
-constexpr absl::strings_internal::Charmap nothing_map{};
-
-TEST(Charmap, AllTests) {
-  const absl::strings_internal::Charmap also_nothing_map("", 0);
-  ASSERT_TRUE(everything_map.contains('\0'));
-  ASSERT_TRUE(!nothing_map.contains('\0'));
-  ASSERT_TRUE(!also_nothing_map.contains('\0'));
-  for (unsigned char ch = 1; ch != 0; ++ch) {
-    ASSERT_TRUE(everything_map.contains(ch));
-    ASSERT_TRUE(!nothing_map.contains(ch));
-    ASSERT_TRUE(!also_nothing_map.contains(ch));
-  }
-
-  const absl::strings_internal::Charmap symbols("&@#@^!@?", 5);
-  ASSERT_TRUE(symbols.contains('&'));
-  ASSERT_TRUE(symbols.contains('@'));
-  ASSERT_TRUE(symbols.contains('#'));
-  ASSERT_TRUE(symbols.contains('^'));
-  ASSERT_TRUE(!symbols.contains('!'));
-  ASSERT_TRUE(!symbols.contains('?'));
-  int cnt = 0;
-  for (unsigned char ch = 1; ch != 0; ++ch)
-    cnt += symbols.contains(ch);
-  ASSERT_EQ(cnt, 4);
-
-  const absl::strings_internal::Charmap lets("^abcde", 3);
-  const absl::strings_internal::Charmap lets2("fghij\0klmnop", 10);
-  const absl::strings_internal::Charmap lets3("fghij\0klmnop");
-  ASSERT_TRUE(lets2.contains('k'));
-  ASSERT_TRUE(!lets3.contains('k'));
-
-  ASSERT_TRUE(symbols.IntersectsWith(lets));
-  ASSERT_TRUE(!lets2.IntersectsWith(lets));
-  ASSERT_TRUE(lets.IntersectsWith(symbols));
-  ASSERT_TRUE(!lets.IntersectsWith(lets2));
-
-  ASSERT_TRUE(nothing_map.IsZero());
-  ASSERT_TRUE(!lets.IsZero());
-}
-
-namespace {
-std::string Members(const absl::strings_internal::Charmap& m) {
-  std::string r;
-  for (size_t i = 0; i < 256; ++i)
-    if (m.contains(i)) r.push_back(i);
-  return r;
-}
-
-std::string ClosedRangeString(unsigned char lo, unsigned char hi) {
-  // Don't depend on lo<hi. Just increment until lo==hi.
-  std::string s;
-  while (true) {
-    s.push_back(lo);
-    if (lo == hi) break;
-    ++lo;
-  }
-  return s;
-}
-
-}  // namespace
-
-TEST(Charmap, Constexpr) {
-  constexpr absl::strings_internal::Charmap kEmpty = nothing_map;
-  EXPECT_THAT(Members(kEmpty), "");
-  constexpr absl::strings_internal::Charmap kA =
-      absl::strings_internal::Charmap::Char('A');
-  EXPECT_THAT(Members(kA), "A");
-  constexpr absl::strings_internal::Charmap kAZ =
-      absl::strings_internal::Charmap::Range('A', 'Z');
-  EXPECT_THAT(Members(kAZ), "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
-  constexpr absl::strings_internal::Charmap kIdentifier =
-      absl::strings_internal::Charmap::Range('0', '9') |
-      absl::strings_internal::Charmap::Range('A', 'Z') |
-      absl::strings_internal::Charmap::Range('a', 'z') |
-      absl::strings_internal::Charmap::Char('_');
-  EXPECT_THAT(Members(kIdentifier),
-              "0123456789"
-              "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
-              "_"
-              "abcdefghijklmnopqrstuvwxyz");
-  constexpr absl::strings_internal::Charmap kAll = everything_map;
-  for (size_t i = 0; i < 256; ++i) {
-    EXPECT_TRUE(kAll.contains(i)) << i;
-  }
-  constexpr absl::strings_internal::Charmap kHello =
-      absl::strings_internal::Charmap::FromString("Hello, world!");
-  EXPECT_THAT(Members(kHello), " !,Hdelorw");
-
-  // test negation and intersection
-  constexpr absl::strings_internal::Charmap kABC =
-      absl::strings_internal::Charmap::Range('A', 'Z') &
-      ~absl::strings_internal::Charmap::Range('D', 'Z');
-  EXPECT_THAT(Members(kABC), "ABC");
-}
-
-TEST(Charmap, Range) {
-  // Exhaustive testing takes too long, so test some of the boundaries that
-  // are perhaps going to cause trouble.
-  std::vector<size_t> poi = {0,   1,   2,   3,   4,   7,   8,   9,  15,
-                             16,  17,  30,  31,  32,  33,  63,  64, 65,
-                             127, 128, 129, 223, 224, 225, 254, 255};
-  for (auto lo = poi.begin(); lo != poi.end(); ++lo) {
-    SCOPED_TRACE(*lo);
-    for (auto hi = lo; hi != poi.end(); ++hi) {
-      SCOPED_TRACE(*hi);
-      EXPECT_THAT(Members(absl::strings_internal::Charmap::Range(*lo, *hi)),
-                  ClosedRangeString(*lo, *hi));
-    }
-  }
-}
-
-bool AsBool(int x) { return static_cast<bool>(x); }
-
-TEST(CharmapCtype, Match) {
-  for (int c = 0; c < 256; ++c) {
-    SCOPED_TRACE(c);
-    SCOPED_TRACE(static_cast<char>(c));
-    EXPECT_EQ(AsBool(std::isupper(c)),
-              absl::strings_internal::UpperCharmap().contains(c));
-    EXPECT_EQ(AsBool(std::islower(c)),
-              absl::strings_internal::LowerCharmap().contains(c));
-    EXPECT_EQ(AsBool(std::isdigit(c)),
-              absl::strings_internal::DigitCharmap().contains(c));
-    EXPECT_EQ(AsBool(std::isalpha(c)),
-              absl::strings_internal::AlphaCharmap().contains(c));
-    EXPECT_EQ(AsBool(std::isalnum(c)),
-              absl::strings_internal::AlnumCharmap().contains(c));
-    EXPECT_EQ(AsBool(std::isxdigit(c)),
-              absl::strings_internal::XDigitCharmap().contains(c));
-    EXPECT_EQ(AsBool(std::isprint(c)),
-              absl::strings_internal::PrintCharmap().contains(c));
-    EXPECT_EQ(AsBool(std::isspace(c)),
-              absl::strings_internal::SpaceCharmap().contains(c));
-    EXPECT_EQ(AsBool(std::iscntrl(c)),
-              absl::strings_internal::CntrlCharmap().contains(c));
-    EXPECT_EQ(AsBool(std::isblank(c)),
-              absl::strings_internal::BlankCharmap().contains(c));
-    EXPECT_EQ(AsBool(std::isgraph(c)),
-              absl::strings_internal::GraphCharmap().contains(c));
-    EXPECT_EQ(AsBool(std::ispunct(c)),
-              absl::strings_internal::PunctCharmap().contains(c));
-  }
-}
-
-}  // namespace
diff --git a/absl/strings/internal/cord_internal.cc b/absl/strings/internal/cord_internal.cc
index b787438..57d9d38 100644
--- a/absl/strings/internal/cord_internal.cc
+++ b/absl/strings/internal/cord_internal.cc
@@ -22,15 +22,12 @@
 #include "absl/strings/internal/cord_rep_btree.h"
 #include "absl/strings/internal/cord_rep_crc.h"
 #include "absl/strings/internal/cord_rep_flat.h"
-#include "absl/strings/internal/cord_rep_ring.h"
 #include "absl/strings/str_cat.h"
 
 namespace absl {
 ABSL_NAMESPACE_BEGIN
 namespace cord_internal {
 
-ABSL_CONST_INIT std::atomic<bool> cord_ring_buffer_enabled(
-    kCordEnableRingBufferDefault);
 ABSL_CONST_INIT std::atomic<bool> shallow_subcords_enabled(
     kCordShallowSubcordsDefault);
 
@@ -47,9 +44,6 @@
     if (rep->tag == BTREE) {
       CordRepBtree::Destroy(rep->btree());
       return;
-    } else if (rep->tag == RING) {
-      CordRepRing::Destroy(rep->ring());
-      return;
     } else if (rep->tag == EXTERNAL) {
       CordRepExternal::Delete(rep);
       return;
diff --git a/absl/strings/internal/cord_internal.h b/absl/strings/internal/cord_internal.h
index 20dd008..8744540 100644
--- a/absl/strings/internal/cord_internal.h
+++ b/absl/strings/internal/cord_internal.h
@@ -55,24 +55,15 @@
 struct CordRepFlat;
 struct CordRepSubstring;
 struct CordRepCrc;
-class CordRepRing;
 class CordRepBtree;
 
 class CordzInfo;
 
 // Default feature enable states for cord ring buffers
-enum CordFeatureDefaults {
-  kCordEnableRingBufferDefault = false,
-  kCordShallowSubcordsDefault = false
-};
+enum CordFeatureDefaults { kCordShallowSubcordsDefault = false };
 
-extern std::atomic<bool> cord_ring_buffer_enabled;
 extern std::atomic<bool> shallow_subcords_enabled;
 
-inline void enable_cord_ring_buffer(bool enable) {
-  cord_ring_buffer_enabled.store(enable, std::memory_order_relaxed);
-}
-
 inline void enable_shallow_subcords(bool enable) {
   shallow_subcords_enabled.store(enable, std::memory_order_relaxed);
 }
@@ -110,8 +101,16 @@
     if (nullify_tail) {
       memset(dst + 7, 0, 8);
     }
+    // GCC 12 has a false-positive -Wstringop-overflow warning here.
+#if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(12, 0)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wstringop-overflow"
+#endif
     memcpy(dst, &buf1, 8);
     memcpy(dst + n - 8, &buf2, 8);
+#if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(12, 0)
+#pragma GCC diagnostic pop
+#endif
   } else if (n >= 4) {
     uint32_t buf1;
     uint32_t buf2;
@@ -158,18 +157,18 @@
   // false.  Always returns false when the immortal bit is set.
   inline bool Decrement() {
     int32_t refcount = count_.load(std::memory_order_acquire);
-    assert((refcount & kRefcountMask) > 0 || refcount & kImmortalFlag);
+    assert(refcount > 0 || refcount & kImmortalFlag);
     return refcount != kRefIncrement &&
-           (count_.fetch_sub(kRefIncrement, std::memory_order_acq_rel) &
-            kHighRefcountMask) != 0;
+           count_.fetch_sub(kRefIncrement, std::memory_order_acq_rel) !=
+               kRefIncrement;
   }
 
   // Same as Decrement but expect that refcount is greater than 1.
   inline bool DecrementExpectHighRefcount() {
     int32_t refcount =
         count_.fetch_sub(kRefIncrement, std::memory_order_acq_rel);
-    assert((refcount & kRefcountMask) > 0 || refcount & kImmortalFlag);
-    return (refcount & kHighRefcountMask) != 0;
+    assert(refcount > 0 || refcount & kImmortalFlag);
+    return refcount != kRefIncrement;
   }
 
   // Returns the current reference count using acquire semantics.
@@ -185,10 +184,9 @@
   // This call performs the test for a reference count of one, and
   // performs the memory barrier needed for the owning thread
   // to act on the object, knowing that it has exclusive access to the
-  // object.  Always returns false when the immortal bit is set.
+  // object. Always returns false when the immortal bit is set.
   inline bool IsOne() {
-    return (count_.load(std::memory_order_acquire) & kRefcountMask) ==
-           kRefIncrement;
+    return count_.load(std::memory_order_acquire) == kRefIncrement;
   }
 
   bool IsImmortal() const {
@@ -196,32 +194,15 @@
   }
 
  private:
-  // We reserve the bottom bits for flags.
+  // We reserve the bottom bit for flag.
   // kImmortalBit indicates that this entity should never be collected; it is
   // used for the StringConstant constructor to avoid collecting immutable
   // constant cords.
-  // kReservedFlag is reserved for future use.
   enum Flags {
-    kNumFlags = 2,
+    kNumFlags = 1,
 
     kImmortalFlag = 0x1,
-    kReservedFlag = 0x2,
     kRefIncrement = (1 << kNumFlags),
-
-    // Bitmask to use when checking refcount by equality.  This masks out
-    // all flags except kImmortalFlag, which is part of the refcount for
-    // purposes of equality.  (A refcount of 0 or 1 does not count as 0 or 1
-    // if the immortal bit is set.)
-    kRefcountMask = ~kReservedFlag,
-
-    // Bitmask to use when checking if refcount is equal to 1 and not
-    // immortal when decrementing the refcount. This masks out kRefIncrement and
-    // all flags except kImmortalFlag. If the masked RefcountAndFlags is 0, we
-    // assume the refcount is equal to 1, since we know it's not immortal and
-    // not greater than 1. If the masked RefcountAndFlags is not 0, we can
-    // assume the refcount is not equal to 1 since either a higher bit in the
-    // refcount is set, or kImmortal is set.
-    kHighRefcountMask = kRefcountMask & ~kRefIncrement,
   };
 
   std::atomic<int32_t> count_;
@@ -233,7 +214,7 @@
   SUBSTRING = 1,
   CRC = 2,
   BTREE = 3,
-  RING = 4,
+  UNUSED_4 = 4,
   EXTERNAL = 5,
 
   // We have different tags for different sized flat arrays,
@@ -252,12 +233,8 @@
 // There are various locations where we want to check if some rep is a 'plain'
 // data edge, i.e. an external or flat rep. By having FLAT == EXTERNAL + 1, we
 // can perform this check in a single branch as 'tag >= EXTERNAL'
-// Likewise, we have some locations where we check for 'ring or external/flat',
-// so likewise align RING to EXTERNAL.
 // Note that we can leave this optimization to the compiler. The compiler will
 // DTRT when it sees a condition like `tag == EXTERNAL || tag >= FLAT`.
-static_assert(RING == BTREE + 1, "BTREE and RING not consecutive");
-static_assert(EXTERNAL == RING + 1, "BTREE and EXTERNAL not consecutive");
 static_assert(FLAT == EXTERNAL + 1, "EXTERNAL and FLAT not consecutive");
 
 struct CordRep {
@@ -301,15 +278,12 @@
   // # LINT.ThenChange(cord_rep_btree.h:copy_raw)
 
   // Returns true if this instance's tag matches the requested type.
-  constexpr bool IsRing() const { return tag == RING; }
   constexpr bool IsSubstring() const { return tag == SUBSTRING; }
   constexpr bool IsCrc() const { return tag == CRC; }
   constexpr bool IsExternal() const { return tag == EXTERNAL; }
   constexpr bool IsFlat() const { return tag >= FLAT; }
   constexpr bool IsBtree() const { return tag == BTREE; }
 
-  inline CordRepRing* ring();
-  inline const CordRepRing* ring() const;
   inline CordRepSubstring* substring();
   inline const CordRepSubstring* substring() const;
   inline CordRepCrc* crc();
diff --git a/absl/strings/internal/cord_rep_ring.cc b/absl/strings/internal/cord_rep_ring.cc
deleted file mode 100644
index af2fc76..0000000
--- a/absl/strings/internal/cord_rep_ring.cc
+++ /dev/null
@@ -1,773 +0,0 @@
-// Copyright 2020 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/strings/internal/cord_rep_ring.h"
-
-#include <cassert>
-#include <cstddef>
-#include <cstdint>
-#include <iostream>
-#include <limits>
-#include <memory>
-#include <string>
-
-#include "absl/base/internal/raw_logging.h"
-#include "absl/base/internal/throw_delegate.h"
-#include "absl/base/macros.h"
-#include "absl/container/inlined_vector.h"
-#include "absl/strings/internal/cord_internal.h"
-#include "absl/strings/internal/cord_rep_consume.h"
-#include "absl/strings/internal/cord_rep_flat.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace cord_internal {
-
-namespace {
-
-using index_type = CordRepRing::index_type;
-
-enum class Direction { kForward, kReversed };
-
-inline bool IsFlatOrExternal(CordRep* rep) {
-  return rep->IsFlat() || rep->IsExternal();
-}
-
-// Verifies that n + extra <= kMaxCapacity: throws std::length_error otherwise.
-inline void CheckCapacity(size_t n, size_t extra) {
-  if (ABSL_PREDICT_FALSE(extra > CordRepRing::kMaxCapacity - n)) {
-    base_internal::ThrowStdLengthError("Maximum capacity exceeded");
-  }
-}
-
-// Creates a flat from the provided string data, allocating up to `extra`
-// capacity in the returned flat depending on kMaxFlatLength limitations.
-// Requires `len` to be less or equal to `kMaxFlatLength`
-CordRepFlat* CreateFlat(const char* s, size_t n, size_t extra = 0) {  // NOLINT
-  assert(n <= kMaxFlatLength);
-  auto* rep = CordRepFlat::New(n + extra);
-  rep->length = n;
-  memcpy(rep->Data(), s, n);
-  return rep;
-}
-
-// Unrefs the entries in `[head, tail)`.
-// Requires all entries to be a FLAT or EXTERNAL node.
-void UnrefEntries(const CordRepRing* rep, index_type head, index_type tail) {
-  rep->ForEach(head, tail, [rep](index_type ix) {
-    CordRep* child = rep->entry_child(ix);
-    if (!child->refcount.Decrement()) {
-      if (child->tag >= FLAT) {
-        CordRepFlat::Delete(child->flat());
-      } else {
-        CordRepExternal::Delete(child->external());
-      }
-    }
-  });
-}
-
-}  // namespace
-
-std::ostream& operator<<(std::ostream& s, const CordRepRing& rep) {
-  // Note: 'pos' values are defined as size_t (for overflow reasons), but that
-  // prints really awkward for small prepended values such as -5. ssize_t is not
-  // portable (POSIX), so we use ptrdiff_t instead to cast to signed values.
-  s << "  CordRepRing(" << &rep << ", length = " << rep.length
-    << ", head = " << rep.head_ << ", tail = " << rep.tail_
-    << ", cap = " << rep.capacity_ << ", rc = " << rep.refcount.Get()
-    << ", begin_pos_ = " << static_cast<ptrdiff_t>(rep.begin_pos_) << ") {\n";
-  CordRepRing::index_type head = rep.head();
-  do {
-    CordRep* child = rep.entry_child(head);
-    s << " entry[" << head << "] length = " << rep.entry_length(head)
-      << ", child " << child << ", clen = " << child->length
-      << ", tag = " << static_cast<int>(child->tag)
-      << ", rc = " << child->refcount.Get()
-      << ", offset = " << rep.entry_data_offset(head)
-      << ", end_pos = " << static_cast<ptrdiff_t>(rep.entry_end_pos(head))
-      << "\n";
-    head = rep.advance(head);
-  } while (head != rep.tail());
-  return s << "}\n";
-}
-
-void CordRepRing::AddDataOffset(index_type index, size_t n) {
-  entry_data_offset()[index] += static_cast<offset_type>(n);
-}
-
-void CordRepRing::SubLength(index_type index, size_t n) {
-  entry_end_pos()[index] -= n;
-}
-
-class CordRepRing::Filler {
- public:
-  Filler(CordRepRing* rep, index_type pos) : rep_(rep), head_(pos), pos_(pos) {}
-
-  index_type head() const { return head_; }
-  index_type pos() const { return pos_; }
-
-  void Add(CordRep* child, size_t offset, pos_type end_pos) {
-    rep_->entry_end_pos()[pos_] = end_pos;
-    rep_->entry_child()[pos_] = child;
-    rep_->entry_data_offset()[pos_] = static_cast<offset_type>(offset);
-    pos_ = rep_->advance(pos_);
-  }
-
- private:
-  CordRepRing* rep_;
-  index_type head_;
-  index_type pos_;
-};
-
-#ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL
-constexpr size_t CordRepRing::kMaxCapacity;
-#endif
-
-bool CordRepRing::IsValid(std::ostream& output) const {
-  if (capacity_ == 0) {
-    output << "capacity == 0";
-    return false;
-  }
-
-  if (head_ >= capacity_ || tail_ >= capacity_) {
-    output << "head " << head_ << " and/or tail " << tail_ << "exceed capacity "
-           << capacity_;
-    return false;
-  }
-
-  const index_type back = retreat(tail_);
-  size_t pos_length = Distance(begin_pos_, entry_end_pos(back));
-  if (pos_length != length) {
-    output << "length " << length << " does not match positional length "
-           << pos_length << " from begin_pos " << begin_pos_ << " and entry["
-           << back << "].end_pos " << entry_end_pos(back);
-    return false;
-  }
-
-  index_type head = head_;
-  pos_type begin_pos = begin_pos_;
-  do {
-    pos_type end_pos = entry_end_pos(head);
-    size_t entry_length = Distance(begin_pos, end_pos);
-    if (entry_length == 0) {
-      output << "entry[" << head << "] has an invalid length " << entry_length
-             << " from begin_pos " << begin_pos << " and end_pos " << end_pos;
-      return false;
-    }
-
-    CordRep* child = entry_child(head);
-    if (child == nullptr) {
-      output << "entry[" << head << "].child == nullptr";
-      return false;
-    }
-    if (child->tag < FLAT && child->tag != EXTERNAL) {
-      output << "entry[" << head << "].child has an invalid tag "
-             << static_cast<int>(child->tag);
-      return false;
-    }
-
-    size_t offset = entry_data_offset(head);
-    if (offset >= child->length || entry_length > child->length - offset) {
-      output << "entry[" << head << "] has offset " << offset
-             << " and entry length " << entry_length
-             << " which are outside of the child's length of " << child->length;
-      return false;
-    }
-
-    begin_pos = end_pos;
-    head = advance(head);
-  } while (head != tail_);
-
-  return true;
-}
-
-#ifdef EXTRA_CORD_RING_VALIDATION
-CordRepRing* CordRepRing::Validate(CordRepRing* rep, const char* file,
-                                   int line) {
-  if (!rep->IsValid(std::cerr)) {
-    std::cerr << "\nERROR: CordRepRing corrupted";
-    if (line) std::cerr << " at line " << line;
-    if (file) std::cerr << " in file " << file;
-    std::cerr << "\nContent = " << *rep;
-    abort();
-  }
-  return rep;
-}
-#endif  // EXTRA_CORD_RING_VALIDATION
-
-CordRepRing* CordRepRing::New(size_t capacity, size_t extra) {
-  CheckCapacity(capacity, extra);
-
-  size_t size = AllocSize(capacity += extra);
-  void* mem = ::operator new(size);
-  auto* rep = new (mem) CordRepRing(static_cast<index_type>(capacity));
-  rep->tag = RING;
-  rep->capacity_ = static_cast<index_type>(capacity);
-  rep->begin_pos_ = 0;
-  return rep;
-}
-
-void CordRepRing::SetCapacityForTesting(size_t capacity) {
-  // Adjust for the changed layout
-  assert(capacity <= capacity_);
-  assert(head() == 0 || head() < tail());
-  memmove(Layout::Partial(capacity).Pointer<1>(data_) + head(),
-          Layout::Partial(capacity_).Pointer<1>(data_) + head(),
-          entries() * sizeof(Layout::ElementType<1>));
-  memmove(Layout::Partial(capacity, capacity).Pointer<2>(data_) + head(),
-          Layout::Partial(capacity_, capacity_).Pointer<2>(data_) + head(),
-          entries() * sizeof(Layout::ElementType<2>));
-  capacity_ = static_cast<index_type>(capacity);
-}
-
-void CordRepRing::Delete(CordRepRing* rep) {
-  assert(rep != nullptr && rep->IsRing());
-#if defined(__cpp_sized_deallocation)
-  size_t size = AllocSize(rep->capacity_);
-  rep->~CordRepRing();
-  ::operator delete(rep, size);
-#else
-  rep->~CordRepRing();
-  ::operator delete(rep);
-#endif
-}
-
-void CordRepRing::Destroy(CordRepRing* rep) {
-  UnrefEntries(rep, rep->head(), rep->tail());
-  Delete(rep);
-}
-
-template <bool ref>
-void CordRepRing::Fill(const CordRepRing* src, index_type head,
-                       index_type tail) {
-  this->length = src->length;
-  head_ = 0;
-  tail_ = advance(0, src->entries(head, tail));
-  begin_pos_ = src->begin_pos_;
-
-  // TODO(mvels): there may be opportunities here for large buffers.
-  auto* dst_pos = entry_end_pos();
-  auto* dst_child = entry_child();
-  auto* dst_offset = entry_data_offset();
-  src->ForEach(head, tail, [&](index_type index) {
-    *dst_pos++ = src->entry_end_pos(index);
-    CordRep* child = src->entry_child(index);
-    *dst_child++ = ref ? CordRep::Ref(child) : child;
-    *dst_offset++ = src->entry_data_offset(index);
-  });
-}
-
-CordRepRing* CordRepRing::Copy(CordRepRing* rep, index_type head,
-                               index_type tail, size_t extra) {
-  CordRepRing* newrep = CordRepRing::New(rep->entries(head, tail), extra);
-  newrep->Fill<true>(rep, head, tail);
-  CordRep::Unref(rep);
-  return newrep;
-}
-
-CordRepRing* CordRepRing::Mutable(CordRepRing* rep, size_t extra) {
-  // Get current number of entries, and check for max capacity.
-  size_t entries = rep->entries();
-
-  if (!rep->refcount.IsOne()) {
-    return Copy(rep, rep->head(), rep->tail(), extra);
-  } else if (entries + extra > rep->capacity()) {
-    const size_t min_grow = rep->capacity() + rep->capacity() / 2;
-    const size_t min_extra = (std::max)(extra, min_grow - entries);
-    CordRepRing* newrep = CordRepRing::New(entries, min_extra);
-    newrep->Fill<false>(rep, rep->head(), rep->tail());
-    CordRepRing::Delete(rep);
-    return newrep;
-  } else {
-    return rep;
-  }
-}
-
-Span<char> CordRepRing::GetAppendBuffer(size_t size) {
-  assert(refcount.IsOne());
-  index_type back = retreat(tail_);
-  CordRep* child = entry_child(back);
-  if (child->tag >= FLAT && child->refcount.IsOne()) {
-    size_t capacity = child->flat()->Capacity();
-    pos_type end_pos = entry_end_pos(back);
-    size_t data_offset = entry_data_offset(back);
-    size_t entry_length = Distance(entry_begin_pos(back), end_pos);
-    size_t used = data_offset + entry_length;
-    if (size_t n = (std::min)(capacity - used, size)) {
-      child->length = data_offset + entry_length + n;
-      entry_end_pos()[back] = end_pos + n;
-      this->length += n;
-      return {child->flat()->Data() + used, n};
-    }
-  }
-  return {nullptr, 0};
-}
-
-Span<char> CordRepRing::GetPrependBuffer(size_t size) {
-  assert(refcount.IsOne());
-  CordRep* child = entry_child(head_);
-  size_t data_offset = entry_data_offset(head_);
-  if (data_offset && child->refcount.IsOne() && child->tag >= FLAT) {
-    size_t n = (std::min)(data_offset, size);
-    this->length += n;
-    begin_pos_ -= n;
-    data_offset -= n;
-    entry_data_offset()[head_] = static_cast<offset_type>(data_offset);
-    return {child->flat()->Data() + data_offset, n};
-  }
-  return {nullptr, 0};
-}
-
-CordRepRing* CordRepRing::CreateFromLeaf(CordRep* child, size_t offset,
-                                         size_t len, size_t extra) {
-  CordRepRing* rep = CordRepRing::New(1, extra);
-  rep->head_ = 0;
-  rep->tail_ = rep->advance(0);
-  rep->length = len;
-  rep->entry_end_pos()[0] = len;
-  rep->entry_child()[0] = child;
-  rep->entry_data_offset()[0] = static_cast<offset_type>(offset);
-  return Validate(rep);
-}
-
-CordRepRing* CordRepRing::CreateSlow(CordRep* child, size_t extra) {
-  CordRepRing* rep = nullptr;
-  Consume(child, [&](CordRep* child_arg, size_t offset, size_t len) {
-    if (IsFlatOrExternal(child_arg)) {
-      rep = rep ? AppendLeaf(rep, child_arg, offset, len)
-                : CreateFromLeaf(child_arg, offset, len, extra);
-    } else if (rep) {
-      rep = AddRing<AddMode::kAppend>(rep, child_arg->ring(), offset, len);
-    } else if (offset == 0 && child_arg->length == len) {
-      rep = Mutable(child_arg->ring(), extra);
-    } else {
-      rep = SubRing(child_arg->ring(), offset, len, extra);
-    }
-  });
-  return Validate(rep, nullptr, __LINE__);
-}
-
-CordRepRing* CordRepRing::Create(CordRep* child, size_t extra) {
-  size_t length = child->length;
-  if (IsFlatOrExternal(child)) {
-    return CreateFromLeaf(child, 0, length, extra);
-  }
-  if (child->IsRing()) {
-    return Mutable(child->ring(), extra);
-  }
-  return CreateSlow(child, extra);
-}
-
-template <CordRepRing::AddMode mode>
-CordRepRing* CordRepRing::AddRing(CordRepRing* rep, CordRepRing* ring,
-                                  size_t offset, size_t len) {
-  assert(offset < ring->length);
-  constexpr bool append = mode == AddMode::kAppend;
-  Position head = ring->Find(offset);
-  Position tail = ring->FindTail(head.index, offset + len);
-  const index_type entries = ring->entries(head.index, tail.index);
-
-  rep = Mutable(rep, entries);
-
-  // The delta for making ring[head].end_pos into 'len - offset'
-  const pos_type delta_length =
-      (append ? rep->begin_pos_ + rep->length : rep->begin_pos_ - len) -
-      ring->entry_begin_pos(head.index) - head.offset;
-
-  // Start filling at `tail`, or `entries` before `head`
-  Filler filler(rep, append ? rep->tail_ : rep->retreat(rep->head_, entries));
-
-  if (ring->refcount.IsOne()) {
-    // Copy entries from source stealing the ref and adjusting the end position.
-    // Commit the filler as this is no-op.
-    ring->ForEach(head.index, tail.index, [&](index_type ix) {
-      filler.Add(ring->entry_child(ix), ring->entry_data_offset(ix),
-                 ring->entry_end_pos(ix) + delta_length);
-    });
-
-    // Unref entries we did not copy over, and delete source.
-    if (head.index != ring->head_) UnrefEntries(ring, ring->head_, head.index);
-    if (tail.index != ring->tail_) UnrefEntries(ring, tail.index, ring->tail_);
-    CordRepRing::Delete(ring);
-  } else {
-    ring->ForEach(head.index, tail.index, [&](index_type ix) {
-      CordRep* child = ring->entry_child(ix);
-      filler.Add(child, ring->entry_data_offset(ix),
-                 ring->entry_end_pos(ix) + delta_length);
-      CordRep::Ref(child);
-    });
-    CordRepRing::Unref(ring);
-  }
-
-  if (head.offset) {
-    // Increase offset of first 'source' entry appended or prepended.
-    // This is always the entry in `filler.head()`
-    rep->AddDataOffset(filler.head(), head.offset);
-  }
-
-  if (tail.offset) {
-    // Reduce length of last 'source' entry appended or prepended.
-    // This is always the entry tailed by `filler.pos()`
-    rep->SubLength(rep->retreat(filler.pos()), tail.offset);
-  }
-
-  // Commit changes
-  rep->length += len;
-  if (append) {
-    rep->tail_ = filler.pos();
-  } else {
-    rep->head_ = filler.head();
-    rep->begin_pos_ -= len;
-  }
-
-  return Validate(rep);
-}
-
-CordRepRing* CordRepRing::AppendSlow(CordRepRing* rep, CordRep* child) {
-  Consume(child, [&rep](CordRep* child_arg, size_t offset, size_t len) {
-    if (child_arg->IsRing()) {
-      rep = AddRing<AddMode::kAppend>(rep, child_arg->ring(), offset, len);
-    } else {
-      rep = AppendLeaf(rep, child_arg, offset, len);
-    }
-  });
-  return rep;
-}
-
-CordRepRing* CordRepRing::AppendLeaf(CordRepRing* rep, CordRep* child,
-                                     size_t offset, size_t len) {
-  rep = Mutable(rep, 1);
-  index_type back = rep->tail_;
-  const pos_type begin_pos = rep->begin_pos_ + rep->length;
-  rep->tail_ = rep->advance(rep->tail_);
-  rep->length += len;
-  rep->entry_end_pos()[back] = begin_pos + len;
-  rep->entry_child()[back] = child;
-  rep->entry_data_offset()[back] = static_cast<offset_type>(offset);
-  return Validate(rep, nullptr, __LINE__);
-}
-
-CordRepRing* CordRepRing::Append(CordRepRing* rep, CordRep* child) {
-  size_t length = child->length;
-  if (IsFlatOrExternal(child)) {
-    return AppendLeaf(rep, child, 0, length);
-  }
-  if (child->IsRing()) {
-    return AddRing<AddMode::kAppend>(rep, child->ring(), 0, length);
-  }
-  return AppendSlow(rep, child);
-}
-
-CordRepRing* CordRepRing::PrependSlow(CordRepRing* rep, CordRep* child) {
-  ReverseConsume(child, [&](CordRep* child_arg, size_t offset, size_t len) {
-    if (IsFlatOrExternal(child_arg)) {
-      rep = PrependLeaf(rep, child_arg, offset, len);
-    } else {
-      rep = AddRing<AddMode::kPrepend>(rep, child_arg->ring(), offset, len);
-    }
-  });
-  return Validate(rep);
-}
-
-CordRepRing* CordRepRing::PrependLeaf(CordRepRing* rep, CordRep* child,
-                                      size_t offset, size_t len) {
-  rep = Mutable(rep, 1);
-  index_type head = rep->retreat(rep->head_);
-  pos_type end_pos = rep->begin_pos_;
-  rep->head_ = head;
-  rep->length += len;
-  rep->begin_pos_ -= len;
-  rep->entry_end_pos()[head] = end_pos;
-  rep->entry_child()[head] = child;
-  rep->entry_data_offset()[head] = static_cast<offset_type>(offset);
-  return Validate(rep);
-}
-
-CordRepRing* CordRepRing::Prepend(CordRepRing* rep, CordRep* child) {
-  size_t length = child->length;
-  if (IsFlatOrExternal(child)) {
-    return PrependLeaf(rep, child, 0, length);
-  }
-  if (child->IsRing()) {
-    return AddRing<AddMode::kPrepend>(rep, child->ring(), 0, length);
-  }
-  return PrependSlow(rep, child);
-}
-
-CordRepRing* CordRepRing::Append(CordRepRing* rep, absl::string_view data,
-                                 size_t extra) {
-  if (rep->refcount.IsOne()) {
-    Span<char> avail = rep->GetAppendBuffer(data.length());
-    if (!avail.empty()) {
-      memcpy(avail.data(), data.data(), avail.length());
-      data.remove_prefix(avail.length());
-    }
-  }
-  if (data.empty()) return Validate(rep);
-
-  const size_t flats = (data.length() - 1) / kMaxFlatLength + 1;
-  rep = Mutable(rep, flats);
-
-  Filler filler(rep, rep->tail_);
-  pos_type pos = rep->begin_pos_ + rep->length;
-
-  while (data.length() >= kMaxFlatLength) {
-    auto* flat = CreateFlat(data.data(), kMaxFlatLength);
-    filler.Add(flat, 0, pos += kMaxFlatLength);
-    data.remove_prefix(kMaxFlatLength);
-  }
-
-  if (data.length()) {
-    auto* flat = CreateFlat(data.data(), data.length(), extra);
-    filler.Add(flat, 0, pos += data.length());
-  }
-
-  rep->length = pos - rep->begin_pos_;
-  rep->tail_ = filler.pos();
-
-  return Validate(rep);
-}
-
-CordRepRing* CordRepRing::Prepend(CordRepRing* rep, absl::string_view data,
-                                  size_t extra) {
-  if (rep->refcount.IsOne()) {
-    Span<char> avail = rep->GetPrependBuffer(data.length());
-    if (!avail.empty()) {
-      const char* tail = data.data() + data.length() - avail.length();
-      memcpy(avail.data(), tail, avail.length());
-      data.remove_suffix(avail.length());
-    }
-  }
-  if (data.empty()) return rep;
-
-  const size_t flats = (data.length() - 1) / kMaxFlatLength + 1;
-  rep = Mutable(rep, flats);
-  pos_type pos = rep->begin_pos_;
-  Filler filler(rep, rep->retreat(rep->head_, static_cast<index_type>(flats)));
-
-  size_t first_size = data.size() - (flats - 1) * kMaxFlatLength;
-  CordRepFlat* flat = CordRepFlat::New(first_size + extra);
-  flat->length = first_size + extra;
-  memcpy(flat->Data() + extra, data.data(), first_size);
-  data.remove_prefix(first_size);
-  filler.Add(flat, extra, pos);
-  pos -= first_size;
-
-  while (!data.empty()) {
-    assert(data.size() >= kMaxFlatLength);
-    flat = CreateFlat(data.data(), kMaxFlatLength);
-    filler.Add(flat, 0, pos);
-    pos -= kMaxFlatLength;
-    data.remove_prefix(kMaxFlatLength);
-  }
-
-  rep->head_ = filler.head();
-  rep->length += rep->begin_pos_ - pos;
-  rep->begin_pos_ = pos;
-
-  return Validate(rep);
-}
-
-// 32 entries is 32 * sizeof(pos_type) = 4 cache lines on x86
-static constexpr index_type kBinarySearchThreshold = 32;
-static constexpr index_type kBinarySearchEndCount = 8;
-
-template <bool wrap>
-CordRepRing::index_type CordRepRing::FindBinary(index_type head,
-                                                index_type tail,
-                                                size_t offset) const {
-  index_type count = tail + (wrap ? capacity_ : 0) - head;
-  do {
-    count = (count - 1) / 2;
-    assert(count < entries(head, tail_));
-    index_type mid = wrap ? advance(head, count) : head + count;
-    index_type after_mid = wrap ? advance(mid) : mid + 1;
-    bool larger = (offset >= entry_end_offset(mid));
-    head = larger ? after_mid : head;
-    tail = larger ? tail : mid;
-    assert(head != tail);
-  } while (ABSL_PREDICT_TRUE(count > kBinarySearchEndCount));
-  return head;
-}
-
-CordRepRing::Position CordRepRing::FindSlow(index_type head,
-                                            size_t offset) const {
-  index_type tail = tail_;
-
-  // Binary search until we are good for linear search
-  // Optimize for branchless / non wrapping ops
-  if (tail > head) {
-    index_type count = tail - head;
-    if (count > kBinarySearchThreshold) {
-      head = FindBinary<false>(head, tail, offset);
-    }
-  } else {
-    index_type count = capacity_ + tail - head;
-    if (count > kBinarySearchThreshold) {
-      head = FindBinary<true>(head, tail, offset);
-    }
-  }
-
-  pos_type pos = entry_begin_pos(head);
-  pos_type end_pos = entry_end_pos(head);
-  while (offset >= Distance(begin_pos_, end_pos)) {
-    head = advance(head);
-    pos = end_pos;
-    end_pos = entry_end_pos(head);
-  }
-
-  return {head, offset - Distance(begin_pos_, pos)};
-}
-
-CordRepRing::Position CordRepRing::FindTailSlow(index_type head,
-                                                size_t offset) const {
-  index_type tail = tail_;
-  const size_t tail_offset = offset - 1;
-
-  // Binary search until we are good for linear search
-  // Optimize for branchless / non wrapping ops
-  if (tail > head) {
-    index_type count = tail - head;
-    if (count > kBinarySearchThreshold) {
-      head = FindBinary<false>(head, tail, tail_offset);
-    }
-  } else {
-    index_type count = capacity_ + tail - head;
-    if (count > kBinarySearchThreshold) {
-      head = FindBinary<true>(head, tail, tail_offset);
-    }
-  }
-
-  size_t end_offset = entry_end_offset(head);
-  while (tail_offset >= end_offset) {
-    head = advance(head);
-    end_offset = entry_end_offset(head);
-  }
-
-  return {advance(head), end_offset - offset};
-}
-
-char CordRepRing::GetCharacter(size_t offset) const {
-  assert(offset < length);
-
-  Position pos = Find(offset);
-  size_t data_offset = entry_data_offset(pos.index) + pos.offset;
-  return GetRepData(entry_child(pos.index))[data_offset];
-}
-
-CordRepRing* CordRepRing::SubRing(CordRepRing* rep, size_t offset,
-                                  size_t len, size_t extra) {
-  assert(offset <= rep->length);
-  assert(offset <= rep->length - len);
-
-  if (len == 0) {
-    CordRep::Unref(rep);
-    return nullptr;
-  }
-
-  // Find position of first byte
-  Position head = rep->Find(offset);
-  Position tail = rep->FindTail(head.index, offset + len);
-  const size_t new_entries = rep->entries(head.index, tail.index);
-
-  if (rep->refcount.IsOne() && extra <= (rep->capacity() - new_entries)) {
-    // We adopt a privately owned rep and no extra entries needed.
-    if (head.index != rep->head_) UnrefEntries(rep, rep->head_, head.index);
-    if (tail.index != rep->tail_) UnrefEntries(rep, tail.index, rep->tail_);
-    rep->head_ = head.index;
-    rep->tail_ = tail.index;
-  } else {
-    // Copy subset to new rep
-    rep = Copy(rep, head.index, tail.index, extra);
-    head.index = rep->head_;
-    tail.index = rep->tail_;
-  }
-
-  // Adjust begin_pos and length
-  rep->length = len;
-  rep->begin_pos_ += offset;
-
-  // Adjust head and tail blocks
-  if (head.offset) {
-    rep->AddDataOffset(head.index, head.offset);
-  }
-  if (tail.offset) {
-    rep->SubLength(rep->retreat(tail.index), tail.offset);
-  }
-
-  return Validate(rep);
-}
-
-CordRepRing* CordRepRing::RemovePrefix(CordRepRing* rep, size_t len,
-                                       size_t extra) {
-  assert(len <= rep->length);
-  if (len == rep->length) {
-    CordRep::Unref(rep);
-    return nullptr;
-  }
-
-  Position head = rep->Find(len);
-  if (rep->refcount.IsOne()) {
-    if (head.index != rep->head_) UnrefEntries(rep, rep->head_, head.index);
-    rep->head_ = head.index;
-  } else {
-    rep = Copy(rep, head.index, rep->tail_, extra);
-    head.index = rep->head_;
-  }
-
-  // Adjust begin_pos and length
-  rep->length -= len;
-  rep->begin_pos_ += len;
-
-  // Adjust head block
-  if (head.offset) {
-    rep->AddDataOffset(head.index, head.offset);
-  }
-
-  return Validate(rep);
-}
-
-CordRepRing* CordRepRing::RemoveSuffix(CordRepRing* rep, size_t len,
-                                       size_t extra) {
-  assert(len <= rep->length);
-
-  if (len == rep->length) {
-    CordRep::Unref(rep);
-    return nullptr;
-  }
-
-  Position tail = rep->FindTail(rep->length - len);
-  if (rep->refcount.IsOne()) {
-    // We adopt a privately owned rep, scrub.
-    if (tail.index != rep->tail_) UnrefEntries(rep, tail.index, rep->tail_);
-    rep->tail_ = tail.index;
-  } else {
-    // Copy subset to new rep
-    rep = Copy(rep, rep->head_, tail.index, extra);
-    tail.index = rep->tail_;
-  }
-
-  // Adjust length
-  rep->length -= len;
-
-  // Adjust tail block
-  if (tail.offset) {
-    rep->SubLength(rep->retreat(tail.index), tail.offset);
-  }
-
-  return Validate(rep);
-}
-
-}  // namespace cord_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/absl/strings/internal/cord_rep_ring.h b/absl/strings/internal/cord_rep_ring.h
deleted file mode 100644
index 79a2fdb..0000000
--- a/absl/strings/internal/cord_rep_ring.h
+++ /dev/null
@@ -1,607 +0,0 @@
-// Copyright 2020 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.
-
-#ifndef ABSL_STRINGS_INTERNAL_CORD_REP_RING_H_
-#define ABSL_STRINGS_INTERNAL_CORD_REP_RING_H_
-
-#include <cassert>
-#include <cstddef>
-#include <cstdint>
-#include <iosfwd>
-#include <limits>
-#include <memory>
-
-#include "absl/container/internal/layout.h"
-#include "absl/strings/internal/cord_internal.h"
-#include "absl/strings/internal/cord_rep_flat.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace cord_internal {
-
-// All operations modifying a ring buffer are implemented as static methods
-// requiring a CordRepRing instance with a reference adopted by the method.
-//
-// The methods return the modified ring buffer, which may be equal to the input
-// if the input was not shared, and having large enough capacity to accommodate
-// any newly added node(s). Otherwise, a copy of the input rep with the new
-// node(s) added is returned.
-//
-// Any modification on non shared ring buffers with enough capacity will then
-// require minimum atomic operations. Caller should where possible provide
-// reasonable `extra` hints for both anticipated extra `flat` byte space, as
-// well as anticipated extra nodes required for complex operations.
-//
-// Example of code creating a ring buffer, adding some data to it,
-// and discarding the buffer when done:
-//
-//   void FunWithRings() {
-//     // Create ring with 3 flats
-//     CordRep* flat = CreateFlat("Hello");
-//     CordRepRing* ring = CordRepRing::Create(flat, 2);
-//     ring = CordRepRing::Append(ring, CreateFlat(" "));
-//     ring = CordRepRing::Append(ring, CreateFlat("world"));
-//     DoSomethingWithRing(ring);
-//     CordRep::Unref(ring);
-//   }
-//
-// Example of code Copying an existing ring buffer and modifying it:
-//
-//   void MoreFunWithRings(CordRepRing* src) {
-//     CordRepRing* ring = CordRep::Ref(src)->ring();
-//     ring = CordRepRing::Append(ring, CreateFlat("Hello"));
-//     ring = CordRepRing::Append(ring, CreateFlat(" "));
-//     ring = CordRepRing::Append(ring, CreateFlat("world"));
-//     DoSomethingWithRing(ring);
-//     CordRep::Unref(ring);
-//   }
-//
-class CordRepRing : public CordRep {
- public:
-  // `pos_type` represents a 'logical position'. A CordRepRing instance has a
-  // `begin_pos` (default 0), and each node inside the buffer will have an
-  // `end_pos` which is the `end_pos` of the previous node (or `begin_pos`) plus
-  // this node's length. The purpose is to allow for a binary search on this
-  // position, while allowing O(1) prepend and append operations.
-  using pos_type = size_t;
-
-  // `index_type` is the type for the `head`, `tail` and `capacity` indexes.
-  // Ring buffers are limited to having no more than four billion entries.
-  using index_type = uint32_t;
-
-  // `offset_type` is the type for the data offset inside a child rep's data.
-  using offset_type = uint32_t;
-
-  // Position holds the node index and relative offset into the node for
-  // some physical offset in the contained data as returned by the Find()
-  // and FindTail() methods.
-  struct Position {
-    index_type index;
-    size_t offset;
-  };
-
-  // The maximum # of child nodes that can be hosted inside a CordRepRing.
-  static constexpr size_t kMaxCapacity = (std::numeric_limits<uint32_t>::max)();
-
-  // CordRepring can not be default constructed, moved, copied or assigned.
-  CordRepRing() = delete;
-  CordRepRing(const CordRepRing&) = delete;
-  CordRepRing& operator=(const CordRepRing&) = delete;
-
-  // Returns true if this instance is valid, false if some or all of the
-  // invariants are broken. Intended for debug purposes only.
-  // `output` receives an explanation of the broken invariants.
-  bool IsValid(std::ostream& output) const;
-
-  // Returns the size in bytes for a CordRepRing with `capacity' entries.
-  static constexpr size_t AllocSize(size_t capacity);
-
-  // Returns the distance in bytes from `pos` to `end_pos`.
-  static constexpr size_t Distance(pos_type pos, pos_type end_pos);
-
-  // Creates a new ring buffer from the provided `rep`. Adopts a reference
-  // on `rep`. The returned ring buffer has a capacity of at least `extra + 1`
-  static CordRepRing* Create(CordRep* child, size_t extra = 0);
-
-  // `head`, `tail` and `capacity` indexes defining the ring buffer boundaries.
-  index_type head() const { return head_; }
-  index_type tail() const { return tail_; }
-  index_type capacity() const { return capacity_; }
-
-  // Returns the number of entries in this instance.
-  index_type entries() const { return entries(head_, tail_); }
-
-  // Returns the logical begin position of this instance.
-  pos_type begin_pos() const { return begin_pos_; }
-
-  // Returns the number of entries for a given head-tail range.
-  // Requires `head` and `tail` values to be less than `capacity()`.
-  index_type entries(index_type head, index_type tail) const {
-    assert(head < capacity_ && tail < capacity_);
-    return tail - head + ((tail > head) ? 0 : capacity_);
-  }
-
-  // Returns the logical end position of entry `index`.
-  pos_type const& entry_end_pos(index_type index) const {
-    assert(IsValidIndex(index));
-    return Layout::Partial().Pointer<0>(data_)[index];
-  }
-
-  // Returns the child pointer of entry `index`.
-  CordRep* const& entry_child(index_type index) const {
-    assert(IsValidIndex(index));
-    return Layout::Partial(capacity()).Pointer<1>(data_)[index];
-  }
-
-  // Returns the data offset of entry `index`
-  offset_type const& entry_data_offset(index_type index) const {
-    assert(IsValidIndex(index));
-    return Layout::Partial(capacity(), capacity()).Pointer<2>(data_)[index];
-  }
-
-  // Appends the provided child node to the `rep` instance.
-  // Adopts a reference from `rep` and `child` which may not be null.
-  // If the provided child is a FLAT or EXTERNAL node, or a SUBSTRING node
-  // containing a FLAT or EXTERNAL node, then flat or external the node is added
-  // 'as is', with an offset added for the SUBSTRING case.
-  // If the provided child is a RING or CONCAT tree, or a SUBSTRING of a RING or
-  // CONCAT tree, then all child nodes not excluded by any start offset or
-  // length values are added recursively.
-  static CordRepRing* Append(CordRepRing* rep, CordRep* child);
-
-  // Appends the provided string data to the `rep` instance.
-  // This function will attempt to utilize any remaining capacity in the last
-  // node of the input if that node is not shared (directly or indirectly), and
-  // of type FLAT. Remaining data will be added as one or more FLAT nodes.
-  // Any last node added to the ring buffer will be allocated with up to
-  // `extra` bytes of capacity for (anticipated) subsequent append actions.
-  static CordRepRing* Append(CordRepRing* rep, string_view data,
-                             size_t extra = 0);
-
-  // Prepends the provided child node to the `rep` instance.
-  // Adopts a reference from `rep` and `child` which may not be null.
-  // If the provided child is a FLAT or EXTERNAL node, or a SUBSTRING node
-  // containing a FLAT or EXTERNAL node, then flat or external the node is
-  // prepended 'as is', with an optional offset added for the SUBSTRING case.
-  // If the provided child is a RING or CONCAT tree, or a SUBSTRING of a RING
-  // or CONCAT tree, then all child nodes not excluded by any start offset or
-  // length values are added recursively.
-  static CordRepRing* Prepend(CordRepRing* rep, CordRep* child);
-
-  // Prepends the provided string data to the `rep` instance.
-  // This function will attempt to utilize any remaining capacity in the first
-  // node of the input if that node is not shared (directly or indirectly), and
-  // of type FLAT. Remaining data will be added as one or more FLAT nodes.
-  // Any first node prepnded to the ring buffer will be allocated with up to
-  // `extra` bytes of capacity for (anticipated) subsequent prepend actions.
-  static CordRepRing* Prepend(CordRepRing* rep, string_view data,
-                              size_t extra = 0);
-
-  // Returns a span referencing potentially unused capacity in the last node.
-  // The returned span may be empty if no such capacity is available, or if the
-  // current instance is shared. Else, a span of size `n <= size` is returned.
-  // If non empty, the ring buffer is adjusted to the new length, with the newly
-  // added capacity left uninitialized. Callers should assign a value to the
-  // entire span before any other operations on this instance.
-  Span<char> GetAppendBuffer(size_t size);
-
-  // Returns a span referencing potentially unused capacity in the first node.
-  // This function is identical to GetAppendBuffer except that it returns a span
-  // referencing up to `size` capacity directly before the existing data.
-  Span<char> GetPrependBuffer(size_t size);
-
-  // Returns a cord ring buffer containing `len` bytes of data starting at
-  // `offset`. If the input is not shared, this function will remove all head
-  // and tail child nodes outside of the requested range, and adjust the new
-  // head and tail nodes as required. If the input is shared, this function
-  // returns a new instance sharing some or all of the nodes from the input.
-  static CordRepRing* SubRing(CordRepRing* r, size_t offset, size_t len,
-                              size_t extra = 0);
-
-  // Returns a cord ring buffer with the first `len` bytes removed.
-  // If the input is not shared, this function will remove all head child nodes
-  // fully inside the first `length` bytes, and adjust the new head as required.
-  // If the input is shared, this function returns a new instance sharing some
-  // or all of the nodes from the input.
-  static CordRepRing* RemoveSuffix(CordRepRing* r, size_t len,
-                                   size_t extra = 0);
-
-  // Returns a cord ring buffer with the last `len` bytes removed.
-  // If the input is not shared, this function will remove all head child nodes
-  // fully inside the first `length` bytes, and adjust the new head as required.
-  // If the input is shared, this function returns a new instance sharing some
-  // or all of the nodes from the input.
-  static CordRepRing* RemovePrefix(CordRepRing* r, size_t len,
-                                   size_t extra = 0);
-
-  // Returns the character at `offset`. Requires that `offset < length`.
-  char GetCharacter(size_t offset) const;
-
-  // Returns true if this instance manages a single contiguous buffer, in which
-  // case the (optional) output parameter `fragment` is set. Otherwise, the
-  // function returns false, and `fragment` is left unchanged.
-  bool IsFlat(absl::string_view* fragment) const;
-
-  // Returns true if the data starting at `offset` with length `len` is
-  // managed by this instance inside a single contiguous buffer, in which case
-  // the (optional) output parameter `fragment` is set to the contiguous memory
-  // starting at offset `offset` with length `length`. Otherwise, the function
-  // returns false, and `fragment` is left unchanged.
-  bool IsFlat(size_t offset, size_t len, absl::string_view* fragment) const;
-
-  // Testing only: set capacity to requested capacity.
-  void SetCapacityForTesting(size_t capacity);
-
-  // Returns the CordRep data pointer for the provided CordRep.
-  // Requires that the provided `rep` is either a FLAT or EXTERNAL CordRep.
-  static const char* GetLeafData(const CordRep* rep);
-
-  // Returns the CordRep data pointer for the provided CordRep.
-  // Requires that `rep` is either a FLAT, EXTERNAL, or SUBSTRING CordRep.
-  static const char* GetRepData(const CordRep* rep);
-
-  // Advances the provided position, wrapping around capacity as needed.
-  // Requires `index` < capacity()
-  inline index_type advance(index_type index) const;
-
-  // Advances the provided position by 'n`, wrapping around capacity as needed.
-  // Requires `index` < capacity() and `n` <= capacity.
-  inline index_type advance(index_type index, index_type n) const;
-
-  // Retreats the provided position, wrapping around 0 as needed.
-  // Requires `index` < capacity()
-  inline index_type retreat(index_type index) const;
-
-  // Retreats the provided position by 'n', wrapping around 0 as needed.
-  // Requires `index` < capacity()
-  inline index_type retreat(index_type index, index_type n) const;
-
-  // Returns the logical begin position of entry `index`
-  pos_type const& entry_begin_pos(index_type index) const {
-    return (index == head_) ? begin_pos_ : entry_end_pos(retreat(index));
-  }
-
-  // Returns the physical start offset of entry `index`
-  size_t entry_start_offset(index_type index) const {
-    return Distance(begin_pos_, entry_begin_pos(index));
-  }
-
-  // Returns the physical end offset of entry `index`
-  size_t entry_end_offset(index_type index) const {
-    return Distance(begin_pos_, entry_end_pos(index));
-  }
-
-  // Returns the data length for entry `index`
-  size_t entry_length(index_type index) const {
-    return Distance(entry_begin_pos(index), entry_end_pos(index));
-  }
-
-  // Returns the data for entry `index`
-  absl::string_view entry_data(index_type index) const;
-
-  // Returns the position for `offset` as {index, prefix}. `index` holds the
-  // index of the entry at the specified offset and `prefix` holds the relative
-  // offset inside that entry.
-  // Requires `offset` < length.
-  //
-  // For example we can implement GetCharacter(offset) as:
-  //   char GetCharacter(size_t offset) {
-  //     Position pos = this->Find(offset);
-  //     return this->entry_data(pos.pos)[pos.offset];
-  //   }
-  inline Position Find(size_t offset) const;
-
-  // Find starting at `head`
-  inline Position Find(index_type head, size_t offset) const;
-
-  // Returns the tail position for `offset` as {tail index, suffix}.
-  // `tail index` holds holds the index of the entry holding the offset directly
-  // before 'offset` advanced by one. 'suffix` holds the relative offset from
-  // that relative offset in the entry to the end of the entry.
-  // For example, FindTail(length) will return {tail(), 0}, FindTail(length - 5)
-  // will return {retreat(tail), 5)} provided the preceding entry contains at
-  // least 5 bytes of data.
-  // Requires offset >= 1 && offset <= length.
-  //
-  // This function is very useful in functions that need to clip the end of some
-  // ring buffer such as 'RemovePrefix'.
-  // For example, we could implement RemovePrefix for non shared instances as:
-  //   void RemoveSuffix(size_t n) {
-  //     Position pos = FindTail(length - n);
-  //     UnrefEntries(pos.pos, this->tail_);
-  //     this->tail_ = pos.pos;
-  //     entry(retreat(pos.pos)).end_pos -= pos.offset;
-  //   }
-  inline Position FindTail(size_t offset) const;
-
-  // Find tail starting at `head`
-  inline Position FindTail(index_type head, size_t offset) const;
-
-  // Invokes f(index_type index) for each entry inside the range [head, tail>
-  template <typename F>
-  void ForEach(index_type head, index_type tail, F&& f) const {
-    index_type n1 = (tail > head) ? tail : capacity_;
-    for (index_type i = head; i < n1; ++i) f(i);
-    if (tail <= head) {
-      for (index_type i = 0; i < tail; ++i) f(i);
-    }
-  }
-
-  // Invokes f(index_type index) for each entry inside this instance.
-  template <typename F>
-  void ForEach(F&& f) const {
-    ForEach(head_, tail_, std::forward<F>(f));
-  }
-
-  // Dump this instance's data tp stream `s` in human readable format, excluding
-  // the actual data content itself. Intended for debug purposes only.
-  friend std::ostream& operator<<(std::ostream& s, const CordRepRing& rep);
-
- private:
-  enum class AddMode { kAppend, kPrepend };
-
-  using Layout = container_internal::Layout<pos_type, CordRep*, offset_type>;
-
-  class Filler;
-  class Transaction;
-  class CreateTransaction;
-
-  static constexpr size_t kLayoutAlignment = Layout::Partial().Alignment();
-
-  // Creates a new CordRepRing.
-  explicit CordRepRing(index_type capacity) : capacity_(capacity) {}
-
-  // Returns true if `index` is a valid index into this instance.
-  bool IsValidIndex(index_type index) const;
-
-  // Debug use only: validates the provided CordRepRing invariants.
-  // Verification of all CordRepRing methods can be enabled by defining
-  // EXTRA_CORD_RING_VALIDATION, i.e.: `--copts=-DEXTRA_CORD_RING_VALIDATION`
-  // Verification is VERY expensive, so only do it for debugging purposes.
-  static CordRepRing* Validate(CordRepRing* rep, const char* file = nullptr,
-                               int line = 0);
-
-  // Allocates a CordRepRing large enough to hold `capacity + extra' entries.
-  // The returned capacity may be larger if the allocated memory allows for it.
-  // The maximum capacity of a CordRepRing is capped at kMaxCapacity.
-  // Throws `std::length_error` if `capacity + extra' exceeds kMaxCapacity.
-  static CordRepRing* New(size_t capacity, size_t extra);
-
-  // Deallocates (but does not destroy) the provided ring buffer.
-  static void Delete(CordRepRing* rep);
-
-  // Destroys the provided ring buffer, decrementing the reference count of all
-  // contained child CordReps. The provided 1\`rep` should have a ref count of
-  // one (pre decrement destroy call observing `refcount.IsOne()`) or zero
-  // (post decrement destroy call observing `!refcount.Decrement()`).
-  static void Destroy(CordRepRing* rep);
-
-  // Returns a mutable reference to the logical end position array.
-  pos_type* entry_end_pos() {
-    return Layout::Partial().Pointer<0>(data_);
-  }
-
-  // Returns a mutable reference to the child pointer array.
-  CordRep** entry_child() {
-    return Layout::Partial(capacity()).Pointer<1>(data_);
-  }
-
-  // Returns a mutable reference to the data offset array.
-  offset_type* entry_data_offset() {
-    return Layout::Partial(capacity(), capacity()).Pointer<2>(data_);
-  }
-
-  // Find implementations for the non fast path 0 / length cases.
-  Position FindSlow(index_type head, size_t offset) const;
-  Position FindTailSlow(index_type head, size_t offset) const;
-
-  // Finds the index of the first node that is inside a reasonable distance
-  // of the node at `offset` from which we can continue with a linear search.
-  template <bool wrap>
-  index_type FindBinary(index_type head, index_type tail, size_t offset) const;
-
-  // Fills the current (initialized) instance from the provided source, copying
-  // entries [head, tail). Adds a reference to copied entries if `ref` is true.
-  template <bool ref>
-  void Fill(const CordRepRing* src, index_type head, index_type tail);
-
-  // Create a copy of 'rep', copying all entries [head, tail), allocating room
-  // for `extra` entries. Adds a reference on all copied entries.
-  static CordRepRing* Copy(CordRepRing* rep, index_type head, index_type tail,
-                           size_t extra = 0);
-
-  // Returns a Mutable CordRepRing reference from `rep` with room for at least
-  // `extra` additional nodes. Adopts a reference count from `rep`.
-  // This function will return `rep` if, and only if:
-  // - rep.entries + extra <= rep.capacity
-  // - rep.refcount == 1
-  // Otherwise, this function will create a new copy of `rep` with additional
-  // capacity to satisfy `extra` extra nodes, and unref the old `rep` instance.
-  //
-  // If a new CordRepRing can not be allocated, or the new capacity would exceed
-  // the maximum capacity, then the input is consumed only, and an exception is
-  // thrown.
-  static CordRepRing* Mutable(CordRepRing* rep, size_t extra);
-
-  // Slow path for Append(CordRepRing* rep, CordRep* child). This function is
-  // exercised if the provided `child` in Append() is not a leaf node, i.e., a
-  // ring buffer or old (concat) cord tree.
-  static CordRepRing* AppendSlow(CordRepRing* rep, CordRep* child);
-
-  // Appends the provided leaf node. Requires `child` to be FLAT or EXTERNAL.
-  static CordRepRing* AppendLeaf(CordRepRing* rep, CordRep* child,
-                                 size_t offset, size_t length);
-
-  // Prepends the provided leaf node. Requires `child` to be FLAT or EXTERNAL.
-  static CordRepRing* PrependLeaf(CordRepRing* rep, CordRep* child,
-                                  size_t offset, size_t length);
-
-  // Slow path for Prepend(CordRepRing* rep, CordRep* child). This function is
-  // exercised if the provided `child` in Prepend() is not a leaf node, i.e., a
-  // ring buffer or old (concat) cord tree.
-  static CordRepRing* PrependSlow(CordRepRing* rep, CordRep* child);
-
-  // Slow path for Create(CordRep* child, size_t extra). This function is
-  // exercised if the provided `child` in Prepend() is not a leaf node, i.e., a
-  // ring buffer or old (concat) cord tree.
-  static CordRepRing* CreateSlow(CordRep* child, size_t extra);
-
-  // Creates a new ring buffer from the provided `child` leaf node. Requires
-  // `child` to be FLAT or EXTERNAL. on `rep`.
-  // The returned ring buffer has a capacity of at least `1 + extra`
-  static CordRepRing* CreateFromLeaf(CordRep* child, size_t offset,
-                                     size_t length, size_t extra);
-
-  // Appends or prepends (depending on AddMode) the ring buffer in `ring' to
-  // `rep` starting at `offset` with length `len`.
-  template <AddMode mode>
-  static CordRepRing* AddRing(CordRepRing* rep, CordRepRing* ring,
-                              size_t offset, size_t len);
-
-  // Increases the data offset for entry `index` by `n`.
-  void AddDataOffset(index_type index, size_t n);
-
-  // Decreases the length for entry `index` by `n`.
-  void SubLength(index_type index, size_t n);
-
-  index_type head_;
-  index_type tail_;
-  index_type capacity_;
-  pos_type begin_pos_;
-
-  alignas(kLayoutAlignment) char data_[kLayoutAlignment];
-
-  friend struct CordRep;
-};
-
-constexpr size_t CordRepRing::AllocSize(size_t capacity) {
-  return sizeof(CordRepRing) - sizeof(data_) +
-         Layout(capacity, capacity, capacity).AllocSize();
-}
-
-inline constexpr size_t CordRepRing::Distance(pos_type pos, pos_type end_pos) {
-  return (end_pos - pos);
-}
-
-inline const char* CordRepRing::GetLeafData(const CordRep* rep) {
-  return rep->tag != EXTERNAL ? rep->flat()->Data() : rep->external()->base;
-}
-
-inline const char* CordRepRing::GetRepData(const CordRep* rep) {
-  if (rep->tag >= FLAT) return rep->flat()->Data();
-  if (rep->tag == EXTERNAL) return rep->external()->base;
-  return GetLeafData(rep->substring()->child) + rep->substring()->start;
-}
-
-inline CordRepRing::index_type CordRepRing::advance(index_type index) const {
-  assert(index < capacity_);
-  return ++index == capacity_ ? 0 : index;
-}
-
-inline CordRepRing::index_type CordRepRing::advance(index_type index,
-                                                    index_type n) const {
-  assert(index < capacity_ && n <= capacity_);
-  return (index += n) >= capacity_ ? index - capacity_ : index;
-}
-
-inline CordRepRing::index_type CordRepRing::retreat(index_type index) const {
-  assert(index < capacity_);
-  return (index > 0 ? index : capacity_) - 1;
-}
-
-inline CordRepRing::index_type CordRepRing::retreat(index_type index,
-                                                    index_type n) const {
-  assert(index < capacity_ && n <= capacity_);
-  return index >= n ? index - n : capacity_ - n + index;
-}
-
-inline absl::string_view CordRepRing::entry_data(index_type index) const {
-  size_t data_offset = entry_data_offset(index);
-  return {GetRepData(entry_child(index)) + data_offset, entry_length(index)};
-}
-
-inline bool CordRepRing::IsValidIndex(index_type index) const {
-  if (index >= capacity_) return false;
-  return (tail_ > head_) ? (index >= head_ && index < tail_)
-                         : (index >= head_ || index < tail_);
-}
-
-#ifndef EXTRA_CORD_RING_VALIDATION
-inline CordRepRing* CordRepRing::Validate(CordRepRing* rep,
-                                          const char* /*file*/, int /*line*/) {
-  return rep;
-}
-#endif
-
-inline CordRepRing::Position CordRepRing::Find(size_t offset) const {
-  assert(offset < length);
-  return (offset == 0) ? Position{head_, 0} : FindSlow(head_, offset);
-}
-
-inline CordRepRing::Position CordRepRing::Find(index_type head,
-                                               size_t offset) const {
-  assert(offset < length);
-  assert(IsValidIndex(head) && offset >= entry_start_offset(head));
-  return (offset == 0) ? Position{head_, 0} : FindSlow(head, offset);
-}
-
-inline CordRepRing::Position CordRepRing::FindTail(size_t offset) const {
-  assert(offset > 0 && offset <= length);
-  return (offset == length) ? Position{tail_, 0} : FindTailSlow(head_, offset);
-}
-
-inline CordRepRing::Position CordRepRing::FindTail(index_type head,
-                                                   size_t offset) const {
-  assert(offset > 0 && offset <= length);
-  assert(IsValidIndex(head) && offset >= entry_start_offset(head) + 1);
-  return (offset == length) ? Position{tail_, 0} : FindTailSlow(head, offset);
-}
-
-// Now that CordRepRing is defined, we can define CordRep's helper casts:
-inline CordRepRing* CordRep::ring() {
-  assert(IsRing());
-  return static_cast<CordRepRing*>(this);
-}
-
-inline const CordRepRing* CordRep::ring() const {
-  assert(IsRing());
-  return static_cast<const CordRepRing*>(this);
-}
-
-inline bool CordRepRing::IsFlat(absl::string_view* fragment) const {
-  if (entries() == 1) {
-    if (fragment) *fragment = entry_data(head());
-    return true;
-  }
-  return false;
-}
-
-inline bool CordRepRing::IsFlat(size_t offset, size_t len,
-                                absl::string_view* fragment) const {
-  const Position pos = Find(offset);
-  const absl::string_view data = entry_data(pos.index);
-  if (data.length() >= len && data.length() - len >= pos.offset) {
-    if (fragment) *fragment = data.substr(pos.offset, len);
-    return true;
-  }
-  return false;
-}
-
-std::ostream& operator<<(std::ostream& s, const CordRepRing& rep);
-
-}  // namespace cord_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_STRINGS_INTERNAL_CORD_REP_RING_H_
diff --git a/absl/strings/internal/cord_rep_ring_reader.h b/absl/strings/internal/cord_rep_ring_reader.h
deleted file mode 100644
index 7ceeaa0..0000000
--- a/absl/strings/internal/cord_rep_ring_reader.h
+++ /dev/null
@@ -1,118 +0,0 @@
-// Copyright 2021 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.
-
-#ifndef ABSL_STRINGS_INTERNAL_CORD_REP_RING_READER_H_
-#define ABSL_STRINGS_INTERNAL_CORD_REP_RING_READER_H_
-
-#include <cassert>
-#include <cstddef>
-#include <cstdint>
-
-#include "absl/strings/internal/cord_internal.h"
-#include "absl/strings/internal/cord_rep_ring.h"
-#include "absl/strings/string_view.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace cord_internal {
-
-// CordRepRingReader provides basic navigation over CordRepRing data.
-class CordRepRingReader {
- public:
-  // Returns true if this instance is not empty.
-  explicit operator bool() const { return ring_ != nullptr; }
-
-  // Returns the ring buffer reference for this instance, or nullptr if empty.
-  CordRepRing* ring() const { return ring_; }
-
-  // Returns the current node index inside the ring buffer for this instance.
-  // The returned value is undefined if this instance is empty.
-  CordRepRing::index_type index() const { return index_; }
-
-  // Returns the current node inside the ring buffer for this instance.
-  // The returned value is undefined if this instance is empty.
-  CordRep* node() const { return ring_->entry_child(index_); }
-
-  // Returns the length of the referenced ring buffer.
-  // Requires the current instance to be non empty.
-  size_t length() const {
-    assert(ring_);
-    return ring_->length;
-  }
-
-  // Returns the end offset of the last navigated-to chunk, which represents the
-  // total bytes 'consumed' relative to the start of the ring. The returned
-  // value is never zero. For example, initializing a reader with a ring buffer
-  // with a first chunk of 19 bytes will return consumed() = 19.
-  // Requires the current instance to be non empty.
-  size_t consumed() const {
-    assert(ring_);
-    return ring_->entry_end_offset(index_);
-  }
-
-  // Returns the number of bytes remaining beyond the last navigated-to chunk.
-  // Requires the current instance to be non empty.
-  size_t remaining() const {
-    assert(ring_);
-    return length() - consumed();
-  }
-
-  // Resets this instance to an empty value
-  void Reset() { ring_ = nullptr; }
-
-  // Resets this instance to the start of `ring`. `ring` must not be null.
-  // Returns a reference into the first chunk of the provided ring.
-  absl::string_view Reset(CordRepRing* ring) {
-    assert(ring);
-    ring_ = ring;
-    index_ = ring_->head();
-    return ring_->entry_data(index_);
-  }
-
-  // Navigates to the next chunk inside the reference ring buffer.
-  // Returns a reference into the navigated-to chunk.
-  // Requires remaining() to be non zero.
-  absl::string_view Next() {
-    assert(remaining());
-    index_ = ring_->advance(index_);
-    return ring_->entry_data(index_);
-  }
-
-  // Navigates to the chunk at offset `offset`.
-  // Returns a reference into the navigated-to chunk, adjusted for the relative
-  // position of `offset` into that chunk. For example, calling Seek(13) on a
-  // ring buffer containing 2 chunks of 10 and 20 bytes respectively will return
-  // a string view into the second chunk starting at offset 3 with a size of 17.
-  // Requires `offset` to be less than `length()`
-  absl::string_view Seek(size_t offset) {
-    assert(offset < length());
-    size_t current = ring_->entry_end_offset(index_);
-    CordRepRing::index_type hint = (offset >= current) ? index_ : ring_->head();
-    const CordRepRing::Position head = ring_->Find(hint, offset);
-    index_ = head.index;
-    auto data = ring_->entry_data(head.index);
-    data.remove_prefix(head.offset);
-    return data;
-  }
-
- private:
-  CordRepRing* ring_ = nullptr;
-  CordRepRing::index_type index_;
-};
-
-}  // namespace cord_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_STRINGS_INTERNAL_CORD_REP_RING_READER_H_
diff --git a/absl/strings/internal/cordz_info.cc b/absl/strings/internal/cordz_info.cc
index 515dfaf..b24c3da 100644
--- a/absl/strings/internal/cordz_info.cc
+++ b/absl/strings/internal/cordz_info.cc
@@ -21,7 +21,6 @@
 #include "absl/strings/internal/cord_internal.h"
 #include "absl/strings/internal/cord_rep_btree.h"
 #include "absl/strings/internal/cord_rep_crc.h"
-#include "absl/strings/internal/cord_rep_ring.h"
 #include "absl/strings/internal/cordz_handle.h"
 #include "absl/strings/internal/cordz_statistics.h"
 #include "absl/strings/internal/cordz_update_tracker.h"
@@ -33,8 +32,6 @@
 ABSL_NAMESPACE_BEGIN
 namespace cord_internal {
 
-using ::absl::base_internal::SpinLockHolder;
-
 #ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL
 constexpr size_t CordzInfo::kMaxStackDepth;
 #endif
@@ -79,6 +76,8 @@
   // adds the results to `statistics`. Note that node counts and memory sizes
   // are not initialized, computed values are added to any existing values.
   void AnalyzeCordRep(const CordRep* rep) {
+    ABSL_ASSERT(rep != nullptr);
+
     // Process all linear nodes.
     // As per the class comments, use refcout - 1 on the top level node, as the
     // top level node is assumed to be referenced only for analysis purposes.
@@ -86,7 +85,7 @@
     RepRef repref{rep, (refcount > 1) ? refcount - 1 : 1};
 
     // Process the top level CRC node, if present.
-    if (repref.rep->tag == CRC) {
+    if (repref.tag() == CRC) {
       statistics_.node_count++;
       statistics_.node_counts.crc++;
       memory_usage_.Add(sizeof(CordRepCrc), repref.refcount);
@@ -96,15 +95,14 @@
     // Process all top level linear nodes (substrings and flats).
     repref = CountLinearReps(repref, memory_usage_);
 
-    if (repref.rep != nullptr) {
-      if (repref.rep->tag == RING) {
-        AnalyzeRing(repref);
-      } else if (repref.rep->tag == BTREE) {
+    switch (repref.tag()) {
+      case CordRepKind::BTREE:
         AnalyzeBtree(repref);
-      } else {
-        // We should have either a concat, btree, or ring node if not null.
-        assert(false);
-      }
+        break;
+      default:
+        // We should have a btree node if not null.
+        ABSL_ASSERT(repref.tag() == CordRepKind::UNUSED_0);
+        break;
     }
 
     // Adds values to output
@@ -122,11 +120,19 @@
     const CordRep* rep;
     size_t refcount;
 
-    // Returns a 'child' RepRef which contains the cumulative reference count of
-    // this instance multiplied by the child's reference count.
+    // Returns a 'child' RepRef which contains the cumulative reference count
+    // of this instance multiplied by the child's reference count. Returns a
+    // nullptr RepRef value with a refcount of 0 if `child` is nullptr.
     RepRef Child(const CordRep* child) const {
+      if (child == nullptr) return RepRef{nullptr, 0};
       return RepRef{child, refcount * child->refcount.Get()};
     }
+
+    // Returns the tag of this rep, or UNUSED_0 if this instance is null
+    constexpr CordRepKind tag() const {
+      ABSL_ASSERT(rep == nullptr || rep->tag != CordRepKind::UNUSED_0);
+      return rep ? static_cast<CordRepKind>(rep->tag) : CordRepKind::UNUSED_0;
+    }
   };
 
   // Memory usage values
@@ -167,7 +173,7 @@
   // buffers where we count children unrounded.
   RepRef CountLinearReps(RepRef rep, MemoryUsage& memory_usage) {
     // Consume all substrings
-    while (rep.rep->tag == SUBSTRING) {
+    while (rep.tag() == SUBSTRING) {
       statistics_.node_count++;
       statistics_.node_counts.substring++;
       memory_usage.Add(sizeof(CordRepSubstring), rep.refcount);
@@ -175,7 +181,7 @@
     }
 
     // Consume possible FLAT
-    if (rep.rep->tag >= FLAT) {
+    if (rep.tag() >= FLAT) {
       size_t size = rep.rep->flat()->AllocatedSize();
       CountFlat(size);
       memory_usage.Add(size, rep.refcount);
@@ -183,7 +189,7 @@
     }
 
     // Consume possible external
-    if (rep.rep->tag == EXTERNAL) {
+    if (rep.tag() == EXTERNAL) {
       statistics_.node_count++;
       statistics_.node_counts.external++;
       size_t size = rep.rep->length + sizeof(CordRepExternalImpl<intptr_t>);
@@ -194,17 +200,6 @@
     return rep;
   }
 
-  // Analyzes the provided ring.
-  void AnalyzeRing(RepRef rep) {
-    statistics_.node_count++;
-    statistics_.node_counts.ring++;
-    const CordRepRing* ring = rep.rep->ring();
-    memory_usage_.Add(CordRepRing::AllocSize(ring->capacity()), rep.refcount);
-    ring->ForEach([&](CordRepRing::index_type pos) {
-      CountLinearReps(rep.Child(ring->entry_child(pos)), memory_usage_);
-    });
-  }
-
   // Analyzes the provided btree.
   void AnalyzeBtree(RepRef rep) {
     statistics_.node_count++;
diff --git a/absl/strings/internal/cordz_info_statistics_test.cc b/absl/strings/internal/cordz_info_statistics_test.cc
index 53d2f2e..d55773f 100644
--- a/absl/strings/internal/cordz_info_statistics_test.cc
+++ b/absl/strings/internal/cordz_info_statistics_test.cc
@@ -25,7 +25,6 @@
 #include "absl/strings/internal/cord_rep_btree.h"
 #include "absl/strings/internal/cord_rep_crc.h"
 #include "absl/strings/internal/cord_rep_flat.h"
-#include "absl/strings/internal/cord_rep_ring.h"
 #include "absl/strings/internal/cordz_info.h"
 #include "absl/strings/internal/cordz_sample_token.h"
 #include "absl/strings/internal/cordz_statistics.h"
@@ -123,11 +122,6 @@
   return sizeof(CordRepExternalImpl<intptr_t>) + rep->length;
 }
 
-template <>
-size_t SizeOf(const CordRepRing* rep) {
-  return CordRepRing::AllocSize(rep->capacity());
-}
-
 // Computes fair share memory used in a naive 'we dare to recurse' way.
 double FairShareImpl(CordRep* rep, size_t ref) {
   double self = 0.0, children = 0.0;
@@ -144,11 +138,6 @@
     for (CordRep*edge : rep->btree()->Edges()) {
       children += FairShareImpl(edge, ref);
     }
-  } else if (rep->tag == RING) {
-    self = SizeOf(rep->ring());
-    rep->ring()->ForEach([&](CordRepRing::index_type i) {
-      self += FairShareImpl(rep->ring()->entry_child(i), 1);
-    });
   } else {
     assert(false);
   }
@@ -294,64 +283,6 @@
   EXPECT_THAT(SampleCord(substring), EqStatistics(expected));
 }
 
-
-TEST(CordzInfoStatisticsTest, Ring) {
-  RefHelper ref;
-  auto* flat1 = Flat(240);
-  auto* flat2 = Flat(2000);
-  auto* flat3 = Flat(70);
-  auto* external = External(3000);
-  CordRepRing* ring = CordRepRing::Create(flat1);
-  ring = CordRepRing::Append(ring, flat2);
-  ring = CordRepRing::Append(ring, flat3);
-  ring = ref.NeedsUnref(CordRepRing::Append(ring, external));
-
-  CordzStatistics expected;
-  expected.size = ring->length;
-  expected.estimated_memory_usage = SizeOf(ring) + SizeOf(flat1) +
-                                    SizeOf(flat2) + SizeOf(flat3) +
-                                    SizeOf(external);
-  expected.estimated_fair_share_memory_usage = expected.estimated_memory_usage;
-  expected.node_count = 5;
-  expected.node_counts.flat = 3;
-  expected.node_counts.flat_128 = 1;
-  expected.node_counts.flat_256 = 1;
-  expected.node_counts.external = 1;
-  expected.node_counts.ring = 1;
-
-  EXPECT_THAT(SampleCord(ring), EqStatistics(expected));
-}
-
-TEST(CordzInfoStatisticsTest, SharedSubstringRing) {
-  RefHelper ref;
-  auto* flat1 = ref.Ref(Flat(240));
-  auto* flat2 = Flat(200);
-  auto* flat3 = Flat(70);
-  auto* external = ref.Ref(External(3000), 5);
-  CordRepRing* ring = CordRepRing::Create(flat1);
-  ring = CordRepRing::Append(ring, flat2);
-  ring = CordRepRing::Append(ring, flat3);
-  ring = ref.Ref(CordRepRing::Append(ring, external), 4);
-  auto* substring = ref.Ref(ref.NeedsUnref(Substring(ring)));
-
-
-  CordzStatistics expected;
-  expected.size = substring->length;
-  expected.estimated_memory_usage = SizeOf(ring) + SizeOf(flat1) +
-                                    SizeOf(flat2) + SizeOf(flat3) +
-                                    SizeOf(external) + SizeOf(substring);
-  expected.estimated_fair_share_memory_usage = FairShare(substring);
-  expected.node_count = 6;
-  expected.node_counts.flat = 3;
-  expected.node_counts.flat_128 = 1;
-  expected.node_counts.flat_256 = 2;
-  expected.node_counts.external = 1;
-  expected.node_counts.ring = 1;
-  expected.node_counts.substring = 1;
-
-  EXPECT_THAT(SampleCord(substring), EqStatistics(expected));
-}
-
 TEST(CordzInfoStatisticsTest, BtreeLeaf) {
   ASSERT_THAT(CordRepBtree::kMaxCapacity, Ge(3u));
   RefHelper ref;
@@ -452,8 +383,7 @@
 TEST(CordzInfoStatisticsTest, Crc) {
   RefHelper ref;
   auto* left = Flat(1000);
-  auto* crc =
-      ref.NeedsUnref(CordRepCrc::New(left, crc_internal::CrcCordState()));
+  auto* crc = ref.NeedsUnref(CordRepCrc::New(left, {}));
 
   CordzStatistics expected;
   expected.size = left->length;
@@ -467,6 +397,20 @@
   EXPECT_THAT(SampleCord(crc), EqStatistics(expected));
 }
 
+TEST(CordzInfoStatisticsTest, EmptyCrc) {
+  RefHelper ref;
+  auto* crc = ref.NeedsUnref(CordRepCrc::New(nullptr, {}));
+
+  CordzStatistics expected;
+  expected.size = 0;
+  expected.estimated_memory_usage = SizeOf(crc);
+  expected.estimated_fair_share_memory_usage = expected.estimated_memory_usage;
+  expected.node_count = 1;
+  expected.node_counts.crc = 1;
+
+  EXPECT_THAT(SampleCord(crc), EqStatistics(expected));
+}
+
 TEST(CordzInfoStatisticsTest, ThreadSafety) {
   Notification stop;
   static constexpr int kNumThreads = 8;
@@ -497,6 +441,7 @@
         InlineData cords[2];
         std::minstd_rand gen;
         std::uniform_int_distribution<int> coin_toss(0, 1);
+        std::uniform_int_distribution<int> dice_roll(1, 6);
 
         while (!stop.HasBeenNotified()) {
           for (InlineData& cord : cords) {
@@ -514,13 +459,21 @@
                 CordRep::Unref(cord.as_tree());
                 cord.set_inline_size(0);
               } else {
-                // Coin toss to 25% ring, 25% btree, and 50% flat.
+                // Coin toss to 50% btree, and 50% flat.
                 CordRep* rep = Flat(256);
                 if (coin_toss(gen) != 0) {
-                  if (coin_toss(gen) != 0) {
-                    rep = CordRepRing::Create(rep);
+                  rep = CordRepBtree::Create(rep);
+                }
+
+                // Maybe CRC this cord
+                if (dice_roll(gen) == 6) {
+                  if (dice_roll(gen) == 6) {
+                    // Empty CRC rep
+                    CordRep::Unref(rep);
+                    rep = CordRepCrc::New(nullptr, {});
                   } else {
-                    rep = CordRepBtree::Create(rep);
+                    // Regular CRC rep
+                    rep = CordRepCrc::New(rep, {});
                   }
                 }
                 cord.make_tree(rep);
diff --git a/absl/strings/internal/has_absl_stringify.h b/absl/strings/internal/has_absl_stringify.h
index 55a0850..f82cfe2 100644
--- a/absl/strings/internal/has_absl_stringify.h
+++ b/absl/strings/internal/has_absl_stringify.h
@@ -1,4 +1,4 @@
-// Copyright 2022 The Abseil Authors
+// Copyright 2024 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.
@@ -14,38 +14,27 @@
 
 #ifndef ABSL_STRINGS_INTERNAL_HAS_ABSL_STRINGIFY_H_
 #define ABSL_STRINGS_INTERNAL_HAS_ABSL_STRINGIFY_H_
-#include <string>
-#include <type_traits>
-#include <utility>
 
-#include "absl/strings/string_view.h"
+#include "absl/strings/has_absl_stringify.h"
+
+#include "absl/base/config.h"
 
 namespace absl {
 ABSL_NAMESPACE_BEGIN
 
 namespace strings_internal {
 
-// This is an empty class not intended to be used. It exists so that
-// `HasAbslStringify` can reference a universal class rather than needing to be
-// copied for each new sink.
-class UnimplementedSink {
- public:
-  void Append(size_t count, char ch);
-
-  void Append(string_view v);
-
-  // Support `absl::Format(&sink, format, args...)`.
-  friend void AbslFormatFlush(UnimplementedSink* sink, absl::string_view v);
-};
-
-template <typename T, typename = void>
-struct HasAbslStringify : std::false_type {};
-
-template <typename T>
-struct HasAbslStringify<
-    T, std::enable_if_t<std::is_void<decltype(AbslStringify(
-           std::declval<strings_internal::UnimplementedSink&>(),
-           std::declval<const T&>()))>::value>> : std::true_type {};
+// This exists to fix a circular dependency problem with the GoogleTest release.
+// GoogleTest referenced this internal file and this internal trait.  Since
+// simultaneous releases are not possible since once release must reference
+// another, we will temporarily add this back.
+// https://github.com/google/googletest/blob/v1.14.x/googletest/include/gtest/gtest-printers.h#L119
+//
+// This file can be deleted after the next Abseil and GoogleTest release.
+//
+// https://github.com/google/googletest/pull/4368#issuecomment-1717699895
+// https://github.com/google/googletest/pull/4368#issuecomment-1717699895
+using ::absl::HasAbslStringify;
 
 }  // namespace strings_internal
 
diff --git a/absl/strings/internal/memutil.cc b/absl/strings/internal/memutil.cc
index e2e7347..0bbd8aa 100644
--- a/absl/strings/internal/memutil.cc
+++ b/absl/strings/internal/memutil.cc
@@ -27,10 +27,18 @@
   const unsigned char* us2 = reinterpret_cast<const unsigned char*>(s2);
 
   for (size_t i = 0; i < len; i++) {
-    const int diff =
-        int{static_cast<unsigned char>(absl::ascii_tolower(us1[i]))} -
-        int{static_cast<unsigned char>(absl::ascii_tolower(us2[i]))};
-    if (diff != 0) return diff;
+    unsigned char c1 = us1[i];
+    unsigned char c2 = us2[i];
+    // If bytes are the same, they will be the same when converted to lower.
+    // So we only need to convert if bytes are not equal.
+    // NOTE(b/308193381): We do not use `absl::ascii_tolower` here in order
+    // to avoid its lookup table and improve performance.
+    if (c1 != c2) {
+      c1 = c1 >= 'A' && c1 <= 'Z' ? c1 - 'A' + 'a' : c1;
+      c2 = c2 >= 'A' && c2 <= 'Z' ? c2 - 'A' + 'a' : c2;
+      const int diff = int{c1} - int{c2};
+      if (diff != 0) return diff;
+    }
   }
   return 0;
 }
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 3ce30fe..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/internal/has_absl_stringify.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);
@@ -158,6 +167,7 @@
 auto FormatConvertImpl(const T& v, FormatConversionSpecImpl,
                        FormatSinkImpl* sink)
     -> std::enable_if_t<!std::is_enum<T>::value &&
+                            !std::is_same<T, absl::Cord>::value &&
                             std::is_void<decltype(AbslStringify(
                                 std::declval<FormatSink&>(), v))>::value,
                         ArgConvertResult<FormatConversionCharSetInternal::v>> {
@@ -202,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>
@@ -279,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,
@@ -333,7 +365,7 @@
 template <typename T>
 typename std::enable_if<std::is_enum<T>::value &&
                             !HasUserDefinedConvert<T>::value &&
-                            !strings_internal::HasAbslStringify<T>::value,
+                            !HasAbslStringify<T>::value,
                         IntegralConvertResult>::type
 FormatConvertImpl(T v, FormatConversionSpecImpl conv, FormatSinkImpl* sink);
 
@@ -440,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*.
@@ -447,20 +480,23 @@
   struct DecayType {
     static constexpr bool kHasUserDefined =
         str_format_internal::HasUserDefinedConvert<T>::value ||
-        strings_internal::HasAbslStringify<T>::value;
+        HasAbslStringify<T>::value;
     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<T,
-                   typename std::enable_if<
-                       !str_format_internal::HasUserDefinedConvert<T>::value &&
-                       !strings_internal::HasAbslStringify<T>::value &&
-                       std::is_enum<T>::value>::type> {
+  struct DecayType<
+      T, typename std::enable_if<
+             !str_format_internal::HasUserDefinedConvert<T>::value &&
+             !HasAbslStringify<T>::value && std::is_enum<T>::value>::type> {
     using type = decltype(+typename std::underlying_type<T>::type());
   };
 
@@ -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 16ff987..7f22277 100644
--- a/absl/strings/internal/str_format/convert_test.cc
+++ b/absl/strings/internal/str_format/convert_test.cc
@@ -12,24 +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 <cctype>
+#include <algorithm>
+#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
@@ -48,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;
@@ -100,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) {
@@ -111,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
@@ -119,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;
 }
@@ -230,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
 }
 
@@ -242,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) {
@@ -251,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
@@ -277,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);
 
@@ -312,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));
 }
 
@@ -435,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);
             }
@@ -458,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
@@ -474,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);
@@ -510,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;
@@ -684,7 +897,11 @@
   }
 
   // Remove duplicates to speed up the logic below.
-  std::sort(floats.begin(), floats.end());
+  std::sort(floats.begin(), floats.end(), [](const float a, const float b) {
+    if (std::isnan(a)) return false;
+    if (std::isnan(b)) return true;
+    return a < b;
+  });
   floats.erase(std::unique(floats.begin(), floats.end()), floats.end());
 
   TestWithMultipleFormatsHelper(floats, {});
@@ -758,7 +975,11 @@
   }
 
   // Remove duplicates to speed up the logic below.
-  std::sort(doubles.begin(), doubles.end());
+  std::sort(doubles.begin(), doubles.end(), [](const double a, const double b) {
+    if (std::isnan(a)) return false;
+    if (std::isnan(b)) return true;
+    return a < b;
+  });
   doubles.erase(std::unique(doubles.begin(), doubles.end()), doubles.end());
 
   TestWithMultipleFormatsHelper(doubles, skip_verify);
@@ -1059,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>
@@ -1236,9 +1457,16 @@
 // Sanity check to make sure that we are testing what we think we're testing on
 // e.g. the x86_64+glibc platform.
 TEST_F(FormatConvertTest, GlibcHasCorrectTraits) {
-#if !defined(__GLIBC__) || !defined(__x86_64__)
-  return;
+#if defined(__GLIBC__) && defined(__x86_64__)
+  constexpr bool kIsSupportedGlibc = true;
+#else
+  constexpr bool kIsSupportedGlibc = false;
 #endif
+
+  if (!kIsSupportedGlibc) {
+    GTEST_SKIP() << "Test does not support this platform";
+  }
+
   const NativePrintfTraits &native_traits = VerifyNativeImplementation();
   // If one of the following tests break then it is either because the above PP
   // macro guards failed to exclude a new platform (likely) or because something
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/match.cc b/absl/strings/match.cc
index 3b81b2c..72ae6a4 100644
--- a/absl/strings/match.cc
+++ b/absl/strings/match.cc
@@ -17,10 +17,13 @@
 #include <algorithm>
 #include <cstdint>
 
+#include "absl/base/config.h"
 #include "absl/base/internal/endian.h"
+#include "absl/base/optimization.h"
 #include "absl/numeric/bits.h"
 #include "absl/strings/ascii.h"
 #include "absl/strings/internal/memutil.h"
+#include "absl/strings/string_view.h"
 
 namespace absl {
 ABSL_NAMESPACE_BEGIN
diff --git a/absl/strings/match_test.cc b/absl/strings/match_test.cc
index 71618f7..6218ce4 100644
--- a/absl/strings/match_test.cc
+++ b/absl/strings/match_test.cc
@@ -14,7 +14,10 @@
 
 #include "absl/strings/match.h"
 
+#include <string>
+
 #include "gtest/gtest.h"
+#include "absl/strings/string_view.h"
 
 namespace {
 
diff --git a/absl/strings/numbers.cc b/absl/strings/numbers.cc
index c43c6bc..882c3a8 100644
--- a/absl/strings/numbers.cc
+++ b/absl/strings/numbers.cc
@@ -20,32 +20,36 @@
 #include <algorithm>
 #include <cassert>
 #include <cfloat>  // for DBL_DIG and FLT_DIG
+#include <climits>
 #include <cmath>   // for HUGE_VAL
+#include <cstddef>
 #include <cstdint>
 #include <cstdio>
 #include <cstdlib>
 #include <cstring>
 #include <iterator>
 #include <limits>
-#include <memory>
+#include <system_error>  // NOLINT(build/c++11)
+#include <type_traits>
 #include <utility>
 
 #include "absl/base/attributes.h"
+#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"
 #include "absl/strings/ascii.h"
 #include "absl/strings/charconv.h"
-#include "absl/strings/escaping.h"
-#include "absl/strings/internal/memutil.h"
 #include "absl/strings/match.h"
-#include "absl/strings/str_cat.h"
+#include "absl/strings/string_view.h"
 
 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 +80,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 +111,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") ||
@@ -155,29 +159,71 @@
 constexpr uint64_t kFourZeroBytes = 0x01010101 * '0';
 constexpr uint64_t kEightZeroBytes = 0x0101010101010101ull * '0';
 
-// * 103 / 1024 is a division by 10 for values from 0 to 99. It's also a
-// division of a structure [k takes 2 bytes][m takes 2 bytes], then * 103 / 1024
-// will be [k / 10][m / 10]. It allows parallel division.
-constexpr uint64_t kDivisionBy10Mul = 103u;
-constexpr uint64_t kDivisionBy10Div = 1 << 10;
-
-// * 10486 / 1048576 is a division by 100 for values from 0 to 9999.
-constexpr uint64_t kDivisionBy100Mul = 10486u;
-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) {
-  int num_digits = static_cast<int>(n - 10) >> 8;
-  uint32_t base = kTwoZeroBytes;
-  uint32_t div10 = (n * kDivisionBy10Mul) / kDivisionBy10Div;
-  uint32_t mod10 = n - 10u * div10;
-  base += div10 + (mod10 << 8);
-  base >>= num_digits & 8;
-  little_endian::Store16(out_str, static_cast<uint16_t>(base));
-  return out_str + 2 + num_digits;
+template <typename T>
+constexpr T Pow(T base, uint32_t n) {
+  // Exponentiation by squaring
+  return static_cast<T>((n > 1 ? Pow(base * base, n >> 1) : static_cast<T>(1)) *
+                        ((n & 1) ? base : static_cast<T>(1)));
 }
 
-inline char* EncodeTenThousand(uint32_t n, char* out_str) {
+// Given n, calculates C where the following holds for all 0 <= x < Pow(100, n):
+// x / Pow(10, n) == x * C / Pow(2, n * 10)
+// In other words, it allows us to divide by a power of 10 via a single
+// multiplication and bit shifts, assuming the input will be smaller than the
+// square of that power of 10.
+template <typename T>
+constexpr T ComputePowerOf100DivisionCoefficient(uint32_t n) {
+  if (n > 4) {
+    // This doesn't work for large powers of 100, due to overflow
+    abort();
+  }
+  T denom = 16 - 1;
+  T num = (denom + 1) - 10;
+  T gcd = 3;  // Greatest common divisor of numerator and denominator
+  denom = Pow(denom / gcd, n);
+  num = Pow(num / gcd, 9 * n);
+  T quotient = num / denom;
+  if (num % denom >= denom / 2) {
+    // Round up, since the remainder is more than half the denominator
+    ++quotient;
+  }
+  return quotient;
+}
+
+// * kDivisionBy10Mul / kDivisionBy10Div is a division by 10 for values from 0
+// to 99. It's also a division of a structure [k takes 2 bytes][m takes 2
+// bytes], then * kDivisionBy10Mul / kDivisionBy10Div will be [k / 10][m / 10].
+// It allows parallel division.
+constexpr uint64_t kDivisionBy10Mul =
+    ComputePowerOf100DivisionCoefficient<uint64_t>(1);
+static_assert(kDivisionBy10Mul == 103,
+              "division coefficient for 10 is incorrect");
+constexpr uint64_t kDivisionBy10Div = 1 << 10;
+
+// * kDivisionBy100Mul / kDivisionBy100Div is a division by 100 for values from
+// 0 to 9999.
+constexpr uint64_t kDivisionBy100Mul =
+    ComputePowerOf100DivisionCoefficient<uint64_t>(2);
+static_assert(kDivisionBy100Mul == 10486,
+              "division coefficient for 100 is incorrect");
+constexpr uint64_t kDivisionBy100Div = 1 << 20;
+
+static_assert(ComputePowerOf100DivisionCoefficient<uint64_t>(3) == 1073742,
+              "division coefficient for 1000 is incorrect");
+
+// Same as `PrepareEightDigits`, but produces 2 digits for integers < 100.
+inline uint32_t PrepareTwoDigitsImpl(uint32_t i, bool reversed) {
+  assert(i < 100);
+  uint32_t div10 = (i * kDivisionBy10Mul) / kDivisionBy10Div;
+  uint32_t mod10 = i - 10u * div10;
+  return (div10 << (reversed ? 8 : 0)) + (mod10 << (reversed ? 0 : 8));
+}
+inline uint32_t PrepareTwoDigits(uint32_t i) {
+  return PrepareTwoDigitsImpl(i, false);
+}
+
+// Same as `PrepareEightDigits`, but produces 4 digits for integers < 10000.
+inline uint32_t PrepareFourDigitsImpl(uint32_t n, bool reversed) {
   // 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
@@ -185,146 +231,335 @@
   // strip trailing zeros, add ASCII '0000' and return.
   uint32_t div100 = (n * kDivisionBy100Mul) / kDivisionBy100Div;
   uint32_t mod100 = n - 100ull * div100;
-  uint32_t hundreds = (mod100 << 16) + div100;
+  uint32_t hundreds =
+      (mod100 << (reversed ? 0 : 16)) + (div100 << (reversed ? 16 : 0));
   uint32_t tens = (hundreds * kDivisionBy10Mul) / kDivisionBy10Div;
   tens &= (0xFull << 16) | 0xFull;
-  tens += (hundreds - 10ull * tens) << 8;
-  ABSL_ASSUME(tens != 0);
-  // The result can contain trailing zero bits, we need to strip them to a first
-  // significant byte in a final representation. For example, for n = 123, we
-  // have tens to have representation \0\1\2\3. We do `& -8` to round
-  // to a multiple to 8 to strip zero bytes, not all zero bits.
-  // countr_zero to help.
-  // 0 minus 8 to make MSVC happy.
-  uint32_t zeroes = static_cast<uint32_t>(absl::countr_zero(tens)) & (0 - 8ull);
-  tens += kFourZeroBytes;
-  tens >>= zeroes;
-  little_endian::Store32(out_str, tens);
-  return out_str + sizeof(tens) - zeroes / 8;
+  tens = (tens << (reversed ? 8 : 0)) +
+         static_cast<uint32_t>((hundreds - 10ull * tens) << (reversed ? 0 : 8));
+  return tens;
+}
+inline uint32_t PrepareFourDigits(uint32_t n) {
+  return PrepareFourDigitsImpl(n, false);
+}
+inline uint32_t PrepareFourDigitsReversed(uint32_t n) {
+  return PrepareFourDigitsImpl(n, true);
 }
 
-// Prepare functions return an integer that should be written to out_str
-// (but possibly include trailing zeros).
-// For hi < 10000, lo < 10000 returns uint64_t as encoded in ASCII with
-// possibly trailing zeroes of the number hi * 10000 + lo.
-inline uint64_t PrepareTenThousands(uint64_t hi, uint64_t lo) {
-  uint64_t merged = hi | (lo << 32);
+// Helper function to produce an ASCII representation of `i`.
+//
+// Function returns an 8-byte integer which when summed with `kEightZeroBytes`,
+// can be treated as a printable buffer with ascii representation of `i`,
+// possibly with leading zeros.
+//
+// Example:
+//
+//  uint64_t buffer = PrepareEightDigits(102030) + kEightZeroBytes;
+//  char* ascii = reinterpret_cast<char*>(&buffer);
+//  // Note two leading zeros:
+//  EXPECT_EQ(absl::string_view(ascii, 8), "00102030");
+//
+// If `Reversed` is set to true, the result becomes reversed to "03020100".
+//
+// Pre-condition: `i` must be less than 100000000.
+inline uint64_t PrepareEightDigitsImpl(uint32_t i, bool reversed) {
+  ABSL_ASSUME(i < 10000'0000);
+  // Prepare 2 blocks of 4 digits "in parallel".
+  uint32_t hi = i / 10000;
+  uint32_t lo = i % 10000;
+  uint64_t merged = (uint64_t{hi} << (reversed ? 32 : 0)) |
+                    (uint64_t{lo} << (reversed ? 0 : 32));
   uint64_t div100 = ((merged * kDivisionBy100Mul) / kDivisionBy100Div) &
                     ((0x7Full << 32) | 0x7Full);
   uint64_t mod100 = merged - 100ull * div100;
-  uint64_t hundreds = (mod100 << 16) + div100;
+  uint64_t hundreds =
+      (mod100 << (reversed ? 0 : 16)) + (div100 << (reversed ? 16 : 0));
   uint64_t tens = (hundreds * kDivisionBy10Mul) / kDivisionBy10Div;
   tens &= (0xFull << 48) | (0xFull << 32) | (0xFull << 16) | 0xFull;
-  tens += (hundreds - 10ull * tens) << 8;
+  tens = (tens << (reversed ? 8 : 0)) +
+         ((hundreds - 10ull * tens) << (reversed ? 0 : 8));
   return tens;
 }
+inline uint64_t PrepareEightDigits(uint32_t i) {
+  return PrepareEightDigitsImpl(i, false);
+}
+inline uint64_t PrepareEightDigitsReversed(uint32_t i) {
+  return PrepareEightDigitsImpl(i, true);
+}
 
-inline char* EncodeFullU32(uint32_t n, char* out_str) {
-  if (n < 100'000'000) {
-    uint64_t bottom = PrepareTenThousands(n / 10000, n % 10000);
-    ABSL_ASSUME(bottom != 0);
-    // 0 minus 8 to make MSVC happy.
-    uint32_t zeroes = static_cast<uint32_t>(absl::countr_zero(bottom))
-        & (0 - 8ull);
-    uint64_t bottom_res = bottom + kEightZeroBytes;
-    bottom_res >>= zeroes;
-    little_endian::Store64(out_str, bottom_res);
-    return out_str + sizeof(bottom) - zeroes / 8;
+template <typename T, typename BackwardIt>
+class FastUIntToStringConverter {
+  static_assert(
+      std::is_same<T, decltype(+std::declval<T>())>::value,
+      "to avoid code bloat, only instantiate this for int and larger types");
+  static_assert(std::is_unsigned<T>::value,
+                "this class is only for unsigned types");
+
+ public:
+  // Outputs the given number backward (like with std::copy_backward),
+  // starting from the end of the string.
+  // The number of digits in the number must have been already measured and
+  // passed *exactly*, otherwise the behavior is undefined.
+  // (This is an optimization, as calculating the number of digits again would
+  // slow down the hot path.)
+  // Returns an iterator to the start of the suffix that was appended.
+  static BackwardIt FastIntToBufferBackward(T v, BackwardIt end) {
+    // THIS IS A HOT FUNCTION with a very deliberate structure to exploit branch
+    // prediction and shorten the critical path for smaller numbers.
+    // Do not move around the if/else blocks or attempt to simplify it
+    // without benchmarking any changes.
+
+    if (v < 10) {
+      goto AT_LEAST_1 /* NOTE: mandatory for the 0 case */;
+    }
+    if (v < 1000) {
+      goto AT_LEAST_10;
+    }
+    if (v < 10000000) {
+      goto AT_LEAST_1000;
+    }
+
+    if (v >= 100000000 / 10) {
+      if (v >= 10000000000000000 / 10) {
+        DoFastIntToBufferBackward<8>(v, end);
+      }
+      DoFastIntToBufferBackward<8>(v, end);
+    }
+
+    if (v >= 10000 / 10) {
+    AT_LEAST_1000:
+      DoFastIntToBufferBackward<4>(v, end);
+    }
+
+    if (v >= 100 / 10) {
+    AT_LEAST_10:
+      DoFastIntToBufferBackward<2>(v, end);
+    }
+
+    if (v >= 10 / 10) {
+    AT_LEAST_1:
+      end = DoFastIntToBufferBackward(v, end, std::integral_constant<int, 1>());
+    }
+    return end;
   }
-  uint32_t top = n / 100'000'000;
-  n %= 100'000'000;
-  uint64_t bottom = PrepareTenThousands(n / 10000, n % 10000);
-  uint64_t bottom_res = bottom + kEightZeroBytes;
-  out_str = EncodeHundred(top, out_str);
-  little_endian::Store64(out_str, bottom_res);
-  return out_str + sizeof(bottom);
+
+ private:
+  // Only assume pointers are contiguous for now. String and vector iterators
+  // could be special-cased as well, but there's no need for them here.
+  // With C++20 we can probably switch to std::contiguous_iterator_tag.
+  static constexpr bool kIsContiguousIterator =
+      std::is_pointer<BackwardIt>::value;
+
+  template <int Exponent>
+  static void DoFastIntToBufferBackward(T& v, BackwardIt& end) {
+    constexpr T kModulus = Pow<T>(10, Exponent);
+    T remainder = static_cast<T>(v % kModulus);
+    v = static_cast<T>(v / kModulus);
+    end = DoFastIntToBufferBackward(remainder, end,
+                                    std::integral_constant<int, Exponent>());
+  }
+
+  static BackwardIt DoFastIntToBufferBackward(const T&, BackwardIt end,
+                                              std::integral_constant<int, 0>) {
+    return end;
+  }
+
+  static BackwardIt DoFastIntToBufferBackward(T v, BackwardIt end,
+                                              std::integral_constant<int, 1>) {
+    *--end = static_cast<char>('0' + v);
+    return DoFastIntToBufferBackward(v, end, std::integral_constant<int, 0>());
+  }
+
+  static BackwardIt DoFastIntToBufferBackward(T v, BackwardIt end,
+                                              std::integral_constant<int, 4>) {
+    if (kIsContiguousIterator) {
+      const uint32_t digits =
+          PrepareFourDigits(static_cast<uint32_t>(v)) + kFourZeroBytes;
+      end -= sizeof(digits);
+      little_endian::Store32(&*end, digits);
+    } else {
+      uint32_t digits =
+          PrepareFourDigitsReversed(static_cast<uint32_t>(v)) + kFourZeroBytes;
+      for (size_t i = 0; i < sizeof(digits); ++i) {
+        *--end = static_cast<char>(digits);
+        digits >>= CHAR_BIT;
+      }
+    }
+    return end;
+  }
+
+  static BackwardIt DoFastIntToBufferBackward(T v, BackwardIt end,
+                                              std::integral_constant<int, 8>) {
+    if (kIsContiguousIterator) {
+      const uint64_t digits =
+          PrepareEightDigits(static_cast<uint32_t>(v)) + kEightZeroBytes;
+      end -= sizeof(digits);
+      little_endian::Store64(&*end, digits);
+    } else {
+      uint64_t digits = PrepareEightDigitsReversed(static_cast<uint32_t>(v)) +
+                        kEightZeroBytes;
+      for (size_t i = 0; i < sizeof(digits); ++i) {
+        *--end = static_cast<char>(digits);
+        digits >>= CHAR_BIT;
+      }
+    }
+    return end;
+  }
+
+  template <int Digits>
+  static BackwardIt DoFastIntToBufferBackward(
+      T v, BackwardIt end, std::integral_constant<int, Digits>) {
+    constexpr int kLogModulus = Digits - Digits / 2;
+    constexpr T kModulus = Pow(static_cast<T>(10), kLogModulus);
+    bool is_safe_to_use_division_trick = Digits <= 8;
+    T quotient, remainder;
+    if (is_safe_to_use_division_trick) {
+      constexpr uint64_t kCoefficient =
+          ComputePowerOf100DivisionCoefficient<uint64_t>(kLogModulus);
+      quotient = (v * kCoefficient) >> (10 * kLogModulus);
+      remainder = v - quotient * kModulus;
+    } else {
+      quotient = v / kModulus;
+      remainder = v % kModulus;
+    }
+    end = DoFastIntToBufferBackward(remainder, end,
+                                    std::integral_constant<int, kLogModulus>());
+    return DoFastIntToBufferBackward(
+        quotient, end, std::integral_constant<int, Digits - kLogModulus>());
+  }
+};
+
+// Returns an iterator to the start of the suffix that was appended
+template <typename T, typename BackwardIt>
+std::enable_if_t<std::is_unsigned<T>::value, BackwardIt>
+DoFastIntToBufferBackward(T v, BackwardIt end, uint32_t digits) {
+  using PromotedT = std::decay_t<decltype(+v)>;
+  using Converter = FastUIntToStringConverter<PromotedT, BackwardIt>;
+  (void)digits;
+  return Converter().FastIntToBufferBackward(v, end);
+}
+
+template <typename T, typename BackwardIt>
+std::enable_if_t<std::is_signed<T>::value, BackwardIt>
+DoFastIntToBufferBackward(T v, BackwardIt end, uint32_t digits) {
+  if (absl::numbers_internal::IsNegative(v)) {
+    // Store the minus sign *before* we produce the number itself, not after.
+    // This gets us a tail call.
+    end[-static_cast<ptrdiff_t>(digits) - 1] = '-';
+  }
+  return DoFastIntToBufferBackward(
+      absl::numbers_internal::UnsignedAbsoluteValue(v), end, digits);
+}
+
+template <class T>
+std::enable_if_t<std::is_integral<T>::value, int>
+GetNumDigitsOrNegativeIfNegativeImpl(T v) {
+  const auto /* either bool or std::false_type */ is_negative =
+      absl::numbers_internal::IsNegative(v);
+  const int digits = static_cast<int>(absl::numbers_internal::Base10Digits(
+      absl::numbers_internal::UnsignedAbsoluteValue(v)));
+  return is_negative ? ~digits : digits;
 }
 
 }  // namespace
 
-void numbers_internal::PutTwoDigits(uint32_t i, char* buf) {
-  assert(i < 100);
-  uint32_t base = kTwoZeroBytes;
-  uint32_t div10 = (i * kDivisionBy10Mul) / kDivisionBy10Div;
-  uint32_t mod10 = i - 10u * div10;
-  base += div10 + (mod10 << 8);
-  little_endian::Store16(buf, static_cast<uint16_t>(base));
+void numbers_internal::PutTwoDigits(uint32_t i, absl::Nonnull<char*> buf) {
+  little_endian::Store16(
+      buf, static_cast<uint16_t>(PrepareTwoDigits(i) + kTwoZeroBytes));
 }
 
-char* numbers_internal::FastIntToBuffer(uint32_t n, char* out_str) {
-  if (n < 100) {
-    out_str = EncodeHundred(n, out_str);
-    goto set_last_zero;
-  }
-  if (n < 10000) {
-    out_str = EncodeTenThousand(n, out_str);
-    goto set_last_zero;
-  }
-  out_str = EncodeFullU32(n, out_str);
-set_last_zero:
-  *out_str = '\0';
-  return out_str;
-}
-
-char* numbers_internal::FastIntToBuffer(int32_t i, char* buffer) {
-  uint32_t u = static_cast<uint32_t>(i);
-  if (i < 0) {
-    *buffer++ = '-';
-    // We need to do the negation in modular (i.e., "unsigned")
-    // arithmetic; MSVC++ apparently warns for plain "-u", so
-    // we write the equivalent expression "0 - u" instead.
-    u = 0 - u;
-  }
-  return numbers_internal::FastIntToBuffer(u, buffer);
-}
-
-char* numbers_internal::FastIntToBuffer(uint64_t i, char* buffer) {
-  uint32_t u32 = static_cast<uint32_t>(i);
-  if (u32 == i) return numbers_internal::FastIntToBuffer(u32, buffer);
-
-  // 10**9 < 2**32 <= i < 10**10, we can do 2+8
-  uint64_t div08 = i / 100'000'000ull;
-  uint64_t mod08 = i % 100'000'000ull;
-  uint64_t mod_result =
-      PrepareTenThousands(mod08 / 10000, mod08 % 10000) + kEightZeroBytes;
-  if (i < 10'000'000'000ull) {
-    buffer = EncodeHundred(static_cast<uint32_t>(div08), buffer);
-    little_endian::Store64(buffer, mod_result);
-    buffer += 8;
-    goto set_last_zero;
-  }
-
-  // i < 10**16, in this case 8+8
-  if (i < 10'000'000'000'000'000ull) {
-    buffer = EncodeFullU32(static_cast<uint32_t>(div08), buffer);
-    little_endian::Store64(buffer, mod_result);
-    buffer += 8;
-    goto set_last_zero;
-  } else {
-    // 4 + 8 + 8
-    uint64_t div016 = i / 10'000'000'000'000'000ull;
-    buffer = EncodeTenThousand(static_cast<uint32_t>(div016), buffer);
-    uint64_t mid_result = div08 - div016 * 100'000'000ull;
-    mid_result = PrepareTenThousands(mid_result / 10000, mid_result % 10000) +
-                 kEightZeroBytes;
-    little_endian::Store64(buffer, mid_result);
-    buffer += 8;
-    little_endian::Store64(buffer, mod_result);
-    buffer += 8;
-    goto set_last_zero;
-  }
-set_last_zero:
-  *buffer = '\0';
+absl::Nonnull<char*> numbers_internal::FastIntToBuffer(
+    uint32_t i, absl::Nonnull<char*> buffer) {
+  const uint32_t digits = absl::numbers_internal::Base10Digits(i);
+  buffer += digits;
+  *buffer = '\0';  // We're going backward, so store this first
+  FastIntToBufferBackward(i, buffer, digits);
   return buffer;
 }
 
-char* numbers_internal::FastIntToBuffer(int64_t i, char* buffer) {
-  uint64_t u = static_cast<uint64_t>(i);
-  if (i < 0) {
-    *buffer++ = '-';
-    u = 0 - u;
-  }
-  return numbers_internal::FastIntToBuffer(u, buffer);
+absl::Nonnull<char*> numbers_internal::FastIntToBuffer(
+    int32_t i, absl::Nonnull<char*> buffer) {
+  buffer += static_cast<int>(i < 0);
+  uint32_t digits = absl::numbers_internal::Base10Digits(
+      absl::numbers_internal::UnsignedAbsoluteValue(i));
+  buffer += digits;
+  *buffer = '\0';  // We're going backward, so store this first
+  FastIntToBufferBackward(i, buffer, digits);
+  return buffer;
+}
+
+absl::Nonnull<char*> numbers_internal::FastIntToBuffer(
+    uint64_t i, absl::Nonnull<char*> buffer) {
+  uint32_t digits = absl::numbers_internal::Base10Digits(i);
+  buffer += digits;
+  *buffer = '\0';  // We're going backward, so store this first
+  FastIntToBufferBackward(i, buffer, digits);
+  return buffer;
+}
+
+absl::Nonnull<char*> numbers_internal::FastIntToBuffer(
+    int64_t i, absl::Nonnull<char*> buffer) {
+  buffer += static_cast<int>(i < 0);
+  uint32_t digits = absl::numbers_internal::Base10Digits(
+      absl::numbers_internal::UnsignedAbsoluteValue(i));
+  buffer += digits;
+  *buffer = '\0';  // We're going backward, so store this first
+  FastIntToBufferBackward(i, buffer, digits);
+  return buffer;
+}
+
+absl::Nonnull<char*> numbers_internal::FastIntToBufferBackward(
+    uint32_t i, absl::Nonnull<char*> buffer_end, uint32_t exact_digit_count) {
+  return DoFastIntToBufferBackward(i, buffer_end, exact_digit_count);
+}
+
+absl::Nonnull<char*> numbers_internal::FastIntToBufferBackward(
+    int32_t i, absl::Nonnull<char*> buffer_end, uint32_t exact_digit_count) {
+  return DoFastIntToBufferBackward(i, buffer_end, exact_digit_count);
+}
+
+absl::Nonnull<char*> numbers_internal::FastIntToBufferBackward(
+    uint64_t i, absl::Nonnull<char*> buffer_end, uint32_t exact_digit_count) {
+  return DoFastIntToBufferBackward(i, buffer_end, exact_digit_count);
+}
+
+absl::Nonnull<char*> numbers_internal::FastIntToBufferBackward(
+    int64_t i, absl::Nonnull<char*> buffer_end, uint32_t exact_digit_count) {
+  return DoFastIntToBufferBackward(i, buffer_end, exact_digit_count);
+}
+
+int numbers_internal::GetNumDigitsOrNegativeIfNegative(signed char v) {
+  return GetNumDigitsOrNegativeIfNegativeImpl(v);
+}
+int numbers_internal::GetNumDigitsOrNegativeIfNegative(unsigned char v) {
+  return GetNumDigitsOrNegativeIfNegativeImpl(v);
+}
+int numbers_internal::GetNumDigitsOrNegativeIfNegative(short v) {  // NOLINT
+  return GetNumDigitsOrNegativeIfNegativeImpl(v);
+}
+int numbers_internal::GetNumDigitsOrNegativeIfNegative(
+    unsigned short v) {  // NOLINT
+  return GetNumDigitsOrNegativeIfNegativeImpl(v);
+}
+int numbers_internal::GetNumDigitsOrNegativeIfNegative(int v) {
+  return GetNumDigitsOrNegativeIfNegativeImpl(v);
+}
+int numbers_internal::GetNumDigitsOrNegativeIfNegative(unsigned int v) {
+  return GetNumDigitsOrNegativeIfNegativeImpl(v);
+}
+int numbers_internal::GetNumDigitsOrNegativeIfNegative(long v) {  // NOLINT
+  return GetNumDigitsOrNegativeIfNegativeImpl(v);
+}
+int numbers_internal::GetNumDigitsOrNegativeIfNegative(
+    unsigned long v) {  // NOLINT
+  return GetNumDigitsOrNegativeIfNegativeImpl(v);
+}
+int numbers_internal::GetNumDigitsOrNegativeIfNegative(long long v) {  // NOLINT
+  return GetNumDigitsOrNegativeIfNegativeImpl(v);
+}
+int numbers_internal::GetNumDigitsOrNegativeIfNegative(
+    unsigned long long v) {  // NOLINT
+  return GetNumDigitsOrNegativeIfNegativeImpl(v);
 }
 
 // Given a 128-bit number expressed as a pair of uint64_t, high half first,
@@ -534,7 +769,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");
 
@@ -681,9 +917,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;
   }
@@ -968,7 +1205,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);
@@ -1005,7 +1242,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);
@@ -1049,8 +1286,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)) {
@@ -1064,8 +1301,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) {
@@ -1099,27 +1336,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..ad4e66b 100644
--- a/absl/strings/numbers.h
+++ b/absl/strings/numbers.h
@@ -32,6 +32,7 @@
 #endif
 
 #include <cstddef>
+#include <cstdint>
 #include <cstdlib>
 #include <cstring>
 #include <ctime>
@@ -39,9 +40,12 @@
 #include <string>
 #include <type_traits>
 
+#include "absl/base/attributes.h"
 #include "absl/base/config.h"
 #include "absl/base/internal/endian.h"
 #include "absl/base/macros.h"
+#include "absl/base/nullability.h"
+#include "absl/base/optimization.h"
 #include "absl/base/port.h"
 #include "absl/numeric/bits.h"
 #include "absl/numeric/int128.h"
@@ -59,7 +63,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 +75,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 +87,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 +98,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 +112,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,42 +141,136 @@
 //   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;
 
+template <class T>
+std::enable_if_t<!std::is_unsigned<T>::value, bool> IsNegative(const T& v) {
+  return v < T();
+}
+
+template <class T>
+std::enable_if_t<std::is_unsigned<T>::value, std::false_type> IsNegative(
+    const T&) {
+  // The integer is unsigned, so return a compile-time constant.
+  // This can help the optimizer avoid having to prove bool to be false later.
+  return std::false_type();
+}
+
+template <class T>
+std::enable_if_t<std::is_unsigned<std::decay_t<T>>::value, T&&>
+UnsignedAbsoluteValue(T&& v ABSL_ATTRIBUTE_LIFETIME_BOUND) {
+  // The value is unsigned; just return the original.
+  return std::forward<T>(v);
+}
+
+template <class T>
+ABSL_ATTRIBUTE_CONST_FUNCTION
+    std::enable_if_t<!std::is_unsigned<T>::value, std::make_unsigned_t<T>>
+    UnsignedAbsoluteValue(T v) {
+  using U = std::make_unsigned_t<T>;
+  return IsNegative(v) ? U() - static_cast<U>(v) : static_cast<U>(v);
+}
+
+// Returns the number of base-10 digits in the given number.
+// Note that this strictly counts digits. It does not count the sign.
+// The `initial_digits` parameter is the starting point, which is normally equal
+// to 1 because the number of digits in 0 is 1 (a special case).
+// However, callers may e.g. wish to change it to 2 to account for the sign.
+template <typename T>
+std::enable_if_t<std::is_unsigned<T>::value, uint32_t> Base10Digits(
+    T v, const uint32_t initial_digits = 1) {
+  uint32_t r = initial_digits;
+  // If code size becomes an issue, the 'if' stage can be removed for a minor
+  // performance loss.
+  for (;;) {
+    if (ABSL_PREDICT_TRUE(v < 10 * 10)) {
+      r += (v >= 10);
+      break;
+    }
+    if (ABSL_PREDICT_TRUE(v < 1000 * 10)) {
+      r += (v >= 1000) + 2;
+      break;
+    }
+    if (ABSL_PREDICT_TRUE(v < 100000 * 10)) {
+      r += (v >= 100000) + 4;
+      break;
+    }
+    r += 6;
+    v = static_cast<T>(v / 1000000);
+  }
+  return r;
+}
+
+template <typename T>
+std::enable_if_t<std::is_signed<T>::value, uint32_t> Base10Digits(
+    T v, uint32_t r = 1) {
+  // Branchlessly add 1 to account for a minus sign.
+  r += static_cast<uint32_t>(IsNegative(v));
+  return Base10Digits(UnsignedAbsoluteValue(v), r);
+}
+
+// These functions return the number of base-10 digits, but multiplied by -1 if
+// the input itself is negative. This is handy and efficient for later usage,
+// since the bitwise complement of the result becomes equal to the number of
+// characters required.
+ABSL_ATTRIBUTE_CONST_FUNCTION int GetNumDigitsOrNegativeIfNegative(
+    signed char v);
+ABSL_ATTRIBUTE_CONST_FUNCTION int GetNumDigitsOrNegativeIfNegative(
+    unsigned char v);
+ABSL_ATTRIBUTE_CONST_FUNCTION int GetNumDigitsOrNegativeIfNegative(
+    short v);  // NOLINT
+ABSL_ATTRIBUTE_CONST_FUNCTION int GetNumDigitsOrNegativeIfNegative(
+    unsigned short v);  // NOLINT
+ABSL_ATTRIBUTE_CONST_FUNCTION int GetNumDigitsOrNegativeIfNegative(int v);
+ABSL_ATTRIBUTE_CONST_FUNCTION int GetNumDigitsOrNegativeIfNegative(
+    unsigned int v);
+ABSL_ATTRIBUTE_CONST_FUNCTION int GetNumDigitsOrNegativeIfNegative(
+    long v);  // NOLINT
+ABSL_ATTRIBUTE_CONST_FUNCTION int GetNumDigitsOrNegativeIfNegative(
+    unsigned long v);  // NOLINT
+ABSL_ATTRIBUTE_CONST_FUNCTION int GetNumDigitsOrNegativeIfNegative(
+    long long v);  // NOLINT
+ABSL_ATTRIBUTE_CONST_FUNCTION int GetNumDigitsOrNegativeIfNegative(
+    unsigned long long v);  // NOLINT
+
 // Helper function for fast formatting of floating-point values.
 // The result is the same as printf's "%g", a.k.a. "%.6g"; that is, six
 // significant digits are returned, trailing zeros are removed, and numbers
 // 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
+// All of these 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 i, absl::Nonnull<char*> buffer);
+absl::Nonnull<char*> FastIntToBuffer(uint32_t i, absl::Nonnull<char*> buffer);
+absl::Nonnull<char*> FastIntToBuffer(int64_t i, absl::Nonnull<char*> buffer);
+absl::Nonnull<char*> FastIntToBuffer(uint64_t i, absl::Nonnull<char*> buffer);
 
 // 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
@@ -191,10 +294,63 @@
   }
 }
 
+// These functions do NOT add any null-terminator.
+// They return a pointer to the beginning of the written string.
+// The digit counts provided must *exactly* match the number of base-10 digits
+// in the number, or the behavior is undefined.
+// (i.e. do NOT count the minus sign, or over- or under-count the digits.)
+absl::Nonnull<char*> FastIntToBufferBackward(int32_t i,
+                                             absl::Nonnull<char*> buffer_end,
+                                             uint32_t exact_digit_count);
+absl::Nonnull<char*> FastIntToBufferBackward(uint32_t i,
+                                             absl::Nonnull<char*> buffer_end,
+                                             uint32_t exact_digit_count);
+absl::Nonnull<char*> FastIntToBufferBackward(int64_t i,
+                                             absl::Nonnull<char*> buffer_end,
+                                             uint32_t exact_digit_count);
+absl::Nonnull<char*> FastIntToBufferBackward(uint64_t i,
+                                             absl::Nonnull<char*> buffer_end,
+                                             uint32_t exact_digit_count);
+
+// 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>
+absl::Nonnull<char*> FastIntToBufferBackward(int_type i,
+                                             absl::Nonnull<char*> buffer_end,
+                                             uint32_t exact_digit_count) {
+  static_assert(
+      sizeof(i) <= 64 / 8,
+      "FastIntToBufferBackward works only with 64-bit-or-less integers.");
+  // This signed-ness check is used because it works correctly
+  // with enums, and it also serves to check that int_type is not a pointer.
+  // If one day something like std::is_signed<enum E> works, switch to it.
+  // These conditions are constexpr bools to suppress MSVC warning C4127.
+  constexpr bool kIsSigned = static_cast<int_type>(1) - 2 < 0;
+  constexpr bool kUse64Bit = sizeof(i) > 32 / 8;
+  if (kIsSigned) {
+    if (kUse64Bit) {
+      return FastIntToBufferBackward(static_cast<int64_t>(i), buffer_end,
+                                     exact_digit_count);
+    } else {
+      return FastIntToBufferBackward(static_cast<int32_t>(i), buffer_end,
+                                     exact_digit_count);
+    }
+  } else {
+    if (kUse64Bit) {
+      return FastIntToBufferBackward(static_cast<uint64_t>(i), buffer_end,
+                                     exact_digit_count);
+    } else {
+      return FastIntToBufferBackward(static_cast<uint32_t>(i), buffer_end,
+                                     exact_digit_count);
+    }
+  }
+}
+
 // 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 +393,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 +419,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/numbers_benchmark.cc b/absl/strings/numbers_benchmark.cc
index 6e79b3e..e7cb60a 100644
--- a/absl/strings/numbers_benchmark.cc
+++ b/absl/strings/numbers_benchmark.cc
@@ -13,6 +13,7 @@
 // limitations under the License.
 
 #include <cstdint>
+#include <limits>
 #include <random>
 #include <string>
 #include <type_traits>
@@ -23,6 +24,7 @@
 #include "absl/random/distributions.h"
 #include "absl/random/random.h"
 #include "absl/strings/numbers.h"
+#include "absl/strings/string_view.h"
 
 namespace {
 
diff --git a/absl/strings/numbers_test.cc b/absl/strings/numbers_test.cc
index 2864bda..1ceff70 100644
--- a/absl/strings/numbers_test.cc
+++ b/absl/strings/numbers_test.cc
@@ -28,6 +28,7 @@
 #include <cstdio>
 #include <cstdlib>
 #include <cstring>
+#include <ios>
 #include <limits>
 #include <numeric>
 #include <random>
@@ -38,12 +39,14 @@
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
 #include "absl/log/log.h"
+#include "absl/numeric/int128.h"
 #include "absl/random/distributions.h"
 #include "absl/random/random.h"
 #include "absl/strings/internal/numbers_test_common.h"
 #include "absl/strings/internal/ostringstream.h"
 #include "absl/strings/internal/pow10_helper.h"
 #include "absl/strings/str_cat.h"
+#include "absl/strings/string_view.h"
 
 namespace {
 
@@ -60,6 +63,7 @@
 using absl::strings_internal::strtouint64_test_cases;
 using testing::Eq;
 using testing::MatchesRegex;
+using testing::Pointee;
 
 // Number of floats to test with.
 // 5,000,000 is a reasonable default for a test that only takes a few seconds.
@@ -227,10 +231,15 @@
   CheckInt32(INT_MIN);
   CheckInt32(INT_MAX);
   CheckInt64(LONG_MIN);
+  CheckInt64(uint64_t{10000000});
+  CheckInt64(uint64_t{100000000});
   CheckInt64(uint64_t{1000000000});
   CheckInt64(uint64_t{9999999999});
   CheckInt64(uint64_t{100000000000000});
   CheckInt64(uint64_t{999999999999999});
+  CheckInt64(uint64_t{1000000000000000});
+  CheckInt64(uint64_t{10000000000000000});
+  CheckInt64(uint64_t{100000000000000000});
   CheckInt64(uint64_t{1000000000000000000});
   CheckInt64(uint64_t{1199999999999999999});
   CheckInt64(int64_t{-700000000000000000});
@@ -242,6 +251,8 @@
   CheckUInt64(uint64_t{999999999999999});
   CheckUInt64(uint64_t{1000000000000000000});
   CheckUInt64(uint64_t{1199999999999999999});
+  CheckUInt64(uint64_t{10000000000000000000u});
+  CheckUInt64(uint64_t{10200300040000500006u});
   CheckUInt64(std::numeric_limits<uint64_t>::max());
 
   for (int i = 0; i < 10000; i++) {
@@ -1712,4 +1723,25 @@
   }
 }
 
+template <typename Int>
+void ExpectWritesNull() {
+  {
+    char buf[absl::numbers_internal::kFastToBufferSize];
+    Int x = std::numeric_limits<Int>::min();
+    EXPECT_THAT(absl::numbers_internal::FastIntToBuffer(x, buf), Pointee('\0'));
+  }
+  {
+    char buf[absl::numbers_internal::kFastToBufferSize];
+    Int x = std::numeric_limits<Int>::max();
+    EXPECT_THAT(absl::numbers_internal::FastIntToBuffer(x, buf), Pointee('\0'));
+  }
+}
+
+TEST(FastIntToBuffer, WritesNull) {
+  ExpectWritesNull<int32_t>();
+  ExpectWritesNull<uint32_t>();
+  ExpectWritesNull<int64_t>();
+  ExpectWritesNull<uint32_t>();
+}
+
 }  // namespace
diff --git a/absl/strings/str_cat.cc b/absl/strings/str_cat.cc
index 2e49c31..098ab18 100644
--- a/absl/strings/str_cat.cc
+++ b/absl/strings/str_cat.cc
@@ -16,13 +16,15 @@
 
 #include <assert.h>
 
-#include <algorithm>
 #include <cstddef>
 #include <cstdint>
 #include <cstring>
+#include <initializer_list>
 #include <string>
+#include <type_traits>
 
-#include "absl/strings/ascii.h"
+#include "absl/base/config.h"
+#include "absl/base/nullability.h"
 #include "absl/strings/internal/resize_uninitialized.h"
 #include "absl/strings/numbers.h"
 #include "absl/strings/string_view.h"
@@ -30,6 +32,7 @@
 namespace absl {
 ABSL_NAMESPACE_BEGIN
 
+
 // ----------------------------------------------------------------------
 // StrCat()
 //    This merges the given strings or integers, with no delimiter. This
@@ -37,9 +40,10 @@
 //    of a mix of raw C strings, string_views, strings, and integer values.
 // ----------------------------------------------------------------------
 
+namespace {
 // Append is merely a version of memcpy that returns the address of the byte
 // after the area just overwritten.
-static char* Append(char* out, const AlphaNum& x) {
+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();
@@ -49,6 +53,8 @@
   return after;
 }
 
+}  // namespace
+
 std::string StrCat(const AlphaNum& a, const AlphaNum& b) {
   std::string result;
   absl::strings_internal::STLStringResizeUninitialized(&result,
@@ -92,6 +98,130 @@
 namespace strings_internal {
 
 // Do not call directly - these are not part of the public API.
+void STLStringAppendUninitializedAmortized(std::string* dest,
+                                           size_t to_append) {
+  strings_internal::AppendUninitializedTraits<std::string>::Append(dest,
+                                                                   to_append);
+}
+
+template <typename Integer>
+std::enable_if_t<std::is_integral<Integer>::value, std::string> IntegerToString(
+    Integer i) {
+  std::string str;
+  const auto /* either bool or std::false_type */ is_negative =
+      absl::numbers_internal::IsNegative(i);
+  const uint32_t digits = absl::numbers_internal::Base10Digits(
+      absl::numbers_internal::UnsignedAbsoluteValue(i));
+  absl::strings_internal::STLStringResizeUninitialized(
+      &str, digits + static_cast<uint32_t>(is_negative));
+  absl::numbers_internal::FastIntToBufferBackward(i, &str[str.size()], digits);
+  return str;
+}
+
+template <>
+std::string IntegerToString(long i) {  // NOLINT
+  if (sizeof(i) <= sizeof(int)) {
+    return IntegerToString(static_cast<int>(i));
+  } else {
+    return IntegerToString(static_cast<long long>(i));  // NOLINT
+  }
+}
+
+template <>
+std::string IntegerToString(unsigned long i) {  // NOLINT
+  if (sizeof(i) <= sizeof(unsigned int)) {
+    return IntegerToString(static_cast<unsigned int>(i));
+  } else {
+    return IntegerToString(static_cast<unsigned long long>(i));  // NOLINT
+  }
+}
+
+template <typename Float>
+std::enable_if_t<std::is_floating_point<Float>::value, std::string>
+FloatToString(Float f) {
+  std::string result;
+  strings_internal::STLStringResizeUninitialized(
+      &result, numbers_internal::kSixDigitsToBufferSize);
+  char* start = &result[0];
+  result.erase(numbers_internal::SixDigitsToBuffer(f, start));
+  return result;
+}
+
+std::string SingleArgStrCat(int x) { return IntegerToString(x); }
+std::string SingleArgStrCat(unsigned int x) { return IntegerToString(x); }
+// NOLINTNEXTLINE
+std::string SingleArgStrCat(long x) { return IntegerToString(x); }
+// NOLINTNEXTLINE
+std::string SingleArgStrCat(unsigned long x) { return IntegerToString(x); }
+// NOLINTNEXTLINE
+std::string SingleArgStrCat(long long x) { return IntegerToString(x); }
+// NOLINTNEXTLINE
+std::string SingleArgStrCat(unsigned long long x) { return IntegerToString(x); }
+std::string SingleArgStrCat(float x) { return FloatToString(x); }
+std::string SingleArgStrCat(double x) { return FloatToString(x); }
+
+template <class Integer>
+std::enable_if_t<std::is_integral<Integer>::value, void> AppendIntegerToString(
+    std::string& str, Integer i) {
+  const auto /* either bool or std::false_type */ is_negative =
+      absl::numbers_internal::IsNegative(i);
+  const uint32_t digits = absl::numbers_internal::Base10Digits(
+      absl::numbers_internal::UnsignedAbsoluteValue(i));
+  absl::strings_internal::STLStringAppendUninitializedAmortized(
+      &str, digits + static_cast<uint32_t>(is_negative));
+  absl::numbers_internal::FastIntToBufferBackward(i, &str[str.size()], digits);
+}
+
+template <>
+void AppendIntegerToString(std::string& str, long i) {  // NOLINT
+  if (sizeof(i) <= sizeof(int)) {
+    return AppendIntegerToString(str, static_cast<int>(i));
+  } else {
+    return AppendIntegerToString(str, static_cast<long long>(i));  // NOLINT
+  }
+}
+
+template <>
+void AppendIntegerToString(std::string& str,
+                           unsigned long i) {  // NOLINT
+  if (sizeof(i) <= sizeof(unsigned int)) {
+    return AppendIntegerToString(str, static_cast<unsigned int>(i));
+  } else {
+    return AppendIntegerToString(str,
+                                 static_cast<unsigned long long>(i));  // NOLINT
+  }
+}
+
+// `SingleArgStrAppend` overloads are defined here for the same reasons as with
+// `SingleArgStrCat` above.
+void SingleArgStrAppend(std::string& str, int x) {
+  return AppendIntegerToString(str, x);
+}
+
+void SingleArgStrAppend(std::string& str, unsigned int x) {
+  return AppendIntegerToString(str, x);
+}
+
+// NOLINTNEXTLINE
+void SingleArgStrAppend(std::string& str, long x) {
+  return AppendIntegerToString(str, x);
+}
+
+// NOLINTNEXTLINE
+void SingleArgStrAppend(std::string& str, unsigned long x) {
+  return AppendIntegerToString(str, x);
+}
+
+// NOLINTNEXTLINE
+void SingleArgStrAppend(std::string& str, long long x) {
+  return AppendIntegerToString(str, x);
+}
+
+// NOLINTNEXTLINE
+void SingleArgStrAppend(std::string& str, unsigned long long x) {
+  return AppendIntegerToString(str, x);
+}
+
 std::string CatPieces(std::initializer_list<absl::string_view> pieces) {
   std::string result;
   size_t total_size = 0;
@@ -120,15 +250,15 @@
   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 total_size = old_size;
+  size_t to_append = 0;
   for (absl::string_view piece : pieces) {
     ASSERT_NO_OVERLAP(*dest, piece);
-    total_size += piece.size();
+    to_append += piece.size();
   }
-  strings_internal::STLStringResizeUninitializedAmortized(dest, total_size);
+  strings_internal::STLStringAppendUninitializedAmortized(dest, to_append);
 
   char* const begin = &(*dest)[0];
   char* out = begin + old_size;
@@ -144,23 +274,23 @@
 
 }  // 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();
-  strings_internal::STLStringResizeUninitializedAmortized(dest,
-                                                          old_size + a.size());
+  strings_internal::STLStringAppendUninitializedAmortized(dest, a.size());
   char* const begin = &(*dest)[0];
   char* out = begin + old_size;
   out = Append(out, a);
   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();
-  strings_internal::STLStringResizeUninitializedAmortized(
-      dest, old_size + a.size() + b.size());
+  strings_internal::STLStringAppendUninitializedAmortized(dest,
+                                                          a.size() + b.size());
   char* const begin = &(*dest)[0];
   char* out = begin + old_size;
   out = Append(out, a);
@@ -168,14 +298,14 @@
   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);
   std::string::size_type old_size = dest->size();
-  strings_internal::STLStringResizeUninitializedAmortized(
-      dest, old_size + a.size() + b.size() + c.size());
+  strings_internal::STLStringAppendUninitializedAmortized(
+      dest, a.size() + b.size() + c.size());
   char* const begin = &(*dest)[0];
   char* out = begin + old_size;
   out = Append(out, a);
@@ -184,15 +314,15 @@
   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);
   ASSERT_NO_OVERLAP(*dest, d);
   std::string::size_type old_size = dest->size();
-  strings_internal::STLStringResizeUninitializedAmortized(
-      dest, old_size + a.size() + b.size() + c.size() + d.size());
+  strings_internal::STLStringAppendUninitializedAmortized(
+      dest, a.size() + b.size() + c.size() + d.size());
   char* const begin = &(*dest)[0];
   char* out = begin + old_size;
   out = Append(out, a);
diff --git a/absl/strings/str_cat.h b/absl/strings/str_cat.h
index d5f71ff..ea2c4dc 100644
--- a/absl/strings/str_cat.h
+++ b/absl/strings/str_cat.h
@@ -89,6 +89,8 @@
 
 #include <algorithm>
 #include <array>
+#include <cassert>
+#include <cstddef>
 #include <cstdint>
 #include <cstring>
 #include <string>
@@ -97,8 +99,11 @@
 #include <vector>
 
 #include "absl/base/attributes.h"
+#include "absl/base/nullability.h"
 #include "absl/base/port.h"
-#include "absl/strings/internal/has_absl_stringify.h"
+#include "absl/meta/type_traits.h"
+#include "absl/strings/has_absl_stringify.h"
+#include "absl/strings/internal/resize_uninitialized.h"
 #include "absl/strings/internal/stringify_sink.h"
 #include "absl/strings/numbers.h"
 #include "absl/strings/string_view.h"
@@ -201,7 +206,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>
@@ -253,10 +258,9 @@
                typename std::enable_if<(sizeof(Int) <= 8)>::type* = nullptr)
       : value(v >= 0 ? static_cast<uint64_t>(v)
                      : uint64_t{0} - static_cast<uint64_t>(v)),
-        width(spec == absl::kNoPad
-                  ? 1
-                  : spec >= absl::kSpacePad2 ? spec - absl::kSpacePad2 + 2
-                                             : spec - absl::kZeroPad2 + 2),
+        width(spec == absl::kNoPad       ? 1
+              : spec >= absl::kSpacePad2 ? spec - absl::kSpacePad2 + 2
+                                         : spec - absl::kZeroPad2 + 2),
         fill(spec >= absl::kSpacePad2 ? ' ' : '0'),
         neg(v < 0) {}
 
@@ -344,7 +348,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)
@@ -352,7 +356,7 @@
       : piece_(pc) {}
 
   template <typename T, typename = typename std::enable_if<
-                            strings_internal::HasAbslStringify<T>::value>::type>
+                            HasAbslStringify<T>::value>::type>
   AlphaNum(  // NOLINT(runtime/explicit)
       const T& v ABSL_ATTRIBUTE_LIFETIME_BOUND,
       strings_internal::StringifySink&& sink ABSL_ATTRIBUTE_LIFETIME_BOUND = {})
@@ -371,7 +375,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
@@ -379,17 +383,17 @@
   template <typename T,
             typename = typename std::enable_if<
                 std::is_enum<T>{} && std::is_convertible<T, int>{} &&
-                !strings_internal::HasAbslStringify<T>::value>::type>
+                !HasAbslStringify<T>::value>::type>
   AlphaNum(T e)  // NOLINT(runtime/explicit)
       : AlphaNum(+e) {}
 
   // This overload matches scoped enums.  We must explicitly cast to the
   // underlying type, but use integral promotion for the same reason as above.
   template <typename T,
-            typename std::enable_if<
-                std::is_enum<T>{} && !std::is_convertible<T, int>{} &&
-                    !strings_internal::HasAbslStringify<T>::value,
-                char*>::type = nullptr>
+            typename std::enable_if<std::is_enum<T>{} &&
+                                        !std::is_convertible<T, int>{} &&
+                                        !HasAbslStringify<T>::value,
+                                    char*>::type = nullptr>
   AlphaNum(T e)  // NOLINT(runtime/explicit)
       : AlphaNum(+static_cast<typename std::underlying_type<T>::type>(e)) {}
 
@@ -441,13 +445,48 @@
 
 // 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);
 
+void STLStringAppendUninitializedAmortized(std::string* dest, size_t to_append);
+
+// `SingleArgStrCat` overloads take built-in `int`, `long` and `long long` types
+// (signed / unsigned) to avoid ambiguity on the call side. If we used int32_t
+// and int64_t, then at least one of the three (`int` / `long` / `long long`)
+// would have been ambiguous when passed to `SingleArgStrCat`.
+std::string SingleArgStrCat(int x);
+std::string SingleArgStrCat(unsigned int x);
+std::string SingleArgStrCat(long x);                // NOLINT
+std::string SingleArgStrCat(unsigned long x);       // NOLINT
+std::string SingleArgStrCat(long long x);           // NOLINT
+std::string SingleArgStrCat(unsigned long long x);  // NOLINT
+std::string SingleArgStrCat(float x);
+std::string SingleArgStrCat(double x);
+
+// `SingleArgStrAppend` overloads are defined here for the same reasons as with
+// `SingleArgStrCat` above.
+void SingleArgStrAppend(std::string& str, int x);
+void SingleArgStrAppend(std::string& str, unsigned int x);
+void SingleArgStrAppend(std::string& str, long x);                // NOLINT
+void SingleArgStrAppend(std::string& str, unsigned long x);       // NOLINT
+void SingleArgStrAppend(std::string& str, long long x);           // NOLINT
+void SingleArgStrAppend(std::string& str, unsigned long long x);  // NOLINT
+
+template <typename T,
+          typename = std::enable_if_t<std::is_arithmetic<T>::value &&
+                                      !std::is_same<T, char>::value &&
+                                      !std::is_same<T, bool>::value>>
+using EnableIfFastCase = T;
+
 }  // namespace strings_internal
 
 ABSL_MUST_USE_RESULT inline std::string StrCat() { return std::string(); }
 
+template <typename T>
+ABSL_MUST_USE_RESULT inline std::string StrCat(
+    strings_internal::EnableIfFastCase<T> a) {
+  return strings_internal::SingleArgStrCat(a);
+}
 ABSL_MUST_USE_RESULT inline std::string StrCat(const AlphaNum& a) {
   return std::string(a.data(), a.size());
 }
@@ -495,24 +534,87 @@
 //   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()...});
 }
 
+template <class String, class T>
+std::enable_if_t<
+    std::is_integral<absl::strings_internal::EnableIfFastCase<T>>::value, void>
+StrAppend(absl::Nonnull<String*> result, T i) {
+  return absl::strings_internal::SingleArgStrAppend(*result, i);
+}
+
+// This overload is only selected if all the parameters are numbers that can be
+// handled quickly.
+// Later we can look into how we can extend this to more general argument
+// mixtures without bloating codegen too much, or copying unnecessarily.
+template <typename String, typename... T>
+std::enable_if_t<
+    (sizeof...(T) > 1),
+    std::common_type_t<std::conditional_t<
+        true, void, absl::strings_internal::EnableIfFastCase<T>>...>>
+StrAppend(absl::Nonnull<String*> str, T... args) {
+  // Do not add unnecessary variables, logic, or even "free" lambdas here.
+  // They can add overhead for the compiler and/or at run time.
+  // Furthermore, assume this function will be inlined.
+  // This function is carefully tailored to be able to be largely optimized away
+  // so that it becomes near-equivalent to the caller handling each argument
+  // individually while minimizing register pressure, so that the compiler
+  // can inline it with minimal overhead.
+
+  // First, calculate the total length, so we can perform just a single resize.
+  // Save all the lengths for later.
+  size_t total_length = 0;
+  const ptrdiff_t lengths[] = {
+      absl::numbers_internal::GetNumDigitsOrNegativeIfNegative(args)...};
+  for (const ptrdiff_t possibly_negative_length : lengths) {
+    // Lengths are negative for negative numbers. Keep them for later use, but
+    // take their absolute values for calculating total lengths;
+    total_length += possibly_negative_length < 0
+                        ? static_cast<size_t>(-possibly_negative_length)
+                        : static_cast<size_t>(possibly_negative_length);
+  }
+
+  // Now reserve space for all the arguments.
+  const size_t old_size = str->size();
+  absl::strings_internal::STLStringAppendUninitializedAmortized(str,
+                                                                total_length);
+
+  // Finally, output each argument one-by-one, from left to right.
+  size_t i = 0;  // The current argument we're processing
+  ptrdiff_t n;   // The length of the current argument
+  typename String::pointer pos = &(*str)[old_size];
+  using SomeTrivialEmptyType = std::false_type;
+  // Ugly code due to the lack of C++14 fold expression makes us.
+  const SomeTrivialEmptyType dummy1;
+  for (const SomeTrivialEmptyType& dummy2 :
+       {(/* Comma expressions are poor man's C++17 fold expression for C++14 */
+         (void)(n = lengths[i]),
+         (void)(n < 0 ? (void)(*pos++ = '-'), (n = ~n) : 0),
+         (void)absl::numbers_internal::FastIntToBufferBackward(
+             absl::numbers_internal::UnsignedAbsoluteValue(std::move(args)),
+             pos += n, static_cast<uint32_t>(n)),
+         (void)++i, dummy1)...}) {
+    (void)dummy2;  // Remove & migrate to fold expressions in C++17
+  }
+}
+
 // Helper function for the future StrCat default floating-point format, %.6g
 // This is fast.
 inline strings_internal::AlphaNumBuffer<
diff --git a/absl/strings/str_cat_benchmark.cc b/absl/strings/str_cat_benchmark.cc
index 02c4dbe..0a851e7 100644
--- a/absl/strings/str_cat_benchmark.cc
+++ b/absl/strings/str_cat_benchmark.cc
@@ -12,12 +12,20 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "absl/strings/str_cat.h"
-
+#include <array>
 #include <cstdint>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
 #include <string>
+#include <tuple>
+#include <utility>
 
 #include "benchmark/benchmark.h"
+#include "absl/random/log_uniform_int_distribution.h"
+#include "absl/random/random.h"
+#include "absl/strings/str_cat.h"
+#include "absl/strings/string_view.h"
 #include "absl/strings/substitute.h"
 
 namespace {
@@ -137,39 +145,65 @@
 }
 BENCHMARK(BM_DoubleToString_By_SixDigits);
 
-template <typename... Chunks>
-void BM_StrAppendImpl(benchmark::State& state, size_t total_bytes,
-                      Chunks... chunks) {
+template <typename Table, size_t... Index>
+void BM_StrAppendImpl(benchmark::State& state, Table table, size_t total_bytes,
+                      std::index_sequence<Index...>) {
   for (auto s : state) {
+    const size_t table_size = table.size();
+    size_t i = 0;
     std::string result;
     while (result.size() < total_bytes) {
-      absl::StrAppend(&result, chunks...);
+      absl::StrAppend(&result, std::get<Index>(table[i])...);
       benchmark::DoNotOptimize(result);
+      ++i;
+      i -= i >= table_size ? table_size : 0;
     }
   }
 }
 
-void BM_StrAppend(benchmark::State& state) {
-  const int total_bytes = state.range(0);
+template <typename Array>
+void BM_StrAppend(benchmark::State& state, Array&& table) {
+  const size_t total_bytes = state.range(0);
   const int chunks_at_a_time = state.range(1);
-  const absl::string_view kChunk = "0123456789";
 
   switch (chunks_at_a_time) {
     case 1:
-      return BM_StrAppendImpl(state, total_bytes, kChunk);
+      return BM_StrAppendImpl(state, std::forward<Array>(table), total_bytes,
+                              std::make_index_sequence<1>());
     case 2:
-      return BM_StrAppendImpl(state, total_bytes, kChunk, kChunk);
+      return BM_StrAppendImpl(state, std::forward<Array>(table), total_bytes,
+                              std::make_index_sequence<2>());
     case 4:
-      return BM_StrAppendImpl(state, total_bytes, kChunk, kChunk, kChunk,
-                              kChunk);
+      return BM_StrAppendImpl(state, std::forward<Array>(table), total_bytes,
+                              std::make_index_sequence<4>());
     case 8:
-      return BM_StrAppendImpl(state, total_bytes, kChunk, kChunk, kChunk,
-                              kChunk, kChunk, kChunk, kChunk, kChunk);
+      return BM_StrAppendImpl(state, std::forward<Array>(table), total_bytes,
+                              std::make_index_sequence<8>());
     default:
       std::abort();
   }
 }
 
+void BM_StrAppendStr(benchmark::State& state) {
+  using T = absl::string_view;
+  using Row = std::tuple<T, T, T, T, T, T, T, T>;
+  constexpr absl::string_view kChunk = "0123456789";
+  Row row = {kChunk, kChunk, kChunk, kChunk, kChunk, kChunk, kChunk, kChunk};
+  return BM_StrAppend(state, std::array<Row, 1>({row}));
+}
+
+template <typename T>
+void BM_StrAppendInt(benchmark::State& state) {
+  absl::BitGen rng;
+  absl::log_uniform_int_distribution<T> dist;
+  std::array<std::tuple<T, T, T, T, T, T, T, T>, (1 << 7)> table;
+  for (size_t i = 0; i < table.size(); ++i) {
+    table[i] = {dist(rng), dist(rng), dist(rng), dist(rng),
+                dist(rng), dist(rng), dist(rng), dist(rng)};
+  }
+  return BM_StrAppend(state, table);
+}
+
 template <typename B>
 void StrAppendConfig(B* benchmark) {
   for (int bytes : {10, 100, 1000, 10000}) {
@@ -182,6 +216,50 @@
   }
 }
 
-BENCHMARK(BM_StrAppend)->Apply(StrAppendConfig);
+BENCHMARK(BM_StrAppendStr)->Apply(StrAppendConfig);
+BENCHMARK(BM_StrAppendInt<int64_t>)->Apply(StrAppendConfig);
+BENCHMARK(BM_StrAppendInt<uint64_t>)->Apply(StrAppendConfig);
+BENCHMARK(BM_StrAppendInt<int32_t>)->Apply(StrAppendConfig);
+BENCHMARK(BM_StrAppendInt<uint32_t>)->Apply(StrAppendConfig);
+
+template <typename... Chunks>
+void BM_StrCatImpl(benchmark::State& state,
+                      Chunks... chunks) {
+  for (auto s : state) {
+    std::string result = absl::StrCat(chunks...);
+    benchmark::DoNotOptimize(result);
+  }
+}
+
+void BM_StrCat(benchmark::State& state) {
+  const int chunks_at_a_time = state.range(0);
+  const absl::string_view kChunk = "0123456789";
+
+  switch (chunks_at_a_time) {
+    case 1:
+      return BM_StrCatImpl(state, kChunk);
+    case 2:
+      return BM_StrCatImpl(state, kChunk, kChunk);
+    case 3:
+      return BM_StrCatImpl(state, kChunk, kChunk, kChunk);
+    case 4:
+      return BM_StrCatImpl(state, kChunk, kChunk, kChunk, kChunk);
+    default:
+      std::abort();
+  }
+}
+
+BENCHMARK(BM_StrCat)->Arg(1)->Arg(2)->Arg(3)->Arg(4);
+
+void BM_StrCat_int(benchmark::State& state) {
+  int i = 0;
+  for (auto s : state) {
+    std::string result = absl::StrCat(i);
+    benchmark::DoNotOptimize(result);
+    i = IncrementAlternatingSign(i);
+  }
+}
+
+BENCHMARK(BM_StrCat_int);
 
 }  // namespace
diff --git a/absl/strings/str_cat_test.cc b/absl/strings/str_cat_test.cc
index 2d74245..b30a86f 100644
--- a/absl/strings/str_cat_test.cc
+++ b/absl/strings/str_cat_test.cc
@@ -16,13 +16,16 @@
 
 #include "absl/strings/str_cat.h"
 
+#include <cstddef>
 #include <cstdint>
+#include <cstdlib>
+#include <limits>
 #include <string>
 #include <vector>
 
 #include "gtest/gtest.h"
 #include "absl/strings/str_format.h"
-#include "absl/strings/substitute.h"
+#include "absl/strings/string_view.h"
 
 #ifdef __ANDROID__
 // Android assert messages only go to system log, so death tests cannot inspect
@@ -36,6 +39,24 @@
 
 namespace {
 
+template <typename Integer>
+void VerifyInteger(Integer value) {
+  const std::string expected = std::to_string(value);
+
+  EXPECT_EQ(absl::StrCat(value), expected);
+
+  const char* short_prefix = "x";
+  const char* long_prefix = "2;k.msabxiuow2[09i;o3k21-93-9=29]";
+
+  std::string short_str = short_prefix;
+  absl::StrAppend(&short_str, value);
+  EXPECT_EQ(short_str, short_prefix + expected);
+
+  std::string long_str = long_prefix;
+  absl::StrAppend(&long_str, value);
+  EXPECT_EQ(long_str, long_prefix + expected);
+}
+
 // Test absl::StrCat of ints and longs of various sizes and signdedness.
 TEST(StrCat, Ints) {
   const short s = -1;  // NOLINT(runtime/int)
@@ -65,6 +86,34 @@
   EXPECT_EQ(answer, "-9-12");
   answer = absl::StrCat(uintptr, 0);
   EXPECT_EQ(answer, "130");
+
+  for (const uint32_t base : {2u, 10u}) {
+    for (const int extra_shift : {0, 12}) {
+      for (uint64_t i = 0; i < (1 << 8); ++i) {
+        uint64_t j = i;
+        while (true) {
+          uint64_t v = j ^ (extra_shift != 0 ? (j << extra_shift) * base : 0);
+          VerifyInteger(static_cast<bool>(v));
+          VerifyInteger(static_cast<wchar_t>(v));
+          VerifyInteger(static_cast<signed char>(v));
+          VerifyInteger(static_cast<unsigned char>(v));
+          VerifyInteger(static_cast<short>(v));               // NOLINT
+          VerifyInteger(static_cast<unsigned short>(v));      // NOLINT
+          VerifyInteger(static_cast<int>(v));                 // NOLINT
+          VerifyInteger(static_cast<unsigned int>(v));        // NOLINT
+          VerifyInteger(static_cast<long>(v));                // NOLINT
+          VerifyInteger(static_cast<unsigned long>(v));       // NOLINT
+          VerifyInteger(static_cast<long long>(v));           // NOLINT
+          VerifyInteger(static_cast<unsigned long long>(v));  // NOLINT
+          const uint64_t next = j == 0 ? 1 : j * base;
+          if (next <= j) {
+            break;
+          }
+          j = next;
+        }
+      }
+    }
+  }
 }
 
 TEST(StrCat, Enums) {
@@ -662,4 +711,20 @@
   EXPECT_EQ(absl::StrCat(e), "Choices");
 }
 
+template <typename Integer>
+void CheckSingleArgumentIntegerLimits() {
+  Integer max = std::numeric_limits<Integer>::max();
+  Integer min = std::numeric_limits<Integer>::min();
+
+  EXPECT_EQ(absl::StrCat(max), std::to_string(max));
+  EXPECT_EQ(absl::StrCat(min), std::to_string(min));
+}
+
+TEST(StrCat, SingleArgumentLimits) {
+  CheckSingleArgumentIntegerLimits<int32_t>();
+  CheckSingleArgumentIntegerLimits<uint32_t>();
+  CheckSingleArgumentIntegerLimits<int64_t>();
+  CheckSingleArgumentIntegerLimits<uint64_t>();
+}
+
 }  // namespace
diff --git a/absl/strings/str_format.h b/absl/strings/str_format.h
index 023e435..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
@@ -250,13 +258,13 @@
 // `v` uses `d` for signed integer values, `u` for unsigned integer values, `g`
 // for floating point values, and formats boolean values as "true"/"false"
 // (instead of 1 or 0 for booleans formatted using d). `const char*` is not
-// supported; please use `std:string` and `string_view`. `char` is also not
+// supported; please use `std::string` and `string_view`. `char` is also not
 // supported due to ambiguity of the type. This specifier does not support
 // modifiers.
 //
 // 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<
@@ -287,8 +295,8 @@
 // Example:
 //
 //   // Verified at compile time.
-//   absl::ParsedFormat<'s', 'd'> formatString("Welcome to %s, Number %d!");
-//   absl::StrFormat(formatString, "TheVillage", 6);
+//   absl::ParsedFormat<'s', 'd'> format_string("Welcome to %s, Number %d!");
+//   absl::StrFormat(format_string, "TheVillage", 6);
 //
 //   // Verified at runtime.
 //   auto format_runtime = absl::ParsedFormat<'d'>::New(format_string);
@@ -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(
@@ -381,7 +389,7 @@
 //
 // Writes to an output stream given a format string and zero or more arguments,
 // generally in a manner that is more efficient than streaming the result of
-// `absl:: StrFormat()`. The returned object must be streamed before the full
+// `absl::StrFormat()`. The returned object must be streamed before the full
 // expression ends.
 //
 // Example:
@@ -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 20fd028..3c52be1 100644
--- a/absl/strings/str_format_test.cc
+++ b/absl/strings/str_format_test.cc
@@ -14,16 +14,22 @@
 
 #include "absl/strings/str_format.h"
 
+#include <cerrno>
 #include <cstdarg>
 #include <cstdint>
 #include <cstdio>
+#include <ostream>
+#include <sstream>
 #include <string>
+#include <type_traits>
 
-#include "gmock/gmock.h"
 #include "gtest/gtest.h"
+#include "absl/base/config.h"
+#include "absl/base/macros.h"
 #include "absl/strings/cord.h"
 #include "absl/strings/str_cat.h"
 #include "absl/strings/string_view.h"
+#include "absl/types/span.h"
 
 namespace absl {
 ABSL_NAMESPACE_BEGIN
@@ -628,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_join.h b/absl/strings/str_join.h
index ee5ae7e..6a92c0f 100644
--- a/absl/strings/str_join.h
+++ b/absl/strings/str_join.h
@@ -80,7 +80,7 @@
 //       absl::StrJoin(v, ", ", [](std::string* out, absl::Duration dur) {
 //         absl::StrAppend(out, absl::FormatDuration(dur));
 //       });
-//   EXPECT_EQ("1s, 10ms", s);
+//   EXPECT_EQ(s, "1s, 10ms");
 //
 // The following standard formatters are provided within this file:
 //
@@ -164,21 +164,21 @@
 //   // of `absl::string_view` or even `const char*`.
 //   std::vector<std::string> v = {"foo", "bar", "baz"};
 //   std::string s = absl::StrJoin(v, "-");
-//   EXPECT_EQ("foo-bar-baz", s);
+//   EXPECT_EQ(s, "foo-bar-baz");
 //
 // Example 2:
 //   // Joins the values in the given `std::initializer_list<>` specified using
 //   // brace initialization. This pattern also works with an initializer_list
 //   // of ints or `absl::string_view` -- any `AlphaNum`-compatible type.
 //   std::string s = absl::StrJoin({"foo", "bar", "baz"}, "-");
-//   EXPECT_EQ("foo-bar-baz", s);
+//   EXPECT_EQs, "foo-bar-baz");
 //
 // Example 3:
 //   // Joins a collection of ints. This pattern also works with floats,
 //   // doubles, int64s -- any `StrCat()`-compatible type.
 //   std::vector<int> v = {1, 2, 3, -4};
 //   std::string s = absl::StrJoin(v, "-");
-//   EXPECT_EQ("1-2-3--4", s);
+//   EXPECT_EQ(s, "1-2-3--4");
 //
 // Example 4:
 //   // Joins a collection of pointer-to-int. By default, pointers are
@@ -189,7 +189,7 @@
 //   int x = 1, y = 2, z = 3;
 //   std::vector<int*> v = {&x, &y, &z};
 //   std::string s = absl::StrJoin(v, "-");
-//   EXPECT_EQ("1-2-3", s);
+//   EXPECT_EQ(s, "1-2-3");
 //
 // Example 5:
 //   // Dereferencing of `std::unique_ptr<>` is also supported:
@@ -198,42 +198,42 @@
 //   v.emplace_back(new int(2));
 //   v.emplace_back(new int(3));
 //   std::string s = absl::StrJoin(v, "-");
-//   EXPECT_EQ("1-2-3", s);
+//   EXPECT_EQ(s, "1-2-3");
 //
 // Example 6:
 //   // Joins a `std::map`, with each key-value pair separated by an equals
 //   // sign. This pattern would also work with, say, a
 //   // `std::vector<std::pair<>>`.
 //   std::map<std::string, int> m = {
-//       std::make_pair("a", 1),
-//       std::make_pair("b", 2),
-//       std::make_pair("c", 3)};
+//       {"a", 1},
+//       {"b", 2},
+//       {"c", 3}};
 //   std::string s = absl::StrJoin(m, ",", absl::PairFormatter("="));
-//   EXPECT_EQ("a=1,b=2,c=3", s);
+//   EXPECT_EQ(s, "a=1,b=2,c=3");
 //
 // Example 7:
 //   // These examples show how `absl::StrJoin()` handles a few common edge
 //   // cases:
 //   std::vector<std::string> v_empty;
-//   EXPECT_EQ("", absl::StrJoin(v_empty, "-"));
+//   EXPECT_EQ(absl::StrJoin(v_empty, "-"), "");
 //
 //   std::vector<std::string> v_one_item = {"foo"};
-//   EXPECT_EQ("foo", absl::StrJoin(v_one_item, "-"));
+//   EXPECT_EQ(absl::StrJoin(v_one_item, "-"), "foo");
 //
 //   std::vector<std::string> v_empty_string = {""};
-//   EXPECT_EQ("", absl::StrJoin(v_empty_string, "-"));
+//   EXPECT_EQ(absl::StrJoin(v_empty_string, "-"), "");
 //
 //   std::vector<std::string> v_one_item_empty_string = {"a", ""};
-//   EXPECT_EQ("a-", absl::StrJoin(v_one_item_empty_string, "-"));
+//   EXPECT_EQ(absl::StrJoin(v_one_item_empty_string, "-"), "a-");
 //
 //   std::vector<std::string> v_two_empty_string = {"", ""};
-//   EXPECT_EQ("-", absl::StrJoin(v_two_empty_string, "-"));
+//   EXPECT_EQ(absl::StrJoin(v_two_empty_string, "-"), "-");
 //
 // Example 8:
 //   // Joins a `std::tuple<T...>` of heterogeneous types, converting each to
 //   // a std::string using the `absl::AlphaNum` class.
 //   std::string s = absl::StrJoin(std::make_tuple(123, "abc", 0.456), "-");
-//   EXPECT_EQ("123-abc-0.456", s);
+//   EXPECT_EQ(s, "123-abc-0.456");
 
 template <typename Iterator, typename Formatter>
 std::string StrJoin(Iterator start, Iterator end, absl::string_view sep,
diff --git a/absl/strings/str_join_test.cc b/absl/strings/str_join_test.cc
index c986e86..449f95b 100644
--- a/absl/strings/str_join_test.cc
+++ b/absl/strings/str_join_test.cc
@@ -25,8 +25,9 @@
 #include <map>
 #include <memory>
 #include <ostream>
+#include <string>
 #include <tuple>
-#include <type_traits>
+#include <utility>
 #include <vector>
 
 #include "gtest/gtest.h"
diff --git a/absl/strings/str_replace.cc b/absl/strings/str_replace.cc
index 2bd5fa9..a7ab52f 100644
--- a/absl/strings/str_replace.cc
+++ b/absl/strings/str_replace.cc
@@ -14,7 +14,16 @@
 
 #include "absl/strings/str_replace.h"
 
+#include <cstddef>
+#include <initializer_list>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "absl/base/config.h"
+#include "absl/base/nullability.h"
 #include "absl/strings/str_cat.h"
+#include "absl/strings/string_view.h"
 
 namespace absl {
 ABSL_NAMESPACE_BEGIN
@@ -28,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;
@@ -74,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/str_replace_test.cc b/absl/strings/str_replace_test.cc
index 9d8c7f7..04b23af 100644
--- a/absl/strings/str_replace_test.cc
+++ b/absl/strings/str_replace_test.cc
@@ -16,11 +16,15 @@
 
 #include <list>
 #include <map>
+#include <string>
 #include <tuple>
+#include <utility>
+#include <vector>
 
 #include "gtest/gtest.h"
 #include "absl/strings/str_cat.h"
 #include "absl/strings/str_split.h"
+#include "absl/strings/string_view.h"
 
 TEST(StrReplaceAll, OneReplacement) {
   std::string s;
@@ -175,7 +179,7 @@
 }
 
 struct Cont {
-  Cont() {}
+  Cont() = default;
   explicit Cont(absl::string_view src) : data(src) {}
 
   absl::string_view data;
diff --git a/absl/strings/str_split.cc b/absl/strings/str_split.cc
index 72ba7c0..abe486b 100644
--- a/absl/strings/str_split.cc
+++ b/absl/strings/str_split.cc
@@ -15,16 +15,13 @@
 #include "absl/strings/str_split.h"
 
 #include <algorithm>
-#include <cassert>
-#include <cstdint>
+#include <cstddef>
 #include <cstdlib>
 #include <cstring>
-#include <iterator>
-#include <limits>
-#include <memory>
 
+#include "absl/base/config.h"
 #include "absl/base/internal/raw_logging.h"
-#include "absl/strings/ascii.h"
+#include "absl/strings/string_view.h"
 
 namespace absl {
 ABSL_NAMESPACE_BEGIN
@@ -99,6 +96,11 @@
   return GenericFind(text, delimiter_, pos, LiteralPolicy());
 }
 
+absl::string_view ByAsciiWhitespace::Find(absl::string_view text,
+                                          size_t pos) const {
+  return GenericFind(text, " \t\v\f\r\n", pos, AnyOfPolicy());
+}
+
 //
 // ByChar
 //
diff --git a/absl/strings/str_split.h b/absl/strings/str_split.h
index 7bbb68a..8754027 100644
--- a/absl/strings/str_split.h
+++ b/absl/strings/str_split.h
@@ -130,6 +130,24 @@
   const std::string delimiter_;
 };
 
+// ByAsciiWhitespace
+//
+// A sub-string delimiter that splits by ASCII whitespace
+// (space, tab, vertical tab, formfeed, linefeed, or carriage return).
+// Note: you probably want to use absl::SkipEmpty() as well!
+//
+// This class is equivalent to ByAnyChar with ASCII whitespace chars.
+//
+// Example:
+//
+//   std::vector<std::string> v = absl::StrSplit(
+//       "a b\tc\n  d  \n", absl::ByAsciiWhitespace(), absl::SkipEmpty());
+//   // v[0] == "a", v[1] == "b", v[2] == "c", v[3] == "d"
+class ByAsciiWhitespace {
+ public:
+  absl::string_view Find(absl::string_view text, size_t pos) const;
+};
+
 // ByChar
 //
 // A single character delimiter. `ByChar` is functionally equivalent to a
diff --git a/absl/strings/str_split_benchmark.cc b/absl/strings/str_split_benchmark.cc
index f38dfcf..003a66b 100644
--- a/absl/strings/str_split_benchmark.cc
+++ b/absl/strings/str_split_benchmark.cc
@@ -14,6 +14,7 @@
 
 #include "absl/strings/str_split.h"
 
+#include <cstddef>
 #include <iterator>
 #include <string>
 #include <unordered_map>
diff --git a/absl/strings/str_split_test.cc b/absl/strings/str_split_test.cc
index 04a64a4..df6c460 100644
--- a/absl/strings/str_split_test.cc
+++ b/absl/strings/str_split_test.cc
@@ -14,30 +14,33 @@
 
 #include "absl/strings/str_split.h"
 
+#include <cstddef>
+#include <cstdint>
 #include <deque>
 #include <initializer_list>
 #include <list>
 #include <map>
 #include <memory>
+#include <set>
 #include <string>
-#include <type_traits>
 #include <unordered_map>
 #include <unordered_set>
+#include <utility>
 #include <vector>
 
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
-#include "absl/base/dynamic_annotations.h"
 #include "absl/base/macros.h"
 #include "absl/container/btree_map.h"
 #include "absl/container/btree_set.h"
 #include "absl/container/flat_hash_map.h"
 #include "absl/container/node_hash_map.h"
-#include "absl/strings/numbers.h"
+#include "absl/strings/string_view.h"
 
 namespace {
 
 using ::testing::ElementsAre;
+using ::testing::IsEmpty;
 using ::testing::Pair;
 using ::testing::UnorderedElementsAre;
 
@@ -922,6 +925,45 @@
 }
 
 //
+// Tests for ByAsciiWhitespace
+//
+TEST(Split, ByAsciiWhitespace) {
+  using absl::ByAsciiWhitespace;
+  using absl::SkipEmpty;
+  std::vector<absl::string_view> results;
+
+  results = absl::StrSplit("aaaa\n", ByAsciiWhitespace());
+  EXPECT_THAT(results, ElementsAre("aaaa", ""));
+
+  results = absl::StrSplit("aaaa\n", ByAsciiWhitespace(), SkipEmpty());
+  EXPECT_THAT(results, ElementsAre("aaaa"));
+
+  results = absl::StrSplit(" ", ByAsciiWhitespace());
+  EXPECT_THAT(results, ElementsAre("", ""));
+
+  results = absl::StrSplit(" ", ByAsciiWhitespace(), SkipEmpty());
+  EXPECT_THAT(results, IsEmpty());
+
+  results = absl::StrSplit("a", ByAsciiWhitespace());
+  EXPECT_THAT(results, ElementsAre("a"));
+
+  results = absl::StrSplit("", ByAsciiWhitespace());
+  EXPECT_THAT(results, ElementsAre(""));
+
+  results = absl::StrSplit("", ByAsciiWhitespace(), SkipEmpty());
+  EXPECT_THAT(results, IsEmpty());
+
+  results = absl::StrSplit("a b\tc\n  d\n", ByAsciiWhitespace());
+  EXPECT_THAT(results, ElementsAre("a", "b", "c", "", "", "d", ""));
+
+  results = absl::StrSplit("a b\tc\n  d  \n", ByAsciiWhitespace(), SkipEmpty());
+  EXPECT_THAT(results, ElementsAre("a", "b", "c", "d"));
+
+  results = absl::StrSplit("a\t\n\v\f\r b", ByAsciiWhitespace(), SkipEmpty());
+  EXPECT_THAT(results, ElementsAre("a", "b"));
+}
+
+//
 // Tests for ByLength
 //
 
diff --git a/absl/strings/string_view.cc b/absl/strings/string_view.cc
index f20ff53..97025c3 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
   }
@@ -37,8 +41,8 @@
 
   const char* match;
   const char* hayend = phaystack + haylen - neelen + 1;
-  // A static cast is used here to work around the fact that memchr returns
-  // a void* on Posix-compliant systems and const void* on Windows.
+  // A static cast is used here as memchr returns a const void *, and pointer
+  // arithmetic is not allowed on pointers to void.
   while (
       (match = static_cast<const char*>(memchr(
            phaystack, pneedle[0], static_cast<size_t>(hayend - phaystack))))) {
@@ -229,7 +233,6 @@
   return npos;
 }
 
-
 #ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL
 constexpr string_view::size_type string_view::npos;
 constexpr string_view::size_type string_view::kMaxSize;
@@ -238,4 +241,22 @@
 ABSL_NAMESPACE_END
 }  // namespace absl
 
+#else
+
+// https://github.com/abseil/abseil-cpp/issues/1465
+// CMake builds on Apple platforms error when libraries are empty.
+// Our CMake configuration can avoid this error on header-only libraries,
+// but since this library is conditionally empty, including a single
+// variable is an easy workaround.
+#ifdef __APPLE__
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace strings_internal {
+extern const char kAvoidEmptyStringViewLibraryWarning;
+const char kAvoidEmptyStringViewLibraryWarning = 0;
+}  // namespace strings_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
+#endif  // __APPLE__
+
 #endif  // ABSL_USES_STD_STRING_VIEW
diff --git a/absl/strings/string_view.h b/absl/strings/string_view.h
index eae11b2..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 count) const {
+  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,22 +590,76 @@
 
   // 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);
   }
 
+#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
+  // string_view::starts_with()
+  //
+  // Returns true if the `string_view` starts with the prefix `s`.
+  //
+  // This method only exists when targeting at least C++20.
+  // If support for C++ prior to C++20 is required, use `absl::StartsWith()`
+  // from `//absl/strings/match.h` for compatibility.
+  constexpr bool starts_with(string_view s) const noexcept {
+    return s.empty() ||
+           (size() >= s.size() &&
+            ABSL_INTERNAL_STRING_VIEW_MEMCMP(data(), s.data(), s.size()) == 0);
+  }
+
+  // Overload of `string_view::starts_with()` that returns true if `c` is the
+  // first character of the `string_view`.
+  constexpr bool starts_with(char c) const noexcept {
+    return !empty() && front() == c;
+  }
+
+  // Overload of `string_view::starts_with()` that returns true if the
+  // `string_view` starts with the C-style prefix `s`.
+  constexpr bool starts_with(const char* s) const {
+    return starts_with(string_view(s));
+  }
+
+  // string_view::ends_with()
+  //
+  // Returns true if the `string_view` ends with the suffix `s`.
+  //
+  // This method only exists when targeting at least C++20.
+  // If support for C++ prior to C++20 is required, use `absl::EndsWith()`
+  // from `//absl/strings/match.h` for compatibility.
+  constexpr bool ends_with(string_view s) const noexcept {
+    return s.empty() || (size() >= s.size() && ABSL_INTERNAL_STRING_VIEW_MEMCMP(
+                                                   data() + (size() - s.size()),
+                                                   s.data(), s.size()) == 0);
+  }
+
+  // Overload of `string_view::ends_with()` that returns true if `c` is the
+  // last character of the `string_view`.
+  constexpr bool ends_with(char c) const noexcept {
+    return !empty() && back() == c;
+  }
+
+  // Overload of `string_view::ends_with()` that returns true if the
+  // `string_view` ends with the C-style suffix `s`.
+  constexpr bool ends_with(const char* s) const {
+    return ends_with(string_view(s));
+  }
+#endif  // ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
+
  private:
   // 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 =
@@ -604,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;
@@ -633,7 +698,7 @@
                                : (compare_result < 0 ? -1 : 1);
   }
 
-  const char* ptr_;
+  absl::Nullable<const char*> ptr_;
   size_type length_;
 };
 
@@ -694,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/string_view_benchmark.cc b/absl/strings/string_view_benchmark.cc
index 0d74e23..98f747c 100644
--- a/absl/strings/string_view_benchmark.cc
+++ b/absl/strings/string_view_benchmark.cc
@@ -15,6 +15,7 @@
 #include "absl/strings/string_view.h"
 
 #include <algorithm>
+#include <cstddef>
 #include <cstdint>
 #include <map>
 #include <random>
diff --git a/absl/strings/string_view_test.cc b/absl/strings/string_view_test.cc
index 990c211..5b1eb01 100644
--- a/absl/strings/string_view_test.cc
+++ b/absl/strings/string_view_test.cc
@@ -15,20 +15,23 @@
 #include "absl/strings/string_view.h"
 
 #include <stdlib.h>
+
+#include <cstddef>
+#include <cstdlib>
+#include <cstring>
 #include <iomanip>
+#include <ios>
 #include <iterator>
 #include <limits>
 #include <map>
+#include <memory>
 #include <sstream>
-#include <stdexcept>
 #include <string>
 #include <type_traits>
 #include <utility>
 
 #include "gtest/gtest.h"
 #include "absl/base/config.h"
-#include "absl/base/dynamic_annotations.h"
-#include "absl/base/options.h"
 
 #if defined(ABSL_HAVE_STD_STRING_VIEW) || defined(__ANDROID__)
 // We don't control the death messaging when using std::string_view.
@@ -948,6 +951,76 @@
 #endif
 }
 
+#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
+TEST(StringViewTest, StartsWith) {
+  const absl::string_view a("foobar");
+  const absl::string_view b("123\0abc", 7);
+  const absl::string_view e;
+  EXPECT_TRUE(a.starts_with(a));
+  EXPECT_TRUE(a.starts_with("foo"));
+  EXPECT_TRUE(a.starts_with('f'));
+  EXPECT_TRUE(a.starts_with(e));
+  EXPECT_TRUE(b.starts_with(b));
+  EXPECT_TRUE(b.starts_with('1'));
+  EXPECT_TRUE(b.starts_with(e));
+  EXPECT_TRUE(e.starts_with(""));
+  EXPECT_FALSE(a.starts_with(b));
+  EXPECT_FALSE(b.starts_with(a));
+  EXPECT_FALSE(e.starts_with(a));
+  EXPECT_FALSE(a.starts_with('r'));
+  EXPECT_FALSE(a.starts_with('\0'));
+  EXPECT_FALSE(e.starts_with('r'));
+  EXPECT_FALSE(e.starts_with('\0'));
+
+  // Test that constexpr compiles.
+  constexpr absl::string_view kFooBar("foobar");
+  constexpr absl::string_view kFoo("foo");
+  constexpr absl::string_view kBar("bar");
+  constexpr bool k1 = kFooBar.starts_with(kFoo);
+  EXPECT_TRUE(k1);
+  constexpr bool k2 = kFooBar.starts_with(kBar);
+  EXPECT_FALSE(k2);
+  constexpr bool k3 = kFooBar.starts_with('f');
+  EXPECT_TRUE(k3);
+  constexpr bool k4 = kFooBar.starts_with("fo");
+  EXPECT_TRUE(k4);
+}
+
+TEST(StringViewTest, EndsWith) {
+  const absl::string_view a("foobar");
+  const absl::string_view b("123\0abc", 7);
+  const absl::string_view e;
+  EXPECT_TRUE(a.ends_with(a));
+  EXPECT_TRUE(a.ends_with('r'));
+  EXPECT_TRUE(a.ends_with("bar"));
+  EXPECT_TRUE(a.ends_with(e));
+  EXPECT_TRUE(b.ends_with(b));
+  EXPECT_TRUE(b.ends_with('c'));
+  EXPECT_TRUE(b.ends_with(e));
+  EXPECT_TRUE(e.ends_with(""));
+  EXPECT_FALSE(a.ends_with(b));
+  EXPECT_FALSE(b.ends_with(a));
+  EXPECT_FALSE(e.ends_with(a));
+  EXPECT_FALSE(a.ends_with('f'));
+  EXPECT_FALSE(a.ends_with('\0'));
+  EXPECT_FALSE(e.ends_with('r'));
+  EXPECT_FALSE(e.ends_with('\0'));
+
+  // Test that constexpr compiles.
+  constexpr absl::string_view kFooBar("foobar");
+  constexpr absl::string_view kFoo("foo");
+  constexpr absl::string_view kBar("bar");
+  constexpr bool k1 = kFooBar.ends_with(kFoo);
+  EXPECT_FALSE(k1);
+  constexpr bool k2 = kFooBar.ends_with(kBar);
+  EXPECT_TRUE(k2);
+  constexpr bool k3 = kFooBar.ends_with('r');
+  EXPECT_TRUE(k3);
+  constexpr bool k4 = kFooBar.ends_with("ar");
+  EXPECT_TRUE(k4);
+}
+#endif  // ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
+
 struct MyCharAlloc : std::allocator<char> {};
 
 TEST(StringViewTest, ExplicitConversionOperator) {
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 33a3930..dd32c75 100644
--- a/absl/strings/substitute.cc
+++ b/absl/strings/substitute.cc
@@ -15,20 +15,28 @@
 #include "absl/strings/substitute.h"
 
 #include <algorithm>
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+#include <string>
 
+#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"
+#include "absl/strings/numbers.h"
+#include "absl/strings/str_cat.h"
 #include "absl/strings/string_view.h"
 
 namespace absl {
 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++) {
@@ -97,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 d6a5a69..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
@@ -176,7 +177,7 @@
       : piece_(value ? "true" : "false") {}
 
   template <typename T, typename = typename std::enable_if<
-                            strings_internal::HasAbslStringify<T>::value>::type>
+                            HasAbslStringify<T>::value>::type>
   Arg(  // NOLINT(google-explicit-constructor)
       const T& v, strings_internal::StringifySink&& sink = {})
       : piece_(strings_internal::ExtractStringification(sink, v)) {}
@@ -197,14 +198,15 @@
 
   // `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.
   template <typename T,
             typename = typename std::enable_if<
                 std::is_enum<T>{} && !std::is_convertible<T, int>{} &&
-                !strings_internal::HasAbslStringify<T>::value>::type>
+                !HasAbslStringify<T>::value>::type>
   Arg(T value)  // NOLINT(google-explicit-constructor)
       : Arg(static_cast<typename std::underlying_type<T>::type>(value)) {}
 
@@ -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/strings/substitute_test.cc b/absl/strings/substitute_test.cc
index ecf78d6..70f9119 100644
--- a/absl/strings/substitute_test.cc
+++ b/absl/strings/substitute_test.cc
@@ -15,10 +15,13 @@
 #include "absl/strings/substitute.h"
 
 #include <cstdint>
+#include <cstring>
+#include <string>
 #include <vector>
 
 #include "gtest/gtest.h"
 #include "absl/strings/str_cat.h"
+#include "absl/strings/string_view.h"
 
 namespace {
 
diff --git a/absl/synchronization/BUILD.bazel b/absl/synchronization/BUILD.bazel
index 0ca94e0..de06ebd 100644
--- a/absl/synchronization/BUILD.bazel
+++ b/absl/synchronization/BUILD.bazel
@@ -21,7 +21,14 @@
     "ABSL_TEST_COPTS",
 )
 
-package(default_visibility = ["//visibility:private"])
+package(
+    default_visibility = ["//visibility:private"],
+    features = [
+        "header_modules",
+        "layering_check",
+        "parse_headers",
+    ],
+)
 
 licenses(["notice"])
 
@@ -76,6 +83,7 @@
         "//absl/base:config",
         "//absl/random",
         "//absl/time",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -151,6 +159,7 @@
     deps = [
         ":synchronization",
         "//absl/time",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -167,6 +176,7 @@
     deps = [
         ":synchronization",
         "//absl/time",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -181,6 +191,7 @@
     deps = [
         ":synchronization",
         ":thread_pool",
+        "//absl/base:no_destructor",
         "@com_github_google_benchmark//:benchmark_main",
     ],
 )
@@ -196,6 +207,7 @@
         "//absl/base:core_headers",
         "//absl/log",
         "//absl/log:check",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -212,6 +224,7 @@
         ":graphcycles_internal",
         "//absl/base:raw_logging_internal",
         "@com_github_google_benchmark//:benchmark_main",
+        "@com_google_googletest//:gtest",
     ],
 )
 
@@ -248,6 +261,7 @@
         "//absl/log:check",
         "//absl/memory",
         "//absl/time",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -260,6 +274,7 @@
     deps = [
         ":synchronization",
         "//absl/base:config",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -277,6 +292,7 @@
         ":thread_pool",
         "//absl/base",
         "//absl/base:config",
+        "//absl/base:no_destructor",
         "@com_github_google_benchmark//:benchmark_main",
     ],
     alwayslink = 1,
@@ -303,6 +319,7 @@
     deps = [
         ":synchronization",
         "//absl/time",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -356,6 +373,7 @@
         "//absl/base:config",
         "//absl/random",
         "//absl/time",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
diff --git a/absl/synchronization/blocking_counter_benchmark.cc b/absl/synchronization/blocking_counter_benchmark.cc
index b504d1a..ea2bf9f 100644
--- a/absl/synchronization/blocking_counter_benchmark.cc
+++ b/absl/synchronization/blocking_counter_benchmark.cc
@@ -14,6 +14,7 @@
 
 #include <limits>
 
+#include "absl/base/no_destructor.h"
 #include "absl/synchronization/blocking_counter.h"
 #include "absl/synchronization/internal/thread_pool.h"
 #include "benchmark/benchmark.h"
@@ -39,8 +40,8 @@
     ->Arg(256);
 
 void BM_BlockingCounter_DecrementCount(benchmark::State& state) {
-  static absl::BlockingCounter* counter =
-      new absl::BlockingCounter{std::numeric_limits<int>::max()};
+  static absl::NoDestructor<absl::BlockingCounter> counter(
+      std::numeric_limits<int>::max());
   for (auto _ : state) {
     counter->DecrementCount();
   }
diff --git a/absl/synchronization/internal/kernel_timeout_test.cc b/absl/synchronization/internal/kernel_timeout_test.cc
index 92ed269..bc54671 100644
--- a/absl/synchronization/internal/kernel_timeout_test.cc
+++ b/absl/synchronization/internal/kernel_timeout_test.cc
@@ -47,11 +47,10 @@
 
 namespace {
 
-#if defined(ABSL_HAVE_ADDRESS_SANITIZER) || \
-    defined(ABSL_HAVE_MEMORY_SANITIZER) ||  \
-    defined(ABSL_HAVE_THREAD_SANITIZER) || \
-    defined(__ANDROID__) || \
-    defined(_WIN32) || defined(_WIN64)
+#if defined(ABSL_HAVE_ADDRESS_SANITIZER) ||                        \
+    defined(ABSL_HAVE_MEMORY_SANITIZER) ||                         \
+    defined(ABSL_HAVE_THREAD_SANITIZER) || defined(__ANDROID__) || \
+    defined(__APPLE__) || defined(_WIN32) || defined(_WIN64)
 constexpr absl::Duration kTimingBound = absl::Milliseconds(5);
 #else
 constexpr absl::Duration kTimingBound = absl::Microseconds(250);
diff --git a/absl/synchronization/internal/pthread_waiter.h b/absl/synchronization/internal/pthread_waiter.h
index 206aefa..23db76a 100644
--- a/absl/synchronization/internal/pthread_waiter.h
+++ b/absl/synchronization/internal/pthread_waiter.h
@@ -16,7 +16,7 @@
 #ifndef ABSL_SYNCHRONIZATION_INTERNAL_PTHREAD_WAITER_H_
 #define ABSL_SYNCHRONIZATION_INTERNAL_PTHREAD_WAITER_H_
 
-#ifndef _WIN32
+#if !defined(_WIN32) && !defined(__MINGW32__)
 #include <pthread.h>
 
 #include "absl/base/config.h"
@@ -55,6 +55,6 @@
 ABSL_NAMESPACE_END
 }  // namespace absl
 
-#endif  // ndef _WIN32
+#endif  // !defined(_WIN32) && !defined(__MINGW32__)
 
 #endif  // ABSL_SYNCHRONIZATION_INTERNAL_PTHREAD_WAITER_H_
diff --git a/absl/synchronization/internal/waiter.h b/absl/synchronization/internal/waiter.h
index 1a8b0b8..6ba204b 100644
--- a/absl/synchronization/internal/waiter.h
+++ b/absl/synchronization/internal/waiter.h
@@ -40,6 +40,8 @@
 #define ABSL_WAITER_MODE ABSL_WAITER_MODE_SEM
 #elif defined(ABSL_INTERNAL_HAVE_PTHREAD_WAITER)
 #define ABSL_WAITER_MODE ABSL_WAITER_MODE_CONDVAR
+#elif defined(ABSL_INTERNAL_HAVE_STDCPP_WAITER)
+#define ABSL_WAITER_MODE ABSL_WAITER_MODE_STDCPP
 #else
 #error ABSL_WAITER_MODE is undefined
 #endif
diff --git a/absl/synchronization/internal/win32_waiter.h b/absl/synchronization/internal/win32_waiter.h
index 87eb617..fdab264 100644
--- a/absl/synchronization/internal/win32_waiter.h
+++ b/absl/synchronization/internal/win32_waiter.h
@@ -20,7 +20,8 @@
 #include <sdkddkver.h>
 #endif
 
-#if defined(_WIN32) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+#if defined(_WIN32) && !defined(__MINGW32__) && \
+    _WIN32_WINNT >= _WIN32_WINNT_VISTA
 
 #include "absl/base/config.h"
 #include "absl/synchronization/internal/kernel_timeout.h"
@@ -65,6 +66,7 @@
 ABSL_NAMESPACE_END
 }  // namespace absl
 
-#endif  // defined(_WIN32) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+#endif  // defined(_WIN32) && !defined(__MINGW32__) &&
+        // _WIN32_WINNT >= _WIN32_WINNT_VISTA
 
 #endif  // ABSL_SYNCHRONIZATION_INTERNAL_WIN32_WAITER_H_
diff --git a/absl/synchronization/mutex.cc b/absl/synchronization/mutex.cc
index 3aa5560..cb3c7e7 100644
--- a/absl/synchronization/mutex.cc
+++ b/absl/synchronization/mutex.cc
@@ -129,11 +129,15 @@
 
 struct ABSL_CACHELINE_ALIGNED MutexGlobals {
   absl::once_flag once;
-  int spinloop_iterations = 0;
+  // Note: this variable is initialized separately in Mutex::LockSlow,
+  // so that Mutex::Lock does not have a stack frame in optimized build.
+  std::atomic<int> spinloop_iterations{0};
   int32_t mutex_sleep_spins[2] = {};
   absl::Duration mutex_sleep_time;
 };
 
+ABSL_CONST_INIT static MutexGlobals globals;
+
 absl::Duration MeasureTimeToYield() {
   absl::Time before = absl::Now();
   ABSL_INTERNAL_C_SYMBOL(AbslInternalMutexYield)();
@@ -141,33 +145,30 @@
 }
 
 const MutexGlobals& GetMutexGlobals() {
-  ABSL_CONST_INIT static MutexGlobals data;
-  absl::base_internal::LowLevelCallOnce(&data.once, [&]() {
+  absl::base_internal::LowLevelCallOnce(&globals.once, [&]() {
     if (absl::base_internal::NumCPUs() > 1) {
-      // If this is multiprocessor, allow spinning. If the mode is
-      // aggressive then spin many times before yielding. If the mode is
-      // gentle then spin only a few times before yielding. Aggressive spinning
-      // is used to ensure that an Unlock() call, which must get the spin lock
-      // for any thread to make progress gets it without undue delay.
-      data.spinloop_iterations = 1500;
-      data.mutex_sleep_spins[AGGRESSIVE] = 5000;
-      data.mutex_sleep_spins[GENTLE] = 250;
-      data.mutex_sleep_time = absl::Microseconds(10);
+      // If the mode is aggressive then spin many times before yielding.
+      // If the mode is gentle then spin only a few times before yielding.
+      // Aggressive spinning is used to ensure that an Unlock() call,
+      // which must get the spin lock for any thread to make progress gets it
+      // without undue delay.
+      globals.mutex_sleep_spins[AGGRESSIVE] = 5000;
+      globals.mutex_sleep_spins[GENTLE] = 250;
+      globals.mutex_sleep_time = absl::Microseconds(10);
     } else {
       // If this a uniprocessor, only yield/sleep. Real-time threads are often
       // unable to yield, so the sleep time needs to be long enough to keep
       // the calling thread asleep until scheduling happens.
-      data.spinloop_iterations = 0;
-      data.mutex_sleep_spins[AGGRESSIVE] = 0;
-      data.mutex_sleep_spins[GENTLE] = 0;
-      data.mutex_sleep_time = MeasureTimeToYield() * 5;
-      data.mutex_sleep_time =
-          std::min(data.mutex_sleep_time, absl::Milliseconds(1));
-      data.mutex_sleep_time =
-          std::max(data.mutex_sleep_time, absl::Microseconds(10));
+      globals.mutex_sleep_spins[AGGRESSIVE] = 0;
+      globals.mutex_sleep_spins[GENTLE] = 0;
+      globals.mutex_sleep_time = MeasureTimeToYield() * 5;
+      globals.mutex_sleep_time =
+          std::min(globals.mutex_sleep_time, absl::Milliseconds(1));
+      globals.mutex_sleep_time =
+          std::max(globals.mutex_sleep_time, absl::Microseconds(10));
     }
   });
-  return data;
+  return globals;
 }
 }  // namespace
 
@@ -202,31 +203,23 @@
 // Ensure that "(*pv & bits) == bits" by doing an atomic update of "*pv" to
 // "*pv | bits" if necessary.  Wait until (*pv & wait_until_clear)==0
 // before making any change.
+// Returns true if bits were previously unset and set by the call.
 // This is used to set flags in mutex and condition variable words.
-static void AtomicSetBits(std::atomic<intptr_t>* pv, intptr_t bits,
+static bool AtomicSetBits(std::atomic<intptr_t>* pv, intptr_t bits,
                           intptr_t wait_until_clear) {
-  intptr_t v;
-  do {
-    v = pv->load(std::memory_order_relaxed);
-  } while ((v & bits) != bits &&
-           ((v & wait_until_clear) != 0 ||
-            !pv->compare_exchange_weak(v, v | bits, std::memory_order_release,
-                                       std::memory_order_relaxed)));
-}
-
-// Ensure that "(*pv & bits) == 0" by doing an atomic update of "*pv" to
-// "*pv & ~bits" if necessary.  Wait until (*pv & wait_until_clear)==0
-// before making any change.
-// This is used to unset flags in mutex and condition variable words.
-static void AtomicClearBits(std::atomic<intptr_t>* pv, intptr_t bits,
-                            intptr_t wait_until_clear) {
-  intptr_t v;
-  do {
-    v = pv->load(std::memory_order_relaxed);
-  } while ((v & bits) != 0 &&
-           ((v & wait_until_clear) != 0 ||
-            !pv->compare_exchange_weak(v, v & ~bits, std::memory_order_release,
-                                       std::memory_order_relaxed)));
+  for (;;) {
+    intptr_t v = pv->load(std::memory_order_relaxed);
+    if ((v & bits) == bits) {
+      return false;
+    }
+    if ((v & wait_until_clear) != 0) {
+      continue;
+    }
+    if (pv->compare_exchange_weak(v, v | bits, std::memory_order_release,
+                                  std::memory_order_relaxed)) {
+      return true;
+    }
+  }
 }
 
 //------------------------------------------------------------------
@@ -337,12 +330,49 @@
                                     const char* name, intptr_t bits,
                                     intptr_t lockbit) {
   uint32_t h = reinterpret_cast<uintptr_t>(addr) % kNSynchEvent;
-  SynchEvent* e;
-  // first look for existing SynchEvent struct..
   synch_event_mu.Lock();
-  for (e = synch_event[h];
-       e != nullptr && e->masked_addr != base_internal::HidePtr(addr);
-       e = e->next) {
+  // When a Mutex/CondVar is destroyed, we don't remove the associated
+  // SynchEvent to keep destructors empty in release builds for performance
+  // reasons. If the current call is the first to set bits (kMuEvent/kCVEvent),
+  // we don't look up the existing even because (if it exists, it must be for
+  // the previous Mutex/CondVar that existed at the same address).
+  // The leaking events must not be a problem for tests, which should create
+  // bounded amount of events. And debug logging is not supposed to be enabled
+  // in production. However, if it's accidentally enabled, or briefly enabled
+  // for some debugging, we don't want to crash the program. Instead we drop
+  // all events, if we accumulated too many of them. Size of a single event
+  // is ~48 bytes, so 100K events is ~5 MB.
+  // Additionally we could delete the old event for the same address,
+  // but it would require a better hashmap (if we accumulate too many events,
+  // linked lists will grow and traversing them will be very slow).
+  constexpr size_t kMaxSynchEventCount = 100 << 10;
+  // Total number of live synch events.
+  static size_t synch_event_count ABSL_GUARDED_BY(synch_event_mu);
+  if (++synch_event_count > kMaxSynchEventCount) {
+    synch_event_count = 0;
+    ABSL_RAW_LOG(ERROR,
+                 "Accumulated %zu Mutex debug objects. If you see this"
+                 " in production, it may mean that the production code"
+                 " accidentally calls "
+                 "Mutex/CondVar::EnableDebugLog/EnableInvariantDebugging.",
+                 kMaxSynchEventCount);
+    for (auto*& head : synch_event) {
+      for (auto* e = head; e != nullptr;) {
+        SynchEvent* next = e->next;
+        if (--(e->refcount) == 0) {
+          base_internal::LowLevelAlloc::Free(e);
+        }
+        e = next;
+      }
+      head = nullptr;
+    }
+  }
+  SynchEvent* e = nullptr;
+  if (!AtomicSetBits(addr, bits, lockbit)) {
+    for (e = synch_event[h];
+         e != nullptr && e->masked_addr != base_internal::HidePtr(addr);
+         e = e->next) {
+    }
   }
   if (e == nullptr) {  // no SynchEvent struct found; make one.
     if (name == nullptr) {
@@ -358,7 +388,6 @@
     e->log = false;
     strcpy(e->name, name);  // NOLINT(runtime/printf)
     e->next = synch_event[h];
-    AtomicSetBits(addr, bits, lockbit);
     synch_event[h] = e;
   } else {
     e->refcount++;  // for return value
@@ -367,11 +396,6 @@
   return e;
 }
 
-// Deallocate the SynchEvent *e, whose refcount has fallen to zero.
-static void DeleteSynchEvent(SynchEvent* e) {
-  base_internal::LowLevelAlloc::Free(e);
-}
-
 // Decrement the reference count of *e, or do nothing if e==null.
 static void UnrefSynchEvent(SynchEvent* e) {
   if (e != nullptr) {
@@ -379,36 +403,11 @@
     bool del = (--(e->refcount) == 0);
     synch_event_mu.Unlock();
     if (del) {
-      DeleteSynchEvent(e);
+      base_internal::LowLevelAlloc::Free(e);
     }
   }
 }
 
-// Forget the mapping from the object (Mutex or CondVar) at address addr
-// to SynchEvent object, and clear "bits" in its word (waiting until lockbit
-// is clear before doing so).
-static void ForgetSynchEvent(std::atomic<intptr_t>* addr, intptr_t bits,
-                             intptr_t lockbit) {
-  uint32_t h = reinterpret_cast<uintptr_t>(addr) % kNSynchEvent;
-  SynchEvent** pe;
-  SynchEvent* e;
-  synch_event_mu.Lock();
-  for (pe = &synch_event[h];
-       (e = *pe) != nullptr && e->masked_addr != base_internal::HidePtr(addr);
-       pe = &e->next) {
-  }
-  bool del = false;
-  if (e != nullptr) {
-    *pe = e->next;
-    del = (--(e->refcount) == 0);
-  }
-  AtomicClearBits(addr, bits, lockbit);
-  synch_event_mu.Unlock();
-  if (del) {
-    DeleteSynchEvent(e);
-  }
-}
-
 // Return a refcounted reference to the SynchEvent of the object at address
 // "addr", if any.  The pointer returned is valid until the UnrefSynchEvent() is
 // called.
@@ -579,31 +578,25 @@
 
 // Post on "w"'s associated PerThreadSem.
 void Mutex::IncrementSynchSem(Mutex* mu, PerThreadSynch* w) {
-  if (mu) {
-    ABSL_TSAN_MUTEX_PRE_DIVERT(mu, 0);
-    // We miss synchronization around passing PerThreadSynch between threads
-    // since it happens inside of the Mutex code, so we need to ignore all
-    // accesses to the object.
-    ABSL_ANNOTATE_IGNORE_READS_AND_WRITES_BEGIN();
-    PerThreadSem::Post(w->thread_identity());
-    ABSL_ANNOTATE_IGNORE_READS_AND_WRITES_END();
-    ABSL_TSAN_MUTEX_POST_DIVERT(mu, 0);
-  } else {
-    PerThreadSem::Post(w->thread_identity());
-  }
+  static_cast<void>(mu);  // Prevent unused param warning in non-TSAN builds.
+  ABSL_TSAN_MUTEX_PRE_DIVERT(mu, 0);
+  // We miss synchronization around passing PerThreadSynch between threads
+  // since it happens inside of the Mutex code, so we need to ignore all
+  // accesses to the object.
+  ABSL_ANNOTATE_IGNORE_READS_AND_WRITES_BEGIN();
+  PerThreadSem::Post(w->thread_identity());
+  ABSL_ANNOTATE_IGNORE_READS_AND_WRITES_END();
+  ABSL_TSAN_MUTEX_POST_DIVERT(mu, 0);
 }
 
 // Wait on "w"'s associated PerThreadSem; returns false if timeout expired.
 bool Mutex::DecrementSynchSem(Mutex* mu, PerThreadSynch* w, KernelTimeout t) {
-  if (mu) {
-    ABSL_TSAN_MUTEX_PRE_DIVERT(mu, 0);
-  }
+  static_cast<void>(mu);  // Prevent unused param warning in non-TSAN builds.
+  ABSL_TSAN_MUTEX_PRE_DIVERT(mu, 0);
   assert(w == Synch_GetPerThread());
   static_cast<void>(w);
   bool res = PerThreadSem::Wait(t);
-  if (mu) {
-    ABSL_TSAN_MUTEX_POST_DIVERT(mu, 0);
-  }
+  ABSL_TSAN_MUTEX_POST_DIVERT(mu, 0);
   return res;
 }
 
@@ -682,6 +675,7 @@
 // flags passed to Enqueue and LockSlow{,WithTimeout,Loop}
 static const int kMuHasBlocked = 0x01;  // already blocked (MUST == 1)
 static const int kMuIsCond = 0x02;      // conditional waiter (CV or Condition)
+static const int kMuIsFer = 0x04;       // wait morphing from a CondVar
 
 static_assert(PerThreadSynch::kAlignment > kMuLow,
               "PerThreadSynch::kAlignment must be greater than kMuLow");
@@ -737,25 +731,43 @@
 }
 #endif
 
-static bool DebugOnlyIsExiting() {
-  return false;
-}
+#if defined(__APPLE__) || defined(ABSL_BUILD_DLL)
+// When building a dll symbol export lists may reference the destructor
+// and want it to be an exported symbol rather than an inline function.
+// Some apple builds also do dynamic library build but don't say it explicitly.
+Mutex::~Mutex() { Dtor(); }
+#endif
 
-Mutex::~Mutex() {
-  intptr_t v = mu_.load(std::memory_order_relaxed);
-  if ((v & kMuEvent) != 0 && !DebugOnlyIsExiting()) {
-    ForgetSynchEvent(&this->mu_, kMuEvent, kMuSpin);
-  }
+#if !defined(NDEBUG) || defined(ABSL_HAVE_THREAD_SANITIZER)
+void Mutex::Dtor() {
   if (kDebugMode) {
     this->ForgetDeadlockInfo();
   }
   ABSL_TSAN_MUTEX_DESTROY(this, __tsan_mutex_not_static);
 }
+#endif
 
 void Mutex::EnableDebugLog(const char* name) {
+  // Need to disable writes here and in EnableInvariantDebugging to prevent
+  // false race reports on SynchEvent objects. TSan ignores synchronization
+  // on synch_event_mu in Lock/Unlock/etc methods due to mutex annotations,
+  // but it sees few accesses to SynchEvent in EvalConditionAnnotated.
+  // If we don't ignore accesses here, it can result in false races
+  // between EvalConditionAnnotated and SynchEvent reuse in EnsureSynchEvent.
+  ABSL_ANNOTATE_IGNORE_WRITES_BEGIN();
   SynchEvent* e = EnsureSynchEvent(&this->mu_, name, kMuEvent, kMuSpin);
   e->log = true;
   UnrefSynchEvent(e);
+  // This prevents "error: undefined symbol: absl::Mutex::~Mutex()"
+  // in a release build (NDEBUG defined) when a test does "#undef NDEBUG"
+  // to use assert macro. In such case, the test does not get the dtor
+  // definition because it's supposed to be outline when NDEBUG is not defined,
+  // and this source file does not define one either because NDEBUG is defined.
+  // Since it's not possible to take address of a destructor, we move the
+  // actual destructor code into the separate Dtor function and force the
+  // compiler to emit this function even if it's inline by taking its address.
+  ABSL_ATTRIBUTE_UNUSED volatile auto dtor = &Mutex::Dtor;
+  ABSL_ANNOTATE_IGNORE_WRITES_END();
 }
 
 void EnableMutexInvariantDebugging(bool enabled) {
@@ -763,6 +775,7 @@
 }
 
 void Mutex::EnableInvariantDebugging(void (*invariant)(void*), void* arg) {
+  ABSL_ANNOTATE_IGNORE_WRITES_BEGIN();
   if (synch_check_invariants.load(std::memory_order_acquire) &&
       invariant != nullptr) {
     SynchEvent* e = EnsureSynchEvent(&this->mu_, nullptr, kMuEvent, kMuSpin);
@@ -770,6 +783,7 @@
     e->arg = arg;
     UnrefSynchEvent(e);
   }
+  ABSL_ANNOTATE_IGNORE_WRITES_END();
 }
 
 void SetMutexDeadlockDetectionMode(OnDeadlockCycle mode) {
@@ -920,20 +934,23 @@
   s->wake = false;     // not being woken
   s->cond_waiter = ((flags & kMuIsCond) != 0);
 #ifdef ABSL_HAVE_PTHREAD_GETSCHEDPARAM
-  int64_t now_cycles = CycleClock::Now();
-  if (s->next_priority_read_cycles < now_cycles) {
-    // Every so often, update our idea of the thread's priority.
-    // pthread_getschedparam() is 5% of the block/wakeup time;
-    // CycleClock::Now() is 0.5%.
-    int policy;
-    struct sched_param param;
-    const int err = pthread_getschedparam(pthread_self(), &policy, &param);
-    if (err != 0) {
-      ABSL_RAW_LOG(ERROR, "pthread_getschedparam failed: %d", err);
-    } else {
-      s->priority = param.sched_priority;
-      s->next_priority_read_cycles =
-          now_cycles + static_cast<int64_t>(CycleClock::Frequency());
+  if ((flags & kMuIsFer) == 0) {
+    assert(s == Synch_GetPerThread());
+    int64_t now_cycles = CycleClock::Now();
+    if (s->next_priority_read_cycles < now_cycles) {
+      // Every so often, update our idea of the thread's priority.
+      // pthread_getschedparam() is 5% of the block/wakeup time;
+      // CycleClock::Now() is 0.5%.
+      int policy;
+      struct sched_param param;
+      const int err = pthread_getschedparam(pthread_self(), &policy, &param);
+      if (err != 0) {
+        ABSL_RAW_LOG(ERROR, "pthread_getschedparam failed: %d", err);
+      } else {
+        s->priority = param.sched_priority;
+        s->next_priority_read_cycles =
+            now_cycles + static_cast<int64_t>(CycleClock::Frequency());
+      }
     }
   }
 #endif
@@ -962,8 +979,7 @@
         } while (s->priority <= advance_to->priority);
         // termination guaranteed because s->priority > head->priority
         // and head is the end of a skip chain
-      } else if (waitp->how == kExclusive &&
-                 Condition::GuaranteedEqual(waitp->cond, nullptr)) {
+      } else if (waitp->how == kExclusive && waitp->cond == nullptr) {
         // An unlocker could be scanning the queue, but we know it will recheck
         // the queue front for writers that have no condition, which is what s
         // is, so an insert at front is safe.
@@ -994,6 +1010,24 @@
       if (MuEquivalentWaiter(s, s->next)) {  // s->may_skip is known to be true
         s->skip = s->next;                   // s may skip to its successor
       }
+    } else if ((flags & kMuHasBlocked) &&
+               (s->priority >= head->next->priority) &&
+               (!head->maybe_unlocking ||
+                (waitp->how == kExclusive &&
+                 Condition::GuaranteedEqual(waitp->cond, nullptr)))) {
+      // This thread has already waited, then was woken, then failed to acquire
+      // the mutex and now tries to requeue. Try to requeue it at head,
+      // otherwise it can suffer bad latency (wait whole queue several times).
+      // However, we need to be conservative. First, we need to ensure that we
+      // respect priorities. Then, we need to be careful to not break wait
+      // queue invariants: we require either that unlocker is not scanning
+      // the queue or that the current thread is a writer with no condition
+      // (unlocker will recheck the queue for such waiters).
+      s->next = head->next;
+      head->next = s;
+      if (MuEquivalentWaiter(s, s->next)) {  // s->may_skip is known to be true
+        s->skip = s->next;                   // s may skip to its successor
+      }
     } else {  // enqueue not done any other way, so
               // we're inserting s at the back
       // s will become new head; copy data from head into it
@@ -1472,7 +1506,7 @@
 // Attempt to acquire *mu, and return whether successful.  The implementation
 // may spin for a short while if the lock cannot be acquired immediately.
 static bool TryAcquireWithSpinning(std::atomic<intptr_t>* mu) {
-  int c = GetMutexGlobals().spinloop_iterations;
+  int c = globals.spinloop_iterations.load(std::memory_order_relaxed);
   do {  // do/while somewhat faster on AMD
     intptr_t v = mu->load(std::memory_order_relaxed);
     if ((v & (kMuReader | kMuEvent)) != 0) {
@@ -1492,11 +1526,12 @@
   GraphId id = DebugOnlyDeadlockCheck(this);
   intptr_t v = mu_.load(std::memory_order_relaxed);
   // try fast acquire, then spin loop
-  if ((v & (kMuWriter | kMuReader | kMuEvent)) != 0 ||
-      !mu_.compare_exchange_strong(v, kMuWriter | v, std::memory_order_acquire,
-                                   std::memory_order_relaxed)) {
+  if (ABSL_PREDICT_FALSE((v & (kMuWriter | kMuReader | kMuEvent)) != 0) ||
+      ABSL_PREDICT_FALSE(!mu_.compare_exchange_strong(
+          v, kMuWriter | v, std::memory_order_acquire,
+          std::memory_order_relaxed))) {
     // try spin acquire, then slow loop
-    if (!TryAcquireWithSpinning(&this->mu_)) {
+    if (ABSL_PREDICT_FALSE(!TryAcquireWithSpinning(&this->mu_))) {
       this->LockSlow(kExclusive, nullptr, 0);
     }
   }
@@ -1508,159 +1543,95 @@
   ABSL_TSAN_MUTEX_PRE_LOCK(this, __tsan_mutex_read_lock);
   GraphId id = DebugOnlyDeadlockCheck(this);
   intptr_t v = mu_.load(std::memory_order_relaxed);
-  // try fast acquire, then slow loop
-  if ((v & (kMuWriter | kMuWait | kMuEvent)) != 0 ||
-      !mu_.compare_exchange_strong(v, (kMuReader | v) + kMuOne,
-                                   std::memory_order_acquire,
-                                   std::memory_order_relaxed)) {
-    this->LockSlow(kShared, nullptr, 0);
-  }
-  DebugOnlyLockEnter(this, id);
-  ABSL_TSAN_MUTEX_POST_LOCK(this, __tsan_mutex_read_lock, 0);
-}
-
-void Mutex::LockWhen(const Condition& cond) {
-  ABSL_TSAN_MUTEX_PRE_LOCK(this, 0);
-  GraphId id = DebugOnlyDeadlockCheck(this);
-  this->LockSlow(kExclusive, &cond, 0);
-  DebugOnlyLockEnter(this, id);
-  ABSL_TSAN_MUTEX_POST_LOCK(this, 0, 0);
-}
-
-bool Mutex::LockWhenWithTimeout(const Condition& cond, absl::Duration timeout) {
-  ABSL_TSAN_MUTEX_PRE_LOCK(this, 0);
-  GraphId id = DebugOnlyDeadlockCheck(this);
-  bool res = LockSlowWithDeadline(kExclusive, &cond, KernelTimeout(timeout), 0);
-  DebugOnlyLockEnter(this, id);
-  ABSL_TSAN_MUTEX_POST_LOCK(this, 0, 0);
-  return res;
-}
-
-bool Mutex::LockWhenWithDeadline(const Condition& cond, absl::Time deadline) {
-  ABSL_TSAN_MUTEX_PRE_LOCK(this, 0);
-  GraphId id = DebugOnlyDeadlockCheck(this);
-  bool res =
-      LockSlowWithDeadline(kExclusive, &cond, KernelTimeout(deadline), 0);
-  DebugOnlyLockEnter(this, id);
-  ABSL_TSAN_MUTEX_POST_LOCK(this, 0, 0);
-  return res;
-}
-
-void Mutex::ReaderLockWhen(const Condition& cond) {
-  ABSL_TSAN_MUTEX_PRE_LOCK(this, __tsan_mutex_read_lock);
-  GraphId id = DebugOnlyDeadlockCheck(this);
-  this->LockSlow(kShared, &cond, 0);
-  DebugOnlyLockEnter(this, id);
-  ABSL_TSAN_MUTEX_POST_LOCK(this, __tsan_mutex_read_lock, 0);
-}
-
-bool Mutex::ReaderLockWhenWithTimeout(const Condition& cond,
-                                      absl::Duration timeout) {
-  ABSL_TSAN_MUTEX_PRE_LOCK(this, __tsan_mutex_read_lock);
-  GraphId id = DebugOnlyDeadlockCheck(this);
-  bool res = LockSlowWithDeadline(kShared, &cond, KernelTimeout(timeout), 0);
-  DebugOnlyLockEnter(this, id);
-  ABSL_TSAN_MUTEX_POST_LOCK(this, __tsan_mutex_read_lock, 0);
-  return res;
-}
-
-bool Mutex::ReaderLockWhenWithDeadline(const Condition& cond,
-                                       absl::Time deadline) {
-  ABSL_TSAN_MUTEX_PRE_LOCK(this, __tsan_mutex_read_lock);
-  GraphId id = DebugOnlyDeadlockCheck(this);
-  bool res = LockSlowWithDeadline(kShared, &cond, KernelTimeout(deadline), 0);
-  DebugOnlyLockEnter(this, id);
-  ABSL_TSAN_MUTEX_POST_LOCK(this, __tsan_mutex_read_lock, 0);
-  return res;
-}
-
-void Mutex::Await(const Condition& cond) {
-  if (cond.Eval()) {  // condition already true; nothing to do
-    if (kDebugMode) {
-      this->AssertReaderHeld();
+  for (;;) {
+    // If there are non-readers holding the lock, use the slow loop.
+    if (ABSL_PREDICT_FALSE(v & (kMuWriter | kMuWait | kMuEvent)) != 0) {
+      this->LockSlow(kShared, nullptr, 0);
+      break;
     }
-  } else {  // normal case
-    ABSL_RAW_CHECK(this->AwaitCommon(cond, KernelTimeout::Never()),
-                   "condition untrue on return from Await");
+    // We can avoid the loop and only use the CAS when the lock is free or
+    // only held by readers.
+    if (ABSL_PREDICT_TRUE(mu_.compare_exchange_weak(
+            v, (kMuReader | v) + kMuOne, std::memory_order_acquire,
+            std::memory_order_relaxed))) {
+      break;
+    }
   }
+  DebugOnlyLockEnter(this, id);
+  ABSL_TSAN_MUTEX_POST_LOCK(this, __tsan_mutex_read_lock, 0);
 }
 
-bool Mutex::AwaitWithTimeout(const Condition& cond, absl::Duration timeout) {
-  if (cond.Eval()) {  // condition already true; nothing to do
-    if (kDebugMode) {
-      this->AssertReaderHeld();
-    }
-    return true;
-  }
-
-  KernelTimeout t{timeout};
-  bool res = this->AwaitCommon(cond, t);
-  ABSL_RAW_CHECK(res || t.has_timeout(),
-                 "condition untrue on return from Await");
-  return res;
-}
-
-bool Mutex::AwaitWithDeadline(const Condition& cond, absl::Time deadline) {
-  if (cond.Eval()) {  // condition already true; nothing to do
-    if (kDebugMode) {
-      this->AssertReaderHeld();
-    }
-    return true;
-  }
-
-  KernelTimeout t{deadline};
-  bool res = this->AwaitCommon(cond, t);
-  ABSL_RAW_CHECK(res || t.has_timeout(),
-                 "condition untrue on return from Await");
+bool Mutex::LockWhenCommon(const Condition& cond,
+                           synchronization_internal::KernelTimeout t,
+                           bool write) {
+  MuHow how = write ? kExclusive : kShared;
+  ABSL_TSAN_MUTEX_PRE_LOCK(this, TsanFlags(how));
+  GraphId id = DebugOnlyDeadlockCheck(this);
+  bool res = LockSlowWithDeadline(how, &cond, t, 0);
+  DebugOnlyLockEnter(this, id);
+  ABSL_TSAN_MUTEX_POST_LOCK(this, TsanFlags(how), 0);
   return res;
 }
 
 bool Mutex::AwaitCommon(const Condition& cond, KernelTimeout t) {
-  this->AssertReaderHeld();
+  if (kDebugMode) {
+    this->AssertReaderHeld();
+  }
+  if (cond.Eval()) {  // condition already true; nothing to do
+    return true;
+  }
   MuHow how =
       (mu_.load(std::memory_order_relaxed) & kMuWriter) ? kExclusive : kShared;
   ABSL_TSAN_MUTEX_PRE_UNLOCK(this, TsanFlags(how));
   SynchWaitParams waitp(how, &cond, t, nullptr /*no cvmu*/,
                         Synch_GetPerThreadAnnotated(this),
                         nullptr /*no cv_word*/);
-  int flags = kMuHasBlocked;
-  if (!Condition::GuaranteedEqual(&cond, nullptr)) {
-    flags |= kMuIsCond;
-  }
   this->UnlockSlow(&waitp);
   this->Block(waitp.thread);
   ABSL_TSAN_MUTEX_POST_UNLOCK(this, TsanFlags(how));
   ABSL_TSAN_MUTEX_PRE_LOCK(this, TsanFlags(how));
-  this->LockSlowLoop(&waitp, flags);
+  this->LockSlowLoop(&waitp, kMuHasBlocked | kMuIsCond);
   bool res = waitp.cond != nullptr ||  // => cond known true from LockSlowLoop
              EvalConditionAnnotated(&cond, this, true, false, how == kShared);
   ABSL_TSAN_MUTEX_POST_LOCK(this, TsanFlags(how), 0);
+  ABSL_RAW_CHECK(res || t.has_timeout(),
+                 "condition untrue on return from Await");
   return res;
 }
 
 bool Mutex::TryLock() {
   ABSL_TSAN_MUTEX_PRE_LOCK(this, __tsan_mutex_try_lock);
   intptr_t v = mu_.load(std::memory_order_relaxed);
-  if ((v & (kMuWriter | kMuReader | kMuEvent)) == 0 &&  // try fast acquire
-      mu_.compare_exchange_strong(v, kMuWriter | v, std::memory_order_acquire,
-                                  std::memory_order_relaxed)) {
+  // Try fast acquire.
+  if (ABSL_PREDICT_TRUE((v & (kMuWriter | kMuReader | kMuEvent)) == 0)) {
+    if (ABSL_PREDICT_TRUE(mu_.compare_exchange_strong(
+            v, kMuWriter | v, std::memory_order_acquire,
+            std::memory_order_relaxed))) {
+      DebugOnlyLockEnter(this);
+      ABSL_TSAN_MUTEX_POST_LOCK(this, __tsan_mutex_try_lock, 0);
+      return true;
+    }
+  } else if (ABSL_PREDICT_FALSE((v & kMuEvent) != 0)) {
+    // We're recording events.
+    return TryLockSlow();
+  }
+  ABSL_TSAN_MUTEX_POST_LOCK(
+      this, __tsan_mutex_try_lock | __tsan_mutex_try_lock_failed, 0);
+  return false;
+}
+
+ABSL_ATTRIBUTE_NOINLINE bool Mutex::TryLockSlow() {
+  intptr_t v = mu_.load(std::memory_order_relaxed);
+  if ((v & kExclusive->slow_need_zero) == 0 &&  // try fast acquire
+      mu_.compare_exchange_strong(
+          v, (kExclusive->fast_or | v) + kExclusive->fast_add,
+          std::memory_order_acquire, std::memory_order_relaxed)) {
     DebugOnlyLockEnter(this);
+    PostSynchEvent(this, SYNCH_EV_TRYLOCK_SUCCESS);
     ABSL_TSAN_MUTEX_POST_LOCK(this, __tsan_mutex_try_lock, 0);
     return true;
   }
-  if ((v & kMuEvent) != 0) {                      // we're recording events
-    if ((v & kExclusive->slow_need_zero) == 0 &&  // try fast acquire
-        mu_.compare_exchange_strong(
-            v, (kExclusive->fast_or | v) + kExclusive->fast_add,
-            std::memory_order_acquire, std::memory_order_relaxed)) {
-      DebugOnlyLockEnter(this);
-      PostSynchEvent(this, SYNCH_EV_TRYLOCK_SUCCESS);
-      ABSL_TSAN_MUTEX_POST_LOCK(this, __tsan_mutex_try_lock, 0);
-      return true;
-    } else {
-      PostSynchEvent(this, SYNCH_EV_TRYLOCK_FAILED);
-    }
-  }
+  PostSynchEvent(this, SYNCH_EV_TRYLOCK_FAILED);
   ABSL_TSAN_MUTEX_POST_LOCK(
       this, __tsan_mutex_try_lock | __tsan_mutex_try_lock_failed, 0);
   return false;
@@ -1670,41 +1641,57 @@
   ABSL_TSAN_MUTEX_PRE_LOCK(this,
                            __tsan_mutex_read_lock | __tsan_mutex_try_lock);
   intptr_t v = mu_.load(std::memory_order_relaxed);
+  // Clang tends to unroll the loop when compiling with optimization.
+  // But in this case it just unnecessary increases code size.
+  // If CAS is failing due to contention, the jump cost is negligible.
+#if defined(__clang__)
+#pragma nounroll
+#endif
   // The while-loops (here and below) iterate only if the mutex word keeps
-  // changing (typically because the reader count changes) under the CAS.  We
-  // limit the number of attempts to avoid having to think about livelock.
-  int loop_limit = 5;
-  while ((v & (kMuWriter | kMuWait | kMuEvent)) == 0 && loop_limit != 0) {
-    if (mu_.compare_exchange_strong(v, (kMuReader | v) + kMuOne,
-                                    std::memory_order_acquire,
-                                    std::memory_order_relaxed)) {
+  // changing (typically because the reader count changes) under the CAS.
+  // We limit the number of attempts to avoid having to think about livelock.
+  for (int loop_limit = 5; loop_limit != 0; loop_limit--) {
+    if (ABSL_PREDICT_FALSE((v & (kMuWriter | kMuWait | kMuEvent)) != 0)) {
+      break;
+    }
+    if (ABSL_PREDICT_TRUE(mu_.compare_exchange_strong(
+            v, (kMuReader | v) + kMuOne, std::memory_order_acquire,
+            std::memory_order_relaxed))) {
       DebugOnlyLockEnter(this);
       ABSL_TSAN_MUTEX_POST_LOCK(
           this, __tsan_mutex_read_lock | __tsan_mutex_try_lock, 0);
       return true;
     }
-    loop_limit--;
-    v = mu_.load(std::memory_order_relaxed);
   }
-  if ((v & kMuEvent) != 0) {  // we're recording events
-    loop_limit = 5;
-    while ((v & kShared->slow_need_zero) == 0 && loop_limit != 0) {
-      if (mu_.compare_exchange_strong(v, (kMuReader | v) + kMuOne,
-                                      std::memory_order_acquire,
-                                      std::memory_order_relaxed)) {
-        DebugOnlyLockEnter(this);
-        PostSynchEvent(this, SYNCH_EV_READERTRYLOCK_SUCCESS);
-        ABSL_TSAN_MUTEX_POST_LOCK(
-            this, __tsan_mutex_read_lock | __tsan_mutex_try_lock, 0);
-        return true;
-      }
-      loop_limit--;
-      v = mu_.load(std::memory_order_relaxed);
-    }
-    if ((v & kMuEvent) != 0) {
-      PostSynchEvent(this, SYNCH_EV_READERTRYLOCK_FAILED);
+  if (ABSL_PREDICT_TRUE((v & kMuEvent) == 0)) {
+    ABSL_TSAN_MUTEX_POST_LOCK(this,
+                              __tsan_mutex_read_lock | __tsan_mutex_try_lock |
+                                  __tsan_mutex_try_lock_failed,
+                              0);
+    return false;
+  }
+  // we're recording events
+  return ReaderTryLockSlow();
+}
+
+ABSL_ATTRIBUTE_NOINLINE bool Mutex::ReaderTryLockSlow() {
+  intptr_t v = mu_.load(std::memory_order_relaxed);
+#if defined(__clang__)
+#pragma nounroll
+#endif
+  for (int loop_limit = 5; loop_limit != 0; loop_limit--) {
+    if ((v & kShared->slow_need_zero) == 0 &&
+        mu_.compare_exchange_strong(v, (kMuReader | v) + kMuOne,
+                                    std::memory_order_acquire,
+                                    std::memory_order_relaxed)) {
+      DebugOnlyLockEnter(this);
+      PostSynchEvent(this, SYNCH_EV_READERTRYLOCK_SUCCESS);
+      ABSL_TSAN_MUTEX_POST_LOCK(
+          this, __tsan_mutex_read_lock | __tsan_mutex_try_lock, 0);
+      return true;
     }
   }
+  PostSynchEvent(this, SYNCH_EV_READERTRYLOCK_FAILED);
   ABSL_TSAN_MUTEX_POST_LOCK(this,
                             __tsan_mutex_read_lock | __tsan_mutex_try_lock |
                                 __tsan_mutex_try_lock_failed,
@@ -1768,16 +1755,20 @@
   DebugOnlyLockLeave(this);
   intptr_t v = mu_.load(std::memory_order_relaxed);
   assert((v & (kMuWriter | kMuReader)) == kMuReader);
-  if ((v & (kMuReader | kMuWait | kMuEvent)) == kMuReader) {
+  for (;;) {
+    if (ABSL_PREDICT_FALSE((v & (kMuReader | kMuWait | kMuEvent)) !=
+                           kMuReader)) {
+      this->UnlockSlow(nullptr /*no waitp*/);  // take slow path
+      break;
+    }
     // fast reader release (reader with no waiters)
     intptr_t clear = ExactlyOneReader(v) ? kMuReader | kMuOne : kMuOne;
-    if (mu_.compare_exchange_strong(v, v - clear, std::memory_order_release,
-                                    std::memory_order_relaxed)) {
-      ABSL_TSAN_MUTEX_POST_UNLOCK(this, __tsan_mutex_read_lock);
-      return;
+    if (ABSL_PREDICT_TRUE(
+            mu_.compare_exchange_strong(v, v - clear, std::memory_order_release,
+                                        std::memory_order_relaxed))) {
+      break;
     }
   }
-  this->UnlockSlow(nullptr /*no waitp*/);  // take slow path
   ABSL_TSAN_MUTEX_POST_UNLOCK(this, __tsan_mutex_read_lock);
 }
 
@@ -1812,6 +1803,22 @@
 // Internal version of LockWhen().  See LockSlowWithDeadline()
 ABSL_ATTRIBUTE_NOINLINE void Mutex::LockSlow(MuHow how, const Condition* cond,
                                              int flags) {
+  // Note: we specifically initialize spinloop_iterations after the first use
+  // in TryAcquireWithSpinning so that Lock function does not have any non-tail
+  // calls and consequently a stack frame. It's fine to have spinloop_iterations
+  // uninitialized (meaning no spinning) in all initial uncontended Lock calls
+  // and in the first contended call. After that we will have
+  // spinloop_iterations properly initialized.
+  if (ABSL_PREDICT_FALSE(
+          globals.spinloop_iterations.load(std::memory_order_relaxed) == 0)) {
+    if (absl::base_internal::NumCPUs() > 1) {
+      // If this is multiprocessor, allow spinning.
+      globals.spinloop_iterations.store(1500, std::memory_order_relaxed);
+    } else {
+      // If this a uniprocessor, only yield/sleep.
+      globals.spinloop_iterations.store(-1, std::memory_order_relaxed);
+    }
+  }
   ABSL_RAW_CHECK(
       this->LockSlowWithDeadline(how, cond, KernelTimeout::Never(), flags),
       "condition untrue on return from LockSlow");
@@ -1913,7 +1920,7 @@
   SynchWaitParams waitp(how, cond, t, nullptr /*no cvmu*/,
                         Synch_GetPerThreadAnnotated(this),
                         nullptr /*no cv_word*/);
-  if (!Condition::GuaranteedEqual(cond, nullptr)) {
+  if (cond != nullptr) {
     flags |= kMuIsCond;
   }
   if (unlock) {
@@ -2103,7 +2110,6 @@
   // head of the list searched previously, or zero
   PerThreadSynch* old_h = nullptr;
   // a condition that's known to be false.
-  const Condition* known_false = nullptr;
   PerThreadSynch* wake_list = kPerThreadSynchNull;  // list of threads to wake
   intptr_t wr_wait = 0;  // set to kMuWrWait if we wake a reader and a
                          // later writer could have acquired the lock
@@ -2207,7 +2213,7 @@
         }
       }
       if (h->next->waitp->how == kExclusive &&
-          Condition::GuaranteedEqual(h->next->waitp->cond, nullptr)) {
+          h->next->waitp->cond == nullptr) {
         // easy case: writer with no condition; no need to search
         pw = h;  // wake w, the successor of h (=pw)
         w = h->next;
@@ -2290,10 +2296,8 @@
           w_walk->wake = false;
           if (w_walk->waitp->cond ==
                   nullptr ||  // no condition => vacuously true OR
-              (w_walk->waitp->cond != known_false &&
-               // this thread's condition is not known false, AND
-               //  is in fact true
-               EvalConditionIgnored(this, w_walk->waitp->cond))) {
+                              // this thread's condition is true
+              EvalConditionIgnored(this, w_walk->waitp->cond)) {
             if (w == nullptr) {
               w_walk->wake = true;  // can wake this waiter
               w = w_walk;
@@ -2307,8 +2311,6 @@
             } else {  // writer with true condition
               wr_wait = kMuWrWait;
             }
-          } else {                              // can't wake; condition false
-            known_false = w_walk->waitp->cond;  // remember last false condition
           }
           if (w_walk->wake) {  // we're waking reader w_walk
             pw_walk = w_walk;  // don't skip similar waiters
@@ -2414,10 +2416,10 @@
   int c = 0;
   ABSL_RAW_CHECK(w->waitp->cond == nullptr,
                  "Mutex::Fer while waiting on Condition");
-  ABSL_RAW_CHECK(!w->waitp->timeout.has_timeout(),
-                 "Mutex::Fer while in timed wait");
   ABSL_RAW_CHECK(w->waitp->cv_word == nullptr,
                  "Mutex::Fer with pending CondVar queueing");
+  // The CondVar timeout is not relevant for the Mutex wait.
+  w->waitp->timeout = {};
   for (;;) {
     intptr_t v = mu_.load(std::memory_order_relaxed);
     // Note: must not queue if the mutex is unlocked (nobody will wake it).
@@ -2436,7 +2438,8 @@
     } else {
       if ((v & (kMuSpin | kMuWait)) == 0) {  // no waiters
         // This thread tries to become the one and only waiter.
-        PerThreadSynch* new_h = Enqueue(nullptr, w->waitp, v, kMuIsCond);
+        PerThreadSynch* new_h =
+            Enqueue(nullptr, w->waitp, v, kMuIsCond | kMuIsFer);
         ABSL_RAW_CHECK(new_h != nullptr,
                        "Enqueue failed");  // we must queue ourselves
         if (mu_.compare_exchange_strong(
@@ -2447,7 +2450,7 @@
       } else if ((v & kMuSpin) == 0 &&
                  mu_.compare_exchange_strong(v, v | kMuSpin | kMuWait)) {
         PerThreadSynch* h = GetPerThreadSynch(v);
-        PerThreadSynch* new_h = Enqueue(h, w->waitp, v, kMuIsCond);
+        PerThreadSynch* new_h = Enqueue(h, w->waitp, v, kMuIsCond | kMuIsFer);
         ABSL_RAW_CHECK(new_h != nullptr,
                        "Enqueue failed");  // we must queue ourselves
         do {
@@ -2503,12 +2506,6 @@
   UnrefSynchEvent(e);
 }
 
-CondVar::~CondVar() {
-  if ((cv_.load(std::memory_order_relaxed) & kCvEvent) != 0) {
-    ForgetSynchEvent(&this->cv_, kCvEvent, kCvSpin);
-  }
-}
-
 // Remove thread s from the list of waiters on this condition variable.
 void CondVar::Remove(PerThreadSynch* s) {
   SchedulingGuard::ScopedDisable disable_rescheduling;
@@ -2659,33 +2656,6 @@
   return rc;
 }
 
-bool CondVar::WaitWithTimeout(Mutex* mu, absl::Duration timeout) {
-  return WaitCommon(mu, KernelTimeout(timeout));
-}
-
-bool CondVar::WaitWithDeadline(Mutex* mu, absl::Time deadline) {
-  return WaitCommon(mu, KernelTimeout(deadline));
-}
-
-void CondVar::Wait(Mutex* mu) { WaitCommon(mu, KernelTimeout::Never()); }
-
-// Wake thread w
-// If it was a timed wait, w will be waiting on w->cv
-// Otherwise, if it was not a Mutex mutex, w will be waiting on w->sem
-// Otherwise, w is transferred to the Mutex mutex via Mutex::Fer().
-void CondVar::Wakeup(PerThreadSynch* w) {
-  if (w->waitp->timeout.has_timeout() || w->waitp->cvmu == nullptr) {
-    // The waiting thread only needs to observe "w->state == kAvailable" to be
-    // released, we must cache "cvmu" before clearing "next".
-    Mutex* mu = w->waitp->cvmu;
-    w->next = nullptr;
-    w->state.store(PerThreadSynch::kAvailable, std::memory_order_release);
-    Mutex::IncrementSynchSem(mu, w);
-  } else {
-    w->waitp->cvmu->Fer(w);
-  }
-}
-
 void CondVar::Signal() {
   SchedulingGuard::ScopedDisable disable_rescheduling;
   ABSL_TSAN_MUTEX_PRE_SIGNAL(nullptr, 0);
@@ -2710,7 +2680,7 @@
       cv_.store((v & kCvEvent) | reinterpret_cast<intptr_t>(h),
                 std::memory_order_release);
       if (w != nullptr) {
-        CondVar::Wakeup(w);  // wake waiter, if there was one
+        w->waitp->cvmu->Fer(w);  // wake waiter, if there was one
         cond_var_tracer("Signal wakeup", this);
       }
       if ((v & kCvEvent) != 0) {
@@ -2746,7 +2716,7 @@
         do {  // for every thread, wake it up
           w = n;
           n = n->next;
-          CondVar::Wakeup(w);
+          w->waitp->cvmu->Fer(w);
         } while (w != h);
         cond_var_tracer("SignalAll wakeup", this);
       }
@@ -2810,17 +2780,11 @@
   StoreCallback(dereference);
 }
 
-bool Condition::Eval() const {
-  // eval_ == null for kTrue
-  return (this->eval_ == nullptr) || (*this->eval_)(this);
-}
+bool Condition::Eval() const { return (*this->eval_)(this); }
 
 bool Condition::GuaranteedEqual(const Condition* a, const Condition* b) {
-  // kTrue logic.
-  if (a == nullptr || a->eval_ == nullptr) {
-    return b == nullptr || b->eval_ == nullptr;
-  } else if (b == nullptr || b->eval_ == nullptr) {
-    return false;
+  if (a == nullptr || b == nullptr) {
+    return a == b;
   }
   // Check equality of the representative fields.
   return a->eval_ == b->eval_ && a->arg_ == b->arg_ &&
diff --git a/absl/synchronization/mutex.h b/absl/synchronization/mutex.h
index 645c26d..d53a22b 100644
--- a/absl/synchronization/mutex.h
+++ b/absl/synchronization/mutex.h
@@ -64,6 +64,7 @@
 #include <iterator>
 #include <string>
 
+#include "absl/base/attributes.h"
 #include "absl/base/const_init.h"
 #include "absl/base/internal/identity.h"
 #include "absl/base/internal/low_level_alloc.h"
@@ -324,7 +325,9 @@
   // `true`, `Await()` *may* skip the release/re-acquire step.
   //
   // `Await()` requires that this thread holds this `Mutex` in some mode.
-  void Await(const Condition& cond);
+  void Await(const Condition& cond) {
+    AwaitCommon(cond, synchronization_internal::KernelTimeout::Never());
+  }
 
   // Mutex::LockWhen()
   // Mutex::ReaderLockWhen()
@@ -334,9 +337,15 @@
   // be acquired, then atomically acquires this `Mutex`. `LockWhen()` is
   // logically equivalent to `*Lock(); Await();` though they may have different
   // performance characteristics.
-  void LockWhen(const Condition& cond) ABSL_EXCLUSIVE_LOCK_FUNCTION();
+  void LockWhen(const Condition& cond) ABSL_EXCLUSIVE_LOCK_FUNCTION() {
+    LockWhenCommon(cond, synchronization_internal::KernelTimeout::Never(),
+                   true);
+  }
 
-  void ReaderLockWhen(const Condition& cond) ABSL_SHARED_LOCK_FUNCTION();
+  void ReaderLockWhen(const Condition& cond) ABSL_SHARED_LOCK_FUNCTION() {
+    LockWhenCommon(cond, synchronization_internal::KernelTimeout::Never(),
+                   false);
+  }
 
   void WriterLockWhen(const Condition& cond) ABSL_EXCLUSIVE_LOCK_FUNCTION() {
     this->LockWhen(cond);
@@ -363,9 +372,13 @@
   // Negative timeouts are equivalent to a zero timeout.
   //
   // This method requires that this thread holds this `Mutex` in some mode.
-  bool AwaitWithTimeout(const Condition& cond, absl::Duration timeout);
+  bool AwaitWithTimeout(const Condition& cond, absl::Duration timeout) {
+    return AwaitCommon(cond, synchronization_internal::KernelTimeout{timeout});
+  }
 
-  bool AwaitWithDeadline(const Condition& cond, absl::Time deadline);
+  bool AwaitWithDeadline(const Condition& cond, absl::Time deadline) {
+    return AwaitCommon(cond, synchronization_internal::KernelTimeout{deadline});
+  }
 
   // Mutex::LockWhenWithTimeout()
   // Mutex::ReaderLockWhenWithTimeout()
@@ -379,9 +392,15 @@
   //
   // Negative timeouts are equivalent to a zero timeout.
   bool LockWhenWithTimeout(const Condition& cond, absl::Duration timeout)
-      ABSL_EXCLUSIVE_LOCK_FUNCTION();
+      ABSL_EXCLUSIVE_LOCK_FUNCTION() {
+    return LockWhenCommon(
+        cond, synchronization_internal::KernelTimeout{timeout}, true);
+  }
   bool ReaderLockWhenWithTimeout(const Condition& cond, absl::Duration timeout)
-      ABSL_SHARED_LOCK_FUNCTION();
+      ABSL_SHARED_LOCK_FUNCTION() {
+    return LockWhenCommon(
+        cond, synchronization_internal::KernelTimeout{timeout}, false);
+  }
   bool WriterLockWhenWithTimeout(const Condition& cond, absl::Duration timeout)
       ABSL_EXCLUSIVE_LOCK_FUNCTION() {
     return this->LockWhenWithTimeout(cond, timeout);
@@ -399,9 +418,15 @@
   //
   // Deadlines in the past are equivalent to an immediate deadline.
   bool LockWhenWithDeadline(const Condition& cond, absl::Time deadline)
-      ABSL_EXCLUSIVE_LOCK_FUNCTION();
+      ABSL_EXCLUSIVE_LOCK_FUNCTION() {
+    return LockWhenCommon(
+        cond, synchronization_internal::KernelTimeout{deadline}, true);
+  }
   bool ReaderLockWhenWithDeadline(const Condition& cond, absl::Time deadline)
-      ABSL_SHARED_LOCK_FUNCTION();
+      ABSL_SHARED_LOCK_FUNCTION() {
+    return LockWhenCommon(
+        cond, synchronization_internal::KernelTimeout{deadline}, false);
+  }
   bool WriterLockWhenWithDeadline(const Condition& cond, absl::Time deadline)
       ABSL_EXCLUSIVE_LOCK_FUNCTION() {
     return this->LockWhenWithDeadline(cond, deadline);
@@ -497,15 +522,22 @@
                 int flags) ABSL_ATTRIBUTE_COLD;
   // slow path release
   void UnlockSlow(SynchWaitParams* waitp) ABSL_ATTRIBUTE_COLD;
+  // TryLock slow path.
+  bool TryLockSlow();
+  // ReaderTryLock slow path.
+  bool ReaderTryLockSlow();
   // Common code between Await() and AwaitWithTimeout/Deadline()
   bool AwaitCommon(const Condition& cond,
                    synchronization_internal::KernelTimeout t);
+  bool LockWhenCommon(const Condition& cond,
+                      synchronization_internal::KernelTimeout t, bool write);
   // Attempt to remove thread s from queue.
   void TryRemove(base_internal::PerThreadSynch* s);
   // Block a thread on mutex.
   void Block(base_internal::PerThreadSynch* s);
   // Wake a thread; return successor.
   base_internal::PerThreadSynch* Wakeup(base_internal::PerThreadSynch* w);
+  void Dtor();
 
   friend class CondVar;   // for access to Trans()/Fer().
   void Trans(MuHow how);  // used for CondVar->Mutex transfer
@@ -708,23 +740,25 @@
   // a function template is passed as `func`. Also, the dummy `typename = void`
   // template parameter exists just to work around a MSVC mangling bug.
   template <typename T, typename = void>
-  Condition(bool (*func)(T*), typename absl::internal::identity<T>::type* arg);
+  Condition(bool (*func)(T*),
+            typename absl::internal::type_identity<T>::type* arg);
 
   // Templated version for invoking a method that returns a `bool`.
   //
   // `Condition(object, &Class::Method)` constructs a `Condition` that evaluates
   // `object->Method()`.
   //
-  // Implementation Note: `absl::internal::identity` is used to allow methods to
-  // come from base classes. A simpler signature like
+  // Implementation Note: `absl::internal::type_identity` is used to allow
+  // methods to come from base classes. A simpler signature like
   // `Condition(T*, bool (T::*)())` does not suffice.
   template <typename T>
-  Condition(T* object, bool (absl::internal::identity<T>::type::*method)());
+  Condition(T* object,
+            bool (absl::internal::type_identity<T>::type::*method)());
 
   // Same as above, for const members
   template <typename T>
   Condition(const T* object,
-            bool (absl::internal::identity<T>::type::*method)() const);
+            bool (absl::internal::type_identity<T>::type::*method)() const);
 
   // A Condition that returns the value of `*cond`
   explicit Condition(const bool* cond);
@@ -816,7 +850,7 @@
   static bool CallVoidPtrFunction(const Condition*);
   template <typename T>
   static bool CastAndCallFunction(const Condition* c);
-  template <typename T>
+  template <typename T, typename ConditionMethodPtr>
   static bool CastAndCallMethod(const Condition* c);
 
   // Helper methods for storing, validating, and reading callback arguments.
@@ -833,8 +867,10 @@
     std::memcpy(callback, callback_, sizeof(*callback));
   }
 
+  static bool AlwaysTrue(const Condition*) { return true; }
+
   // Used only to create kTrue.
-  constexpr Condition() = default;
+  constexpr Condition() : eval_(AlwaysTrue), arg_(nullptr) {}
 };
 
 // -----------------------------------------------------------------------------
@@ -877,7 +913,6 @@
   // A `CondVar` allocated on the heap or on the stack can use the this
   // constructor.
   CondVar();
-  ~CondVar();
 
   // CondVar::Wait()
   //
@@ -886,7 +921,9 @@
   // spurious wakeup), then reacquires the `Mutex` and returns.
   //
   // Requires and ensures that the current thread holds the `Mutex`.
-  void Wait(Mutex* mu);
+  void Wait(Mutex* mu) {
+    WaitCommon(mu, synchronization_internal::KernelTimeout::Never());
+  }
 
   // CondVar::WaitWithTimeout()
   //
@@ -901,7 +938,9 @@
   // to return `true` or `false`.
   //
   // Requires and ensures that the current thread holds the `Mutex`.
-  bool WaitWithTimeout(Mutex* mu, absl::Duration timeout);
+  bool WaitWithTimeout(Mutex* mu, absl::Duration timeout) {
+    return WaitCommon(mu, synchronization_internal::KernelTimeout(timeout));
+  }
 
   // CondVar::WaitWithDeadline()
   //
@@ -918,7 +957,9 @@
   // to return `true` or `false`.
   //
   // Requires and ensures that the current thread holds the `Mutex`.
-  bool WaitWithDeadline(Mutex* mu, absl::Time deadline);
+  bool WaitWithDeadline(Mutex* mu, absl::Time deadline) {
+    return WaitCommon(mu, synchronization_internal::KernelTimeout(deadline));
+  }
 
   // CondVar::Signal()
   //
@@ -940,7 +981,6 @@
  private:
   bool WaitCommon(Mutex* mutex, synchronization_internal::KernelTimeout t);
   void Remove(base_internal::PerThreadSynch* s);
-  void Wakeup(base_internal::PerThreadSynch* w);
   std::atomic<intptr_t> cv_;  // Condition variable state.
   CondVar(const CondVar&) = delete;
   CondVar& operator=(const CondVar&) = delete;
@@ -1024,15 +1064,30 @@
 
 inline constexpr Mutex::Mutex(absl::ConstInitType) : mu_(0) {}
 
+#if !defined(__APPLE__) && !defined(ABSL_BUILD_DLL)
+ABSL_ATTRIBUTE_ALWAYS_INLINE
+inline Mutex::~Mutex() { Dtor(); }
+#endif
+
+#if defined(NDEBUG) && !defined(ABSL_HAVE_THREAD_SANITIZER)
+// Use default (empty) destructor in release build for performance reasons.
+// We need to mark both Dtor and ~Mutex as always inline for inconsistent
+// builds that use both NDEBUG and !NDEBUG with dynamic libraries. In these
+// cases we want the empty functions to dissolve entirely rather than being
+// exported from dynamic libraries and potentially override the non-empty ones.
+ABSL_ATTRIBUTE_ALWAYS_INLINE
+inline void Mutex::Dtor() {}
+#endif
+
 inline CondVar::CondVar() : cv_(0) {}
 
 // static
-template <typename T>
+template <typename T, typename ConditionMethodPtr>
 bool Condition::CastAndCallMethod(const Condition* c) {
   T* object = static_cast<T*>(c->arg_);
-  bool (T::*method_pointer)();
-  c->ReadCallback(&method_pointer);
-  return (object->*method_pointer)();
+  ConditionMethodPtr condition_method_pointer;
+  c->ReadCallback(&condition_method_pointer);
+  return (object->*condition_method_pointer)();
 }
 
 // static
@@ -1054,25 +1109,25 @@
 }
 
 template <typename T, typename>
-inline Condition::Condition(bool (*func)(T*),
-                            typename absl::internal::identity<T>::type* arg)
+inline Condition::Condition(
+    bool (*func)(T*), typename absl::internal::type_identity<T>::type* arg)
     // Just delegate to the overload above.
     : Condition(func, arg) {}
 
 template <typename T>
-inline Condition::Condition(T* object,
-                            bool (absl::internal::identity<T>::type::*method)())
-    : eval_(&CastAndCallMethod<T>), arg_(object) {
+inline Condition::Condition(
+    T* object, bool (absl::internal::type_identity<T>::type::*method)())
+    : eval_(&CastAndCallMethod<T, decltype(method)>), arg_(object) {
   static_assert(sizeof(&method) <= sizeof(callback_),
                 "An overlarge method pointer was passed to Condition.");
   StoreCallback(method);
 }
 
 template <typename T>
-inline Condition::Condition(const T* object,
-                            bool (absl::internal::identity<T>::type::*method)()
-                                const)
-    : eval_(&CastAndCallMethod<T>),
+inline Condition::Condition(
+    const T* object,
+    bool (absl::internal::type_identity<T>::type::*method)() const)
+    : eval_(&CastAndCallMethod<const T, decltype(method)>),
       arg_(reinterpret_cast<void*>(const_cast<T*>(object))) {
   StoreCallback(method);
 }
diff --git a/absl/synchronization/mutex_benchmark.cc b/absl/synchronization/mutex_benchmark.cc
index b5d2fbc..06888df 100644
--- a/absl/synchronization/mutex_benchmark.cc
+++ b/absl/synchronization/mutex_benchmark.cc
@@ -19,6 +19,7 @@
 #include "absl/base/config.h"
 #include "absl/base/internal/cycleclock.h"
 #include "absl/base/internal/spinlock.h"
+#include "absl/base/no_destructor.h"
 #include "absl/synchronization/blocking_counter.h"
 #include "absl/synchronization/internal/thread_pool.h"
 #include "absl/synchronization/mutex.h"
@@ -27,13 +28,41 @@
 namespace {
 
 void BM_Mutex(benchmark::State& state) {
-  static absl::Mutex* mu = new absl::Mutex;
+  static absl::NoDestructor<absl::Mutex> mu;
   for (auto _ : state) {
-    absl::MutexLock lock(mu);
+    absl::MutexLock lock(mu.get());
   }
 }
 BENCHMARK(BM_Mutex)->UseRealTime()->Threads(1)->ThreadPerCpu();
 
+void BM_ReaderLock(benchmark::State& state) {
+  static absl::NoDestructor<absl::Mutex> mu;
+  for (auto _ : state) {
+    absl::ReaderMutexLock lock(mu.get());
+  }
+}
+BENCHMARK(BM_ReaderLock)->UseRealTime()->Threads(1)->ThreadPerCpu();
+
+void BM_TryLock(benchmark::State& state) {
+  absl::Mutex mu;
+  for (auto _ : state) {
+    if (mu.TryLock()) {
+      mu.Unlock();
+    }
+  }
+}
+BENCHMARK(BM_TryLock);
+
+void BM_ReaderTryLock(benchmark::State& state) {
+  static absl::NoDestructor<absl::Mutex> mu;
+  for (auto _ : state) {
+    if (mu->ReaderTryLock()) {
+      mu->ReaderUnlock();
+    }
+  }
+}
+BENCHMARK(BM_ReaderTryLock)->UseRealTime()->Threads(1)->ThreadPerCpu();
+
 static void DelayNs(int64_t ns, int* data) {
   int64_t end = absl::base_internal::CycleClock::Now() +
                 ns * absl::base_internal::CycleClock::Frequency() / 1e9;
@@ -105,7 +134,7 @@
     std::atomic<int> blocked_threads{0};
     std::atomic<bool> thread_has_mutex{false};
   };
-  static Shared* shared = new Shared;
+  static absl::NoDestructor<Shared> shared;
 
   // Set up 'blocked_threads' to count how many threads are currently blocked
   // in Abseil synchronization code.
@@ -183,7 +212,7 @@
     MutexType mu;
     int data = 0;
   };
-  static auto* shared = new Shared;
+  static absl::NoDestructor<Shared> shared;
   int local = 0;
   for (auto _ : state) {
     // Here we model both local work outside of the critical section as well as
diff --git a/absl/synchronization/mutex_test.cc b/absl/synchronization/mutex_test.cc
index b585c34..bb93abc 100644
--- a/absl/synchronization/mutex_test.cc
+++ b/absl/synchronization/mutex_test.cc
@@ -36,10 +36,16 @@
 #include "absl/log/check.h"
 #include "absl/log/log.h"
 #include "absl/memory/memory.h"
+#include "absl/synchronization/internal/create_thread_identity.h"
 #include "absl/synchronization/internal/thread_pool.h"
 #include "absl/time/clock.h"
 #include "absl/time/time.h"
 
+#ifdef ABSL_HAVE_PTHREAD_GETSCHEDPARAM
+#include <pthread.h>
+#include <string.h>
+#endif
+
 namespace {
 
 // TODO(dmauro): Replace with a commandline flag.
@@ -66,6 +72,11 @@
   });
 }
 
+struct ScopedInvariantDebugging {
+  ScopedInvariantDebugging() { absl::EnableMutexInvariantDebugging(true); }
+  ~ScopedInvariantDebugging() { absl::EnableMutexInvariantDebugging(false); }
+};
+
 struct TestContext {
   int iterations;
   int threads;
@@ -389,13 +400,12 @@
                                          int threads, int iterations,
                                          int operations,
                                          void (*invariant)(void *)) {
-  absl::EnableMutexInvariantDebugging(true);
+  ScopedInvariantDebugging scoped_debugging;
   SetInvariantChecked(false);
   TestContext cxt;
   cxt.mu.EnableInvariantDebugging(invariant, &cxt);
   int ret = RunTestCommon(&cxt, test, threads, iterations, operations);
   CHECK(GetInvariantChecked()) << "Invariant not checked";
-  absl::EnableMutexInvariantDebugging(false);  // Restore.
   return ret;
 }
 #endif
@@ -971,6 +981,15 @@
                                      const Derived *>::value));
 }
 
+struct Constable {
+  bool WotsAllThisThen() const { return true; }
+};
+
+TEST(Mutex, FunctionPointerConditionWithConstMethod) {
+  const Constable chapman;
+  EXPECT_TRUE(absl::Condition(&chapman, &Constable::WotsAllThisThen).Eval());
+}
+
 struct True {
   template <class... Args>
   bool operator()(Args...) const {
@@ -1019,6 +1038,19 @@
   }
 }
 
+TEST(Mutex, ConditionSwap) {
+  // Ensure that Conditions can be swap'ed.
+  bool b1 = true;
+  absl::Condition c1(&b1);
+  bool b2 = false;
+  absl::Condition c2(&b2);
+  EXPECT_TRUE(c1.Eval());
+  EXPECT_FALSE(c2.Eval());
+  std::swap(c1, c2);
+  EXPECT_FALSE(c1.Eval());
+  EXPECT_TRUE(c2.Eval());
+}
+
 // --------------------------------------------------------
 // Test for bug with pattern of readers using a condvar.  The bug was that if a
 // reader went to sleep on a condition variable while one or more other readers
@@ -1690,6 +1722,61 @@
   logged_cv.SignalAll();
 }
 
+TEST(Mutex, LoggingAddressReuse) {
+  // Repeatedly re-create a Mutex with debug logging at the same address.
+  ScopedInvariantDebugging scoped_debugging;
+  alignas(absl::Mutex) char storage[sizeof(absl::Mutex)];
+  auto invariant =
+      +[](void *alive) { EXPECT_TRUE(*static_cast<bool *>(alive)); };
+  constexpr size_t kIters = 10;
+  bool alive[kIters] = {};
+  for (size_t i = 0; i < kIters; ++i) {
+    absl::Mutex *mu = new (storage) absl::Mutex;
+    alive[i] = true;
+    mu->EnableDebugLog("Mutex");
+    mu->EnableInvariantDebugging(invariant, &alive[i]);
+    mu->Lock();
+    mu->Unlock();
+    mu->~Mutex();
+    alive[i] = false;
+  }
+}
+
+TEST(Mutex, LoggingBankrupcy) {
+  // Test the case with too many live Mutexes with debug logging.
+  ScopedInvariantDebugging scoped_debugging;
+  std::vector<absl::Mutex> mus(1 << 20);
+  for (auto &mu : mus) {
+    mu.EnableDebugLog("Mutex");
+  }
+}
+
+TEST(Mutex, SynchEventRace) {
+  // Regression test for a false TSan race report in
+  // EnableInvariantDebugging/EnableDebugLog related to SynchEvent reuse.
+  ScopedInvariantDebugging scoped_debugging;
+  std::vector<std::thread> threads;
+  for (size_t i = 0; i < 5; i++) {
+    threads.emplace_back([&] {
+      for (size_t j = 0; j < (1 << 17); j++) {
+        {
+          absl::Mutex mu;
+          mu.EnableInvariantDebugging([](void *) {}, nullptr);
+          mu.Lock();
+          mu.Unlock();
+        }
+        {
+          absl::Mutex mu;
+          mu.EnableDebugLog("Mutex");
+        }
+      }
+    });
+  }
+  for (auto &thread : threads) {
+    thread.join();
+  }
+}
+
 // --------------------------------------------------------
 
 // Generate the vector of thread counts for tests parameterized on thread count.
@@ -1868,6 +1955,60 @@
   EXPECT_TRUE(saw_wrote.load());
 }
 
+#ifdef ABSL_HAVE_PTHREAD_GETSCHEDPARAM
+TEST(Mutex, CondVarPriority) {
+  // A regression test for a bug in condition variable wait morphing,
+  // which resulted in the waiting thread getting priority of the waking thread.
+  int err = 0;
+  sched_param param;
+  param.sched_priority = 7;
+  std::thread test([&]() {
+    err = pthread_setschedparam(pthread_self(), SCHED_FIFO, &param);
+  });
+  test.join();
+  if (err) {
+    // Setting priority usually requires special privileges.
+    GTEST_SKIP() << "failed to set priority: " << strerror(err);
+  }
+  absl::Mutex mu;
+  absl::CondVar cv;
+  bool locked = false;
+  bool notified = false;
+  bool waiting = false;
+  bool morph = false;
+  std::thread th([&]() {
+    EXPECT_EQ(0, pthread_setschedparam(pthread_self(), SCHED_FIFO, &param));
+    mu.Lock();
+    locked = true;
+    mu.Await(absl::Condition(&notified));
+    mu.Unlock();
+    EXPECT_EQ(absl::synchronization_internal::GetOrCreateCurrentThreadIdentity()
+                  ->per_thread_synch.priority,
+              param.sched_priority);
+    mu.Lock();
+    mu.Await(absl::Condition(&waiting));
+    morph = true;
+    absl::SleepFor(absl::Seconds(1));
+    cv.Signal();
+    mu.Unlock();
+  });
+  mu.Lock();
+  mu.Await(absl::Condition(&locked));
+  notified = true;
+  mu.Unlock();
+  mu.Lock();
+  waiting = true;
+  while (!morph) {
+    cv.Wait(&mu);
+  }
+  mu.Unlock();
+  th.join();
+  EXPECT_NE(absl::synchronization_internal::GetOrCreateCurrentThreadIdentity()
+                ->per_thread_synch.priority,
+            param.sched_priority);
+}
+#endif
+
 TEST(Mutex, LockWhenWithTimeoutResult) {
   // Check various corner cases for Await/LockWhen return value
   // with always true/always false conditions.
diff --git a/absl/time/BUILD.bazel b/absl/time/BUILD.bazel
index 88d2088..e3fe705 100644
--- a/absl/time/BUILD.bazel
+++ b/absl/time/BUILD.bazel
@@ -21,7 +21,14 @@
     "ABSL_TEST_COPTS",
 )
 
-package(default_visibility = ["//visibility:public"])
+package(
+    default_visibility = ["//visibility:public"],
+    features = [
+        "header_modules",
+        "layering_check",
+        "parse_headers",
+    ],
+)
 
 licenses(["notice"])
 
@@ -93,6 +100,7 @@
         "//absl/numeric:int128",
         "//absl/strings:str_format",
         "//absl/time/internal/cctz:time_zone",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -117,6 +125,7 @@
         ":time",
         "//absl/flags:flag",
         "//absl/flags:reflection",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
diff --git a/absl/time/civil_time.h b/absl/time/civil_time.h
index 5855bc7..3e904a1 100644
--- a/absl/time/civil_time.h
+++ b/absl/time/civil_time.h
@@ -462,6 +462,32 @@
 std::string FormatCivilTime(CivilMonth c);
 std::string FormatCivilTime(CivilYear c);
 
+// Support for StrFormat(), StrCat(), etc
+template <typename Sink>
+void AbslStringify(Sink& sink, CivilSecond c) {
+  sink.Append(FormatCivilTime(c));
+}
+template <typename Sink>
+void AbslStringify(Sink& sink, CivilMinute c) {
+  sink.Append(FormatCivilTime(c));
+}
+template <typename Sink>
+void AbslStringify(Sink& sink, CivilHour c) {
+  sink.Append(FormatCivilTime(c));
+}
+template <typename Sink>
+void AbslStringify(Sink& sink, CivilDay c) {
+  sink.Append(FormatCivilTime(c));
+}
+template <typename Sink>
+void AbslStringify(Sink& sink, CivilMonth c) {
+  sink.Append(FormatCivilTime(c));
+}
+template <typename Sink>
+void AbslStringify(Sink& sink, CivilYear c) {
+  sink.Append(FormatCivilTime(c));
+}
+
 // absl::ParseCivilTime()
 //
 // Parses a civil-time value from the specified `absl::string_view` into the
diff --git a/absl/time/civil_time_benchmark.cc b/absl/time/civil_time_benchmark.cc
index f04dbe2..2de0233 100644
--- a/absl/time/civil_time_benchmark.cc
+++ b/absl/time/civil_time_benchmark.cc
@@ -14,7 +14,9 @@
 
 #include "absl/time/civil_time.h"
 
+#include <cstddef>
 #include <numeric>
+#include <string>
 #include <vector>
 
 #include "absl/hash/hash.h"
@@ -42,7 +44,7 @@
   const absl::CivilDay c(2014, 8, 22);
   const absl::CivilDay epoch(1970, 1, 1);
   while (state.KeepRunning()) {
-    const absl::civil_diff_t n = c - epoch;
+    absl::civil_diff_t n = c - epoch;
     benchmark::DoNotOptimize(n);
   }
 }
@@ -60,7 +62,7 @@
 void BM_Format(benchmark::State& state) {
   const absl::CivilSecond c(2014, 1, 2, 3, 4, 5);
   while (state.KeepRunning()) {
-    const std::string s = absl::FormatCivilTime(c);
+    std::string s = absl::FormatCivilTime(c);
     benchmark::DoNotOptimize(s);
   }
 }
@@ -70,7 +72,7 @@
   const std::string f = "2014-01-02T03:04:05";
   absl::CivilSecond c;
   while (state.KeepRunning()) {
-    const bool b = absl::ParseCivilTime(f, &c);
+    bool b = absl::ParseCivilTime(f, &c);
     benchmark::DoNotOptimize(b);
   }
 }
@@ -80,7 +82,7 @@
   const absl::CivilSecond c(2014, 1, 2, 3, 4, 5);
   absl::CivilSecond out;
   while (state.KeepRunning()) {
-    const bool b = absl::ParseCivilTime(absl::FormatCivilTime(c), &out);
+    bool b = absl::ParseCivilTime(absl::FormatCivilTime(c), &out);
     benchmark::DoNotOptimize(b);
   }
 }
@@ -95,7 +97,8 @@
   absl::Hash<T> absl_hasher;
   while (state.KeepRunningBatch(kSize)) {
     for (const T civil_time : civil_times) {
-      benchmark::DoNotOptimize(absl_hasher(civil_time));
+      size_t hash = absl_hasher(civil_time);
+      benchmark::DoNotOptimize(hash);
     }
   }
 }
diff --git a/absl/time/civil_time_test.cc b/absl/time/civil_time_test.cc
index ec435ac..19292a9 100644
--- a/absl/time/civil_time_test.cc
+++ b/absl/time/civil_time_test.cc
@@ -14,12 +14,14 @@
 
 #include "absl/time/civil_time.h"
 
+#include <iomanip>
 #include <limits>
 #include <sstream>
 #include <type_traits>
 
-#include "absl/base/macros.h"
 #include "gtest/gtest.h"
+#include "absl/base/macros.h"
+#include "absl/strings/str_format.h"
 
 namespace {
 
@@ -868,6 +870,23 @@
   EXPECT_FALSE(absl::ParseLenientCivilTime("9223372036854775808", &y)) << y;
 }
 
+TEST(CivilTime, AbslStringify) {
+  EXPECT_EQ("2015-01-02T03:04:05",
+            absl::StrFormat("%v", absl::CivilSecond(2015, 1, 2, 3, 4, 5)));
+
+  EXPECT_EQ("2015-01-02T03:04",
+            absl::StrFormat("%v", absl::CivilMinute(2015, 1, 2, 3, 4)));
+
+  EXPECT_EQ("2015-01-02T03",
+            absl::StrFormat("%v", absl::CivilHour(2015, 1, 2, 3)));
+
+  EXPECT_EQ("2015-01-02", absl::StrFormat("%v", absl::CivilDay(2015, 1, 2)));
+
+  EXPECT_EQ("2015-01", absl::StrFormat("%v", absl::CivilMonth(2015, 1)));
+
+  EXPECT_EQ("2015", absl::StrFormat("%v", absl::CivilYear(2015)));
+}
+
 TEST(CivilTime, OutputStream) {
   absl::CivilSecond cs(2016, 2, 3, 4, 5, 6);
   {
diff --git a/absl/time/clock.h b/absl/time/clock.h
index 5fe244d..41d2cf2 100644
--- a/absl/time/clock.h
+++ b/absl/time/clock.h
@@ -22,6 +22,9 @@
 #ifndef ABSL_TIME_CLOCK_H_
 #define ABSL_TIME_CLOCK_H_
 
+#include <cstdint>
+
+#include "absl/base/config.h"
 #include "absl/base/macros.h"
 #include "absl/time/time.h"
 
@@ -64,7 +67,8 @@
 // By changing our extension points to be extern "C", we dodge this
 // check.
 extern "C" {
-void ABSL_INTERNAL_C_SYMBOL(AbslInternalSleepFor)(absl::Duration duration);
+ABSL_DLL void ABSL_INTERNAL_C_SYMBOL(AbslInternalSleepFor)(
+    absl::Duration duration);
 }  // extern "C"
 
 inline void absl::SleepFor(absl::Duration duration) {
diff --git a/absl/time/duration.cc b/absl/time/duration.cc
index 634e5d5..bdb16e2 100644
--- a/absl/time/duration.cc
+++ b/absl/time/duration.cc
@@ -55,8 +55,7 @@
 
 #include <algorithm>
 #include <cassert>
-#include <cctype>
-#include <cerrno>
+#include <chrono>  // NOLINT(build/c++11)
 #include <cmath>
 #include <cstdint>
 #include <cstdlib>
@@ -66,8 +65,9 @@
 #include <limits>
 #include <string>
 
+#include "absl/base/attributes.h"
 #include "absl/base/casts.h"
-#include "absl/base/macros.h"
+#include "absl/base/config.h"
 #include "absl/numeric/int128.h"
 #include "absl/strings/string_view.h"
 #include "absl/strings/strip.h"
diff --git a/absl/time/internal/cctz/BUILD.bazel b/absl/time/internal/cctz/BUILD.bazel
index 4c5ad07..0b43bb1 100644
--- a/absl/time/internal/cctz/BUILD.bazel
+++ b/absl/time/internal/cctz/BUILD.bazel
@@ -12,7 +12,11 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
-package(features = ["-parse_headers"])
+package(features = [
+    "header_modules",
+    "layering_check",
+    "parse_headers",
+])
 
 licenses(["notice"])
 
@@ -83,6 +87,7 @@
     deps = [
         ":civil_time",
         "//absl/base:config",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -103,6 +108,7 @@
         ":civil_time",
         ":time_zone",
         "//absl/base:config",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -124,6 +130,7 @@
         ":civil_time",
         ":time_zone",
         "//absl/base:config",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
diff --git a/absl/time/internal/cctz/include/cctz/civil_time_detail.h b/absl/time/internal/cctz/include/cctz/civil_time_detail.h
index a5b084e..2b0aed5 100644
--- a/absl/time/internal/cctz/include/cctz/civil_time_detail.h
+++ b/absl/time/internal/cctz/include/cctz/civil_time_detail.h
@@ -401,11 +401,11 @@
       : civil_time(ct.f_) {}
 
   // Factories for the maximum/minimum representable civil_time.
-  static CONSTEXPR_F civil_time(max)() {
+  static CONSTEXPR_F auto(max)() -> civil_time {
     const auto max_year = (std::numeric_limits<std::int_least64_t>::max)();
     return civil_time(max_year, 12, 31, 23, 59, 59);
   }
-  static CONSTEXPR_F civil_time(min)() {
+  static CONSTEXPR_F auto(min)() -> civil_time {
     const auto min_year = (std::numeric_limits<std::int_least64_t>::min)();
     return civil_time(min_year, 1, 1, 0, 0, 0);
   }
diff --git a/absl/time/internal/cctz/src/cctz_benchmark.cc b/absl/time/internal/cctz/src/cctz_benchmark.cc
index c64f380..11f9ba6 100644
--- a/absl/time/internal/cctz/src/cctz_benchmark.cc
+++ b/absl/time/internal/cctz/src/cctz_benchmark.cc
@@ -110,7 +110,6 @@
                                       "Africa/Addis_Ababa",
                                       "Africa/Algiers",
                                       "Africa/Asmara",
-                                      "Africa/Asmera",
                                       "Africa/Bamako",
                                       "Africa/Bangui",
                                       "Africa/Banjul",
@@ -166,7 +165,6 @@
                                       "America/Araguaina",
                                       "America/Argentina/Buenos_Aires",
                                       "America/Argentina/Catamarca",
-                                      "America/Argentina/ComodRivadavia",
                                       "America/Argentina/Cordoba",
                                       "America/Argentina/Jujuy",
                                       "America/Argentina/La_Rioja",
@@ -190,18 +188,16 @@
                                       "America/Boa_Vista",
                                       "America/Bogota",
                                       "America/Boise",
-                                      "America/Buenos_Aires",
                                       "America/Cambridge_Bay",
                                       "America/Campo_Grande",
                                       "America/Cancun",
                                       "America/Caracas",
-                                      "America/Catamarca",
                                       "America/Cayenne",
                                       "America/Cayman",
                                       "America/Chicago",
                                       "America/Chihuahua",
+                                      "America/Ciudad_Juarez",
                                       "America/Coral_Harbour",
-                                      "America/Cordoba",
                                       "America/Costa_Rica",
                                       "America/Creston",
                                       "America/Cuiaba",
@@ -217,7 +213,6 @@
                                       "America/El_Salvador",
                                       "America/Ensenada",
                                       "America/Fort_Nelson",
-                                      "America/Fort_Wayne",
                                       "America/Fortaleza",
                                       "America/Glace_Bay",
                                       "America/Godthab",
@@ -239,20 +234,16 @@
                                       "America/Indiana/Vevay",
                                       "America/Indiana/Vincennes",
                                       "America/Indiana/Winamac",
-                                      "America/Indianapolis",
                                       "America/Inuvik",
                                       "America/Iqaluit",
                                       "America/Jamaica",
-                                      "America/Jujuy",
                                       "America/Juneau",
                                       "America/Kentucky/Louisville",
                                       "America/Kentucky/Monticello",
-                                      "America/Knox_IN",
                                       "America/Kralendijk",
                                       "America/La_Paz",
                                       "America/Lima",
                                       "America/Los_Angeles",
-                                      "America/Louisville",
                                       "America/Lower_Princes",
                                       "America/Maceio",
                                       "America/Managua",
@@ -261,7 +252,6 @@
                                       "America/Martinique",
                                       "America/Matamoros",
                                       "America/Mazatlan",
-                                      "America/Mendoza",
                                       "America/Menominee",
                                       "America/Merida",
                                       "America/Metlakatla",
@@ -298,7 +288,6 @@
                                       "America/Regina",
                                       "America/Resolute",
                                       "America/Rio_Branco",
-                                      "America/Rosario",
                                       "America/Santa_Isabel",
                                       "America/Santarem",
                                       "America/Santiago",
@@ -334,7 +323,6 @@
                                       "Antarctica/McMurdo",
                                       "Antarctica/Palmer",
                                       "Antarctica/Rothera",
-                                      "Antarctica/South_Pole",
                                       "Antarctica/Syowa",
                                       "Antarctica/Troll",
                                       "Antarctica/Vostok",
@@ -346,7 +334,6 @@
                                       "Asia/Aqtau",
                                       "Asia/Aqtobe",
                                       "Asia/Ashgabat",
-                                      "Asia/Ashkhabad",
                                       "Asia/Atyrau",
                                       "Asia/Baghdad",
                                       "Asia/Bahrain",
@@ -356,13 +343,10 @@
                                       "Asia/Beirut",
                                       "Asia/Bishkek",
                                       "Asia/Brunei",
-                                      "Asia/Calcutta",
                                       "Asia/Chita",
                                       "Asia/Choibalsan",
                                       "Asia/Chongqing",
-                                      "Asia/Chungking",
                                       "Asia/Colombo",
-                                      "Asia/Dacca",
                                       "Asia/Damascus",
                                       "Asia/Dhaka",
                                       "Asia/Dili",
@@ -385,14 +369,12 @@
                                       "Asia/Karachi",
                                       "Asia/Kashgar",
                                       "Asia/Kathmandu",
-                                      "Asia/Katmandu",
                                       "Asia/Khandyga",
                                       "Asia/Kolkata",
                                       "Asia/Krasnoyarsk",
                                       "Asia/Kuala_Lumpur",
                                       "Asia/Kuching",
                                       "Asia/Kuwait",
-                                      "Asia/Macao",
                                       "Asia/Macau",
                                       "Asia/Magadan",
                                       "Asia/Makassar",
@@ -409,9 +391,7 @@
                                       "Asia/Qatar",
                                       "Asia/Qostanay",
                                       "Asia/Qyzylorda",
-                                      "Asia/Rangoon",
                                       "Asia/Riyadh",
-                                      "Asia/Saigon",
                                       "Asia/Sakhalin",
                                       "Asia/Samarkand",
                                       "Asia/Seoul",
@@ -423,13 +403,10 @@
                                       "Asia/Tbilisi",
                                       "Asia/Tehran",
                                       "Asia/Tel_Aviv",
-                                      "Asia/Thimbu",
                                       "Asia/Thimphu",
                                       "Asia/Tokyo",
                                       "Asia/Tomsk",
-                                      "Asia/Ujung_Pandang",
                                       "Asia/Ulaanbaatar",
-                                      "Asia/Ulan_Bator",
                                       "Asia/Urumqi",
                                       "Asia/Ust-Nera",
                                       "Asia/Vientiane",
@@ -442,7 +419,6 @@
                                       "Atlantic/Bermuda",
                                       "Atlantic/Canary",
                                       "Atlantic/Cape_Verde",
-                                      "Atlantic/Faeroe",
                                       "Atlantic/Faroe",
                                       "Atlantic/Jan_Mayen",
                                       "Atlantic/Madeira",
@@ -450,7 +426,6 @@
                                       "Atlantic/South_Georgia",
                                       "Atlantic/St_Helena",
                                       "Atlantic/Stanley",
-                                      "Australia/ACT",
                                       "Australia/Adelaide",
                                       "Australia/Brisbane",
                                       "Australia/Broken_Hill",
@@ -459,42 +434,12 @@
                                       "Australia/Darwin",
                                       "Australia/Eucla",
                                       "Australia/Hobart",
-                                      "Australia/LHI",
                                       "Australia/Lindeman",
                                       "Australia/Lord_Howe",
                                       "Australia/Melbourne",
-                                      "Australia/NSW",
-                                      "Australia/North",
                                       "Australia/Perth",
-                                      "Australia/Queensland",
-                                      "Australia/South",
                                       "Australia/Sydney",
-                                      "Australia/Tasmania",
-                                      "Australia/Victoria",
-                                      "Australia/West",
                                       "Australia/Yancowinna",
-                                      "Brazil/Acre",
-                                      "Brazil/DeNoronha",
-                                      "Brazil/East",
-                                      "Brazil/West",
-                                      "CET",
-                                      "CST6CDT",
-                                      "Canada/Atlantic",
-                                      "Canada/Central",
-                                      "Canada/Eastern",
-                                      "Canada/Mountain",
-                                      "Canada/Newfoundland",
-                                      "Canada/Pacific",
-                                      "Canada/Saskatchewan",
-                                      "Canada/Yukon",
-                                      "Chile/Continental",
-                                      "Chile/EasterIsland",
-                                      "Cuba",
-                                      "EET",
-                                      "EST",
-                                      "EST5EDT",
-                                      "Egypt",
-                                      "Eire",
                                       "Etc/GMT",
                                       "Etc/GMT+0",
                                       "Etc/GMT+1",
@@ -552,7 +497,6 @@
                                       "Europe/Istanbul",
                                       "Europe/Jersey",
                                       "Europe/Kaliningrad",
-                                      "Europe/Kiev",
                                       "Europe/Kirov",
                                       "Europe/Kyiv",
                                       "Europe/Lisbon",
@@ -584,7 +528,6 @@
                                       "Europe/Tirane",
                                       "Europe/Tiraspol",
                                       "Europe/Ulyanovsk",
-                                      "Europe/Uzhgorod",
                                       "Europe/Vaduz",
                                       "Europe/Vatican",
                                       "Europe/Vienna",
@@ -592,19 +535,8 @@
                                       "Europe/Volgograd",
                                       "Europe/Warsaw",
                                       "Europe/Zagreb",
-                                      "Europe/Zaporozhye",
                                       "Europe/Zurich",
                                       "Factory",
-                                      "GB",
-                                      "GB-Eire",
-                                      "GMT",
-                                      "GMT+0",
-                                      "GMT-0",
-                                      "GMT0",
-                                      "Greenwich",
-                                      "HST",
-                                      "Hongkong",
-                                      "Iceland",
                                       "Indian/Antananarivo",
                                       "Indian/Chagos",
                                       "Indian/Christmas",
@@ -616,23 +548,6 @@
                                       "Indian/Mauritius",
                                       "Indian/Mayotte",
                                       "Indian/Reunion",
-                                      "Iran",
-                                      "Israel",
-                                      "Jamaica",
-                                      "Japan",
-                                      "Kwajalein",
-                                      "Libya",
-                                      "MET",
-                                      "MST",
-                                      "MST7MDT",
-                                      "Mexico/BajaNorte",
-                                      "Mexico/BajaSur",
-                                      "Mexico/General",
-                                      "NZ",
-                                      "NZ-CHAT",
-                                      "Navajo",
-                                      "PRC",
-                                      "PST8PDT",
                                       "Pacific/Apia",
                                       "Pacific/Auckland",
                                       "Pacific/Bougainville",
@@ -640,7 +555,6 @@
                                       "Pacific/Chuuk",
                                       "Pacific/Easter",
                                       "Pacific/Efate",
-                                      "Pacific/Enderbury",
                                       "Pacific/Fakaofo",
                                       "Pacific/Fiji",
                                       "Pacific/Funafuti",
@@ -665,7 +579,6 @@
                                       "Pacific/Palau",
                                       "Pacific/Pitcairn",
                                       "Pacific/Pohnpei",
-                                      "Pacific/Ponape",
                                       "Pacific/Port_Moresby",
                                       "Pacific/Rarotonga",
                                       "Pacific/Saipan",
@@ -673,34 +586,10 @@
                                       "Pacific/Tahiti",
                                       "Pacific/Tarawa",
                                       "Pacific/Tongatapu",
-                                      "Pacific/Truk",
                                       "Pacific/Wake",
                                       "Pacific/Wallis",
                                       "Pacific/Yap",
-                                      "Poland",
-                                      "Portugal",
-                                      "ROC",
-                                      "ROK",
-                                      "Singapore",
-                                      "Turkey",
-                                      "UCT",
-                                      "US/Alaska",
-                                      "US/Aleutian",
-                                      "US/Arizona",
-                                      "US/Central",
-                                      "US/East-Indiana",
-                                      "US/Eastern",
-                                      "US/Hawaii",
-                                      "US/Indiana-Starke",
-                                      "US/Michigan",
-                                      "US/Mountain",
-                                      "US/Pacific",
-                                      "US/Samoa",
                                       "UTC",
-                                      "Universal",
-                                      "W-SU",
-                                      "WET",
-                                      "Zulu",
                                       nullptr};
 
 std::vector<std::string> AllTimeZoneNames() {
diff --git a/absl/time/internal/cctz/src/time_zone_format.cc b/absl/time/internal/cctz/src/time_zone_format.cc
index 9b91f61..e7e30a2 100644
--- a/absl/time/internal/cctz/src/time_zone_format.cc
+++ b/absl/time/internal/cctz/src/time_zone_format.cc
@@ -19,7 +19,7 @@
 #endif
 
 #if defined(HAS_STRPTIME) && HAS_STRPTIME
-#if !defined(_XOPEN_SOURCE) && !defined(__OpenBSD__)
+#if !defined(_XOPEN_SOURCE) && !defined(__FreeBSD__) && !defined(__OpenBSD__)
 #define _XOPEN_SOURCE 500  // Exposes definitions for SUSv2 (UNIX 98).
 #endif
 #endif
diff --git a/absl/time/internal/cctz/src/time_zone_info.cc b/absl/time/internal/cctz/src/time_zone_info.cc
index f46198f..b7178a6 100644
--- a/absl/time/internal/cctz/src/time_zone_info.cc
+++ b/absl/time/internal/cctz/src/time_zone_info.cc
@@ -474,7 +474,8 @@
   const std::size_t pos = (name.compare(0, 5, "file:") == 0) ? 5 : 0;
 
   // See Android's libc/tzcode/bionic.cpp for additional information.
-  for (const char* tzdata : {"/data/misc/zoneinfo/current/tzdata",
+  for (const char* tzdata : {"/apex/com.android.tzdata/etc/tz/tzdata",
+                             "/data/misc/zoneinfo/current/tzdata",
                              "/system/usr/share/zoneinfo/tzdata"}) {
     auto fp = FOpen(tzdata, "rb");
     if (fp == nullptr) continue;
@@ -539,9 +540,16 @@
   // Prefixes where a Fuchsia component might find zoneinfo files,
   // in descending order of preference.
   const auto kTzdataPrefixes = {
+      // The tzdata from `config-data`.
       "/config/data/tzdata/",
+      // The tzdata bundled in the component's package.
       "/pkg/data/tzdata/",
+      // General data storage.
       "/data/tzdata/",
+      // The recommended path for routed-in tzdata files.
+      // See for details:
+      // https://fuchsia.dev/fuchsia-src/concepts/process/namespaces?hl=en#typical_directory_structure
+      "/config/tzdata/",
   };
   const auto kEmptyPrefix = {""};
   const bool name_absolute = (pos != name.size() && name[pos] == '/');
@@ -744,19 +752,6 @@
     version_ = zip->Version();
   }
 
-  // Trim redundant transitions. zic may have added these to work around
-  // differences between the glibc and reference implementations (see
-  // zic.c:dontmerge) or to avoid bugs in old readers. For us, they just
-  // get in the way when we do future_spec_ extension.
-  while (hdr.timecnt > 1) {
-    if (!EquivTransitions(transitions_[hdr.timecnt - 1].type_index,
-                          transitions_[hdr.timecnt - 2].type_index)) {
-      break;
-    }
-    hdr.timecnt -= 1;
-  }
-  transitions_.resize(hdr.timecnt);
-
   // Ensure that there is always a transition in the first half of the
   // time line (the second half is handled below) so that the signed
   // difference between a civil_second and the civil_second of its
diff --git a/absl/time/internal/cctz/src/time_zone_lookup_test.cc b/absl/time/internal/cctz/src/time_zone_lookup_test.cc
index 4884c32..6f7e5cf 100644
--- a/absl/time/internal/cctz/src/time_zone_lookup_test.cc
+++ b/absl/time/internal/cctz/src/time_zone_lookup_test.cc
@@ -45,7 +45,6 @@
                                       "Africa/Addis_Ababa",
                                       "Africa/Algiers",
                                       "Africa/Asmara",
-                                      "Africa/Asmera",
                                       "Africa/Bamako",
                                       "Africa/Bangui",
                                       "Africa/Banjul",
@@ -101,7 +100,6 @@
                                       "America/Araguaina",
                                       "America/Argentina/Buenos_Aires",
                                       "America/Argentina/Catamarca",
-                                      "America/Argentina/ComodRivadavia",
                                       "America/Argentina/Cordoba",
                                       "America/Argentina/Jujuy",
                                       "America/Argentina/La_Rioja",
@@ -125,19 +123,16 @@
                                       "America/Boa_Vista",
                                       "America/Bogota",
                                       "America/Boise",
-                                      "America/Buenos_Aires",
                                       "America/Cambridge_Bay",
                                       "America/Campo_Grande",
                                       "America/Cancun",
                                       "America/Caracas",
-                                      "America/Catamarca",
                                       "America/Cayenne",
                                       "America/Cayman",
                                       "America/Chicago",
                                       "America/Chihuahua",
                                       "America/Ciudad_Juarez",
                                       "America/Coral_Harbour",
-                                      "America/Cordoba",
                                       "America/Costa_Rica",
                                       "America/Creston",
                                       "America/Cuiaba",
@@ -153,7 +148,6 @@
                                       "America/El_Salvador",
                                       "America/Ensenada",
                                       "America/Fort_Nelson",
-                                      "America/Fort_Wayne",
                                       "America/Fortaleza",
                                       "America/Glace_Bay",
                                       "America/Godthab",
@@ -175,20 +169,16 @@
                                       "America/Indiana/Vevay",
                                       "America/Indiana/Vincennes",
                                       "America/Indiana/Winamac",
-                                      "America/Indianapolis",
                                       "America/Inuvik",
                                       "America/Iqaluit",
                                       "America/Jamaica",
-                                      "America/Jujuy",
                                       "America/Juneau",
                                       "America/Kentucky/Louisville",
                                       "America/Kentucky/Monticello",
-                                      "America/Knox_IN",
                                       "America/Kralendijk",
                                       "America/La_Paz",
                                       "America/Lima",
                                       "America/Los_Angeles",
-                                      "America/Louisville",
                                       "America/Lower_Princes",
                                       "America/Maceio",
                                       "America/Managua",
@@ -197,7 +187,6 @@
                                       "America/Martinique",
                                       "America/Matamoros",
                                       "America/Mazatlan",
-                                      "America/Mendoza",
                                       "America/Menominee",
                                       "America/Merida",
                                       "America/Metlakatla",
@@ -234,7 +223,6 @@
                                       "America/Regina",
                                       "America/Resolute",
                                       "America/Rio_Branco",
-                                      "America/Rosario",
                                       "America/Santa_Isabel",
                                       "America/Santarem",
                                       "America/Santiago",
@@ -270,7 +258,6 @@
                                       "Antarctica/McMurdo",
                                       "Antarctica/Palmer",
                                       "Antarctica/Rothera",
-                                      "Antarctica/South_Pole",
                                       "Antarctica/Syowa",
                                       "Antarctica/Troll",
                                       "Antarctica/Vostok",
@@ -282,7 +269,6 @@
                                       "Asia/Aqtau",
                                       "Asia/Aqtobe",
                                       "Asia/Ashgabat",
-                                      "Asia/Ashkhabad",
                                       "Asia/Atyrau",
                                       "Asia/Baghdad",
                                       "Asia/Bahrain",
@@ -292,13 +278,10 @@
                                       "Asia/Beirut",
                                       "Asia/Bishkek",
                                       "Asia/Brunei",
-                                      "Asia/Calcutta",
                                       "Asia/Chita",
                                       "Asia/Choibalsan",
                                       "Asia/Chongqing",
-                                      "Asia/Chungking",
                                       "Asia/Colombo",
-                                      "Asia/Dacca",
                                       "Asia/Damascus",
                                       "Asia/Dhaka",
                                       "Asia/Dili",
@@ -321,14 +304,12 @@
                                       "Asia/Karachi",
                                       "Asia/Kashgar",
                                       "Asia/Kathmandu",
-                                      "Asia/Katmandu",
                                       "Asia/Khandyga",
                                       "Asia/Kolkata",
                                       "Asia/Krasnoyarsk",
                                       "Asia/Kuala_Lumpur",
                                       "Asia/Kuching",
                                       "Asia/Kuwait",
-                                      "Asia/Macao",
                                       "Asia/Macau",
                                       "Asia/Magadan",
                                       "Asia/Makassar",
@@ -345,9 +326,7 @@
                                       "Asia/Qatar",
                                       "Asia/Qostanay",
                                       "Asia/Qyzylorda",
-                                      "Asia/Rangoon",
                                       "Asia/Riyadh",
-                                      "Asia/Saigon",
                                       "Asia/Sakhalin",
                                       "Asia/Samarkand",
                                       "Asia/Seoul",
@@ -359,13 +338,10 @@
                                       "Asia/Tbilisi",
                                       "Asia/Tehran",
                                       "Asia/Tel_Aviv",
-                                      "Asia/Thimbu",
                                       "Asia/Thimphu",
                                       "Asia/Tokyo",
                                       "Asia/Tomsk",
-                                      "Asia/Ujung_Pandang",
                                       "Asia/Ulaanbaatar",
-                                      "Asia/Ulan_Bator",
                                       "Asia/Urumqi",
                                       "Asia/Ust-Nera",
                                       "Asia/Vientiane",
@@ -378,7 +354,6 @@
                                       "Atlantic/Bermuda",
                                       "Atlantic/Canary",
                                       "Atlantic/Cape_Verde",
-                                      "Atlantic/Faeroe",
                                       "Atlantic/Faroe",
                                       "Atlantic/Jan_Mayen",
                                       "Atlantic/Madeira",
@@ -386,7 +361,6 @@
                                       "Atlantic/South_Georgia",
                                       "Atlantic/St_Helena",
                                       "Atlantic/Stanley",
-                                      "Australia/ACT",
                                       "Australia/Adelaide",
                                       "Australia/Brisbane",
                                       "Australia/Broken_Hill",
@@ -395,42 +369,12 @@
                                       "Australia/Darwin",
                                       "Australia/Eucla",
                                       "Australia/Hobart",
-                                      "Australia/LHI",
                                       "Australia/Lindeman",
                                       "Australia/Lord_Howe",
                                       "Australia/Melbourne",
-                                      "Australia/NSW",
-                                      "Australia/North",
                                       "Australia/Perth",
-                                      "Australia/Queensland",
-                                      "Australia/South",
                                       "Australia/Sydney",
-                                      "Australia/Tasmania",
-                                      "Australia/Victoria",
-                                      "Australia/West",
                                       "Australia/Yancowinna",
-                                      "Brazil/Acre",
-                                      "Brazil/DeNoronha",
-                                      "Brazil/East",
-                                      "Brazil/West",
-                                      "CET",
-                                      "CST6CDT",
-                                      "Canada/Atlantic",
-                                      "Canada/Central",
-                                      "Canada/Eastern",
-                                      "Canada/Mountain",
-                                      "Canada/Newfoundland",
-                                      "Canada/Pacific",
-                                      "Canada/Saskatchewan",
-                                      "Canada/Yukon",
-                                      "Chile/Continental",
-                                      "Chile/EasterIsland",
-                                      "Cuba",
-                                      "EET",
-                                      "EST",
-                                      "EST5EDT",
-                                      "Egypt",
-                                      "Eire",
                                       "Etc/GMT",
                                       "Etc/GMT+0",
                                       "Etc/GMT+1",
@@ -488,7 +432,6 @@
                                       "Europe/Istanbul",
                                       "Europe/Jersey",
                                       "Europe/Kaliningrad",
-                                      "Europe/Kiev",
                                       "Europe/Kirov",
                                       "Europe/Kyiv",
                                       "Europe/Lisbon",
@@ -520,7 +463,6 @@
                                       "Europe/Tirane",
                                       "Europe/Tiraspol",
                                       "Europe/Ulyanovsk",
-                                      "Europe/Uzhgorod",
                                       "Europe/Vaduz",
                                       "Europe/Vatican",
                                       "Europe/Vienna",
@@ -528,19 +470,8 @@
                                       "Europe/Volgograd",
                                       "Europe/Warsaw",
                                       "Europe/Zagreb",
-                                      "Europe/Zaporozhye",
                                       "Europe/Zurich",
                                       "Factory",
-                                      "GB",
-                                      "GB-Eire",
-                                      "GMT",
-                                      "GMT+0",
-                                      "GMT-0",
-                                      "GMT0",
-                                      "Greenwich",
-                                      "HST",
-                                      "Hongkong",
-                                      "Iceland",
                                       "Indian/Antananarivo",
                                       "Indian/Chagos",
                                       "Indian/Christmas",
@@ -552,23 +483,6 @@
                                       "Indian/Mauritius",
                                       "Indian/Mayotte",
                                       "Indian/Reunion",
-                                      "Iran",
-                                      "Israel",
-                                      "Jamaica",
-                                      "Japan",
-                                      "Kwajalein",
-                                      "Libya",
-                                      "MET",
-                                      "MST",
-                                      "MST7MDT",
-                                      "Mexico/BajaNorte",
-                                      "Mexico/BajaSur",
-                                      "Mexico/General",
-                                      "NZ",
-                                      "NZ-CHAT",
-                                      "Navajo",
-                                      "PRC",
-                                      "PST8PDT",
                                       "Pacific/Apia",
                                       "Pacific/Auckland",
                                       "Pacific/Bougainville",
@@ -576,7 +490,6 @@
                                       "Pacific/Chuuk",
                                       "Pacific/Easter",
                                       "Pacific/Efate",
-                                      "Pacific/Enderbury",
                                       "Pacific/Fakaofo",
                                       "Pacific/Fiji",
                                       "Pacific/Funafuti",
@@ -601,7 +514,6 @@
                                       "Pacific/Palau",
                                       "Pacific/Pitcairn",
                                       "Pacific/Pohnpei",
-                                      "Pacific/Ponape",
                                       "Pacific/Port_Moresby",
                                       "Pacific/Rarotonga",
                                       "Pacific/Saipan",
@@ -609,34 +521,10 @@
                                       "Pacific/Tahiti",
                                       "Pacific/Tarawa",
                                       "Pacific/Tongatapu",
-                                      "Pacific/Truk",
                                       "Pacific/Wake",
                                       "Pacific/Wallis",
                                       "Pacific/Yap",
-                                      "Poland",
-                                      "Portugal",
-                                      "ROC",
-                                      "ROK",
-                                      "Singapore",
-                                      "Turkey",
-                                      "UCT",
-                                      "US/Alaska",
-                                      "US/Aleutian",
-                                      "US/Arizona",
-                                      "US/Central",
-                                      "US/East-Indiana",
-                                      "US/Eastern",
-                                      "US/Hawaii",
-                                      "US/Indiana-Starke",
-                                      "US/Michigan",
-                                      "US/Mountain",
-                                      "US/Pacific",
-                                      "US/Samoa",
                                       "UTC",
-                                      "Universal",
-                                      "W-SU",
-                                      "WET",
-                                      "Zulu",
                                       nullptr};
 
 // Helper to return a loaded time zone by value (UTC on error).
diff --git a/absl/time/internal/cctz/src/tzfile.h b/absl/time/internal/cctz/src/tzfile.h
index 9613055..114026d 100644
--- a/absl/time/internal/cctz/src/tzfile.h
+++ b/absl/time/internal/cctz/src/tzfile.h
@@ -21,14 +21,6 @@
 ** Information about time zone files.
 */
 
-#ifndef TZDIR
-#define TZDIR "/usr/share/zoneinfo" /* Time zone object file directory */
-#endif                              /* !defined TZDIR */
-
-#ifndef TZDEFAULT
-#define TZDEFAULT "/etc/localtime"
-#endif /* !defined TZDEFAULT */
-
 #ifndef TZDEFRULES
 #define TZDEFRULES "posixrules"
 #endif /* !defined TZDEFRULES */
diff --git a/absl/time/internal/cctz/testdata/README.zoneinfo b/absl/time/internal/cctz/testdata/README.zoneinfo
index 67e9c40..0fe5585 100644
--- a/absl/time/internal/cctz/testdata/README.zoneinfo
+++ b/absl/time/internal/cctz/testdata/README.zoneinfo
@@ -22,7 +22,7 @@
                 LOCALTIME=Factory \
                 TZDATA_TEXT= \
                 PACKRATDATA=backzone PACKRATLIST=zone.tab \
-                ZONETABLES=zone1970.tab
+                ZONETABLES=zone1970.tab\ zonenow.tab
     tar --create --dereference --hard-dereference --file tzfile.tar \
         --directory=tz tzfile.h
     tar --create --dereference --hard-dereference --file zoneinfo.tar \
diff --git a/absl/time/internal/cctz/testdata/version b/absl/time/internal/cctz/testdata/version
index 7daa77e..cd9c3f6 100644
--- a/absl/time/internal/cctz/testdata/version
+++ b/absl/time/internal/cctz/testdata/version
@@ -1 +1 @@
-2023c
+2023d
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Ensenada b/absl/time/internal/cctz/testdata/zoneinfo/America/Ensenada
index e8be26b..42087af 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Ensenada
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Ensenada
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Godthab b/absl/time/internal/cctz/testdata/zoneinfo/America/Godthab
index 00b57bb..310774e 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Godthab
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Godthab
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Goose_Bay b/absl/time/internal/cctz/testdata/zoneinfo/America/Goose_Bay
index 820e0dd..e2cc3ee 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Goose_Bay
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Goose_Bay
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Winamac b/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Winamac
index 8700ed9..679d321 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Winamac
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Winamac
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Matamoros b/absl/time/internal/cctz/testdata/zoneinfo/America/Matamoros
index 88cabcd..993ac47 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Matamoros
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Matamoros
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Metlakatla b/absl/time/internal/cctz/testdata/zoneinfo/America/Metlakatla
index 9fefee3..71b0eab 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Metlakatla
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Metlakatla
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Moncton b/absl/time/internal/cctz/testdata/zoneinfo/America/Moncton
index ecb69ef..020e33d 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Moncton
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Moncton
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Nuuk b/absl/time/internal/cctz/testdata/zoneinfo/America/Nuuk
index 00b57bb..310774e 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Nuuk
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Nuuk
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Ojinaga b/absl/time/internal/cctz/testdata/zoneinfo/America/Ojinaga
index 2fc74e9..f7e40c0 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Ojinaga
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Ojinaga
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Santa_Isabel b/absl/time/internal/cctz/testdata/zoneinfo/America/Santa_Isabel
index e8be26b..42087af 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Santa_Isabel
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Santa_Isabel
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Scoresbysund b/absl/time/internal/cctz/testdata/zoneinfo/America/Scoresbysund
index 6db4912..fc1b11c 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Scoresbysund
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Scoresbysund
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/St_Johns b/absl/time/internal/cctz/testdata/zoneinfo/America/St_Johns
index e5f2aec..94d790b 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/St_Johns
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/St_Johns
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Tijuana b/absl/time/internal/cctz/testdata/zoneinfo/America/Tijuana
index e8be26b..42087af 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Tijuana
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Tijuana
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Casey b/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Casey
index 30315cc..84f1c61 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Casey
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Casey
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Macquarie b/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Macquarie
index 3fc1f23..99a8e60 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Macquarie
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Macquarie
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Troll b/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Troll
index 4e31aff..2359c44 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Troll
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Troll
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Vostok b/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Vostok
index 6e32907..4ce8f74 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Vostok
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Vostok
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Gaza b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Gaza
index 7e83389..6241b4e 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Gaza
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Gaza
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hebron b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hebron
index fcf923b..5267de9 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hebron
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hebron
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Nicosia b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Nicosia
index c210d0a..390347f 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Nicosia
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Nicosia
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Canada/Newfoundland b/absl/time/internal/cctz/testdata/zoneinfo/Canada/Newfoundland
index e5f2aec..94d790b 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Canada/Newfoundland
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Canada/Newfoundland
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Belfast b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Belfast
index 323cd38..b9e95d9 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Belfast
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Belfast
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Bucharest b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Bucharest
index efa689b..c4a391e 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Bucharest
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Bucharest
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Chisinau b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Chisinau
index 6970b14..9152e68 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Chisinau
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Chisinau
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Guernsey b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Guernsey
index d40bcaa..f2d87c6 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Guernsey
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Guernsey
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Isle_of_Man b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Isle_of_Man
index b0a37e7..9f5aaff 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Isle_of_Man
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Isle_of_Man
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Jersey b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Jersey
index 9a10a2e..c83814d 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Jersey
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Jersey
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kiev b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kiev
index 4e02685..753a6c8 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kiev
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kiev
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kyiv b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kyiv
index 4e02685..753a6c8 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kyiv
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kyiv
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/London b/absl/time/internal/cctz/testdata/zoneinfo/Europe/London
index 323cd38..b9e95d9 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/London
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/London
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Nicosia b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Nicosia
index c210d0a..390347f 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Nicosia
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Nicosia
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Riga b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Riga
index 26af4c9..d99170b 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Riga
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Riga
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Sofia b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Sofia
index eabc972..8945068 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Sofia
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Sofia
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Tallinn b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Tallinn
index 5321bbd..fbebdc6 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Tallinn
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Tallinn
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Tiraspol b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Tiraspol
index 6970b14..9152e68 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Tiraspol
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Tiraspol
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Uzhgorod b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Uzhgorod
index 4e02685..753a6c8 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Uzhgorod
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Uzhgorod
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vilnius b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vilnius
index 75b2eeb..43c3d7f 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vilnius
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vilnius
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Zaporozhye b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Zaporozhye
index 4e02685..753a6c8 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Zaporozhye
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Zaporozhye
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/GB b/absl/time/internal/cctz/testdata/zoneinfo/GB
index 323cd38..b9e95d9 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/GB
+++ b/absl/time/internal/cctz/testdata/zoneinfo/GB
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/GB-Eire b/absl/time/internal/cctz/testdata/zoneinfo/GB-Eire
index 323cd38..b9e95d9 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/GB-Eire
+++ b/absl/time/internal/cctz/testdata/zoneinfo/GB-Eire
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Mexico/BajaNorte b/absl/time/internal/cctz/testdata/zoneinfo/Mexico/BajaNorte
index e8be26b..42087af 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Mexico/BajaNorte
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Mexico/BajaNorte
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Norfolk b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Norfolk
index 79e2a94..0c0bdbd 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Norfolk
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Norfolk
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/iso3166.tab b/absl/time/internal/cctz/testdata/zoneinfo/iso3166.tab
index be3348d..402c015 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/iso3166.tab
+++ b/absl/time/internal/cctz/testdata/zoneinfo/iso3166.tab
@@ -3,17 +3,22 @@
 # This file is in the public domain, so clarified as of
 # 2009-05-17 by Arthur David Olson.
 #
-# From Paul Eggert (2022-11-18):
+# From Paul Eggert (2023-09-06):
 # This file contains a table of two-letter country codes.  Columns are
 # separated by a single tab.  Lines beginning with '#' are comments.
 # All text uses UTF-8 encoding.  The columns of the table are as follows:
 #
 # 1.  ISO 3166-1 alpha-2 country code, current as of
-#     ISO 3166-1 N1087 (2022-09-02).  See: Updates on ISO 3166-1
-#     https://isotc.iso.org/livelink/livelink/Open/16944257
-# 2.  The usual English name for the coded region,
-#     chosen so that alphabetic sorting of subsets produces helpful lists.
-#     This is not the same as the English name in the ISO 3166 tables.
+#     ISO/TC 46 N1108 (2023-04-05).  See: ISO/TC 46 Documents
+#     https://www.iso.org/committee/48750.html?view=documents
+# 2.  The usual English name for the coded region.  This sometimes
+#     departs from ISO-listed names, sometimes so that sorted subsets
+#     of names are useful (e.g., "Samoa (American)" and "Samoa
+#     (western)" rather than "American Samoa" and "Samoa"),
+#     sometimes to avoid confusion among non-experts (e.g.,
+#     "Czech Republic" and "Turkey" rather than "Czechia" and "Türkiye"),
+#     and sometimes to omit needless detail or churn (e.g., "Netherlands"
+#     rather than "Netherlands (the)" or "Netherlands (Kingdom of the)").
 #
 # The table is sorted by country code.
 #
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/zone1970.tab b/absl/time/internal/cctz/testdata/zoneinfo/zone1970.tab
index 1f1cecb..abd9489 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/zone1970.tab
+++ b/absl/time/internal/cctz/testdata/zoneinfo/zone1970.tab
@@ -37,7 +37,7 @@
 #country-
 #codes	coordinates	TZ	comments
 AD	+4230+00131	Europe/Andorra
-AE,OM,RE,SC,TF	+2518+05518	Asia/Dubai	Crozet, Scattered Is
+AE,OM,RE,SC,TF	+2518+05518	Asia/Dubai	Crozet
 AF	+3431+06912	Asia/Kabul
 AL	+4120+01950	Europe/Tirane
 AM	+4011+04430	Asia/Yerevan
@@ -47,12 +47,13 @@
 AQ	-6448-06406	Antarctica/Palmer	Palmer
 AQ	-6734-06808	Antarctica/Rothera	Rothera
 AQ	-720041+0023206	Antarctica/Troll	Troll
+AQ	-7824+10654	Antarctica/Vostok	Vostok
 AR	-3436-05827	America/Argentina/Buenos_Aires	Buenos Aires (BA, CF)
 AR	-3124-06411	America/Argentina/Cordoba	most areas: CB, CC, CN, ER, FM, MN, SE, SF
 AR	-2447-06525	America/Argentina/Salta	Salta (SA, LP, NQ, RN)
 AR	-2411-06518	America/Argentina/Jujuy	Jujuy (JY)
 AR	-2649-06513	America/Argentina/Tucuman	Tucumán (TM)
-AR	-2828-06547	America/Argentina/Catamarca	Catamarca (CT); Chubut (CH)
+AR	-2828-06547	America/Argentina/Catamarca	Catamarca (CT), Chubut (CH)
 AR	-2926-06651	America/Argentina/La_Rioja	La Rioja (LR)
 AR	-3132-06831	America/Argentina/San_Juan	San Juan (SJ)
 AR	-3253-06849	America/Argentina/Mendoza	Mendoza (MZ)
@@ -81,7 +82,7 @@
 BM	+3217-06446	Atlantic/Bermuda
 BO	-1630-06809	America/La_Paz
 BR	-0351-03225	America/Noronha	Atlantic islands
-BR	-0127-04829	America/Belem	Pará (east); Amapá
+BR	-0127-04829	America/Belem	Pará (east), Amapá
 BR	-0343-03830	America/Fortaleza	Brazil (northeast: MA, PI, CE, RN, PB)
 BR	-0803-03454	America/Recife	Pernambuco
 BR	-0712-04812	America/Araguaina	Tocantins
@@ -99,19 +100,19 @@
 BT	+2728+08939	Asia/Thimphu
 BY	+5354+02734	Europe/Minsk
 BZ	+1730-08812	America/Belize
-CA	+4734-05243	America/St_Johns	Newfoundland; Labrador (southeast)
-CA	+4439-06336	America/Halifax	Atlantic - NS (most areas); PE
+CA	+4734-05243	America/St_Johns	Newfoundland, Labrador (SE)
+CA	+4439-06336	America/Halifax	Atlantic - NS (most areas), PE
 CA	+4612-05957	America/Glace_Bay	Atlantic - NS (Cape Breton)
 CA	+4606-06447	America/Moncton	Atlantic - New Brunswick
 CA	+5320-06025	America/Goose_Bay	Atlantic - Labrador (most areas)
-CA,BS	+4339-07923	America/Toronto	Eastern - ON, QC (most areas)
+CA,BS	+4339-07923	America/Toronto	Eastern - ON & QC (most areas)
 CA	+6344-06828	America/Iqaluit	Eastern - NU (most areas)
-CA	+4953-09709	America/Winnipeg	Central - ON (west); Manitoba
+CA	+4953-09709	America/Winnipeg	Central - ON (west), Manitoba
 CA	+744144-0944945	America/Resolute	Central - NU (Resolute)
 CA	+624900-0920459	America/Rankin_Inlet	Central - NU (central)
 CA	+5024-10439	America/Regina	CST - SK (most areas)
 CA	+5017-10750	America/Swift_Current	CST - SK (midwest)
-CA	+5333-11328	America/Edmonton	Mountain - AB; BC (E); NT (E); SK (W)
+CA	+5333-11328	America/Edmonton	Mountain - AB, BC(E), NT(E), SK(W)
 CA	+690650-1050310	America/Cambridge_Bay	Mountain - NU (west)
 CA	+682059-1334300	America/Inuvik	Mountain - NT (west)
 CA	+5546-12014	America/Dawson_Creek	MST - BC (Dawson Cr, Ft St John)
@@ -126,7 +127,7 @@
 CL	-5309-07055	America/Punta_Arenas	Region of Magallanes
 CL	-2709-10926	Pacific/Easter	Easter Island
 CN	+3114+12128	Asia/Shanghai	Beijing Time
-CN,AQ	+4348+08735	Asia/Urumqi	Xinjiang Time, Vostok
+CN	+4348+08735	Asia/Urumqi	Xinjiang Time
 CO	+0436-07405	America/Bogota
 CR	+0956-08405	America/Costa_Rica
 CU	+2308-08222	America/Havana
@@ -171,8 +172,8 @@
 HU	+4730+01905	Europe/Budapest
 ID	-0610+10648	Asia/Jakarta	Java, Sumatra
 ID	-0002+10920	Asia/Pontianak	Borneo (west, central)
-ID	-0507+11924	Asia/Makassar	Borneo (east, south); Sulawesi/Celebes, Bali, Nusa Tengarra; Timor (west)
-ID	-0232+14042	Asia/Jayapura	New Guinea (West Papua / Irian Jaya); Malukus/Moluccas
+ID	-0507+11924	Asia/Makassar	Borneo (east, south), Sulawesi/Celebes, Bali, Nusa Tengarra, Timor (west)
+ID	-0232+14042	Asia/Jayapura	New Guinea (West Papua / Irian Jaya), Malukus/Moluccas
 IE	+5320-00615	Europe/Dublin
 IL	+314650+0351326	Asia/Jerusalem
 IN	+2232+08822	Asia/Kolkata
@@ -251,7 +252,7 @@
 PL	+5215+02100	Europe/Warsaw
 PM	+4703-05620	America/Miquelon
 PN	-2504-13005	Pacific/Pitcairn
-PR,AG,CA,AI,AW,BL,BQ,CW,DM,GD,GP,KN,LC,MF,MS,SX,TT,VC,VG,VI	+182806-0660622	America/Puerto_Rico	AST
+PR,AG,CA,AI,AW,BL,BQ,CW,DM,GD,GP,KN,LC,MF,MS,SX,TT,VC,VG,VI	+182806-0660622	America/Puerto_Rico	AST - QC (Lower North Shore)
 PS	+3130+03428	Asia/Gaza	Gaza Strip
 PS	+313200+0350542	Asia/Hebron	West Bank
 PT	+3843-00908	Europe/Lisbon	Portugal (mainland)
@@ -287,7 +288,7 @@
 RU	+643337+1431336	Asia/Ust-Nera	MSK+07 - Oymyakonsky
 RU	+5934+15048	Asia/Magadan	MSK+08 - Magadan
 RU	+4658+14242	Asia/Sakhalin	MSK+08 - Sakhalin Island
-RU	+6728+15343	Asia/Srednekolymsk	MSK+08 - Sakha (E); N Kuril Is
+RU	+6728+15343	Asia/Srednekolymsk	MSK+08 - Sakha (E), N Kuril Is
 RU	+5301+15839	Asia/Kamchatka	MSK+09 - Kamchatka
 RU	+6445+17729	Asia/Anadyr	MSK+09 - Bering Sea
 SA,AQ,KW,YE	+2438+04643	Asia/Riyadh	Syowa
@@ -329,7 +330,7 @@
 US	+465042-1012439	America/North_Dakota/New_Salem	Central - ND (Morton rural)
 US	+471551-1014640	America/North_Dakota/Beulah	Central - ND (Mercer)
 US	+394421-1045903	America/Denver	Mountain (most areas)
-US	+433649-1161209	America/Boise	Mountain - ID (south); OR (east)
+US	+433649-1161209	America/Boise	Mountain - ID (south), OR (east)
 US,CA	+332654-1120424	America/Phoenix	MST - AZ (most areas), Creston BC
 US	+340308-1181434	America/Los_Angeles	Pacific
 US	+611305-1495401	America/Anchorage	Alaska (most areas)
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/zonenow.tab b/absl/time/internal/cctz/testdata/zoneinfo/zonenow.tab
new file mode 100644
index 0000000..2dbe8f0
--- /dev/null
+++ b/absl/time/internal/cctz/testdata/zoneinfo/zonenow.tab
@@ -0,0 +1,301 @@
+# tzdb timezone descriptions, for users who do not care about old timestamps
+#
+# This file is in the public domain.
+#
+# From Paul Eggert (2023-12-18):
+# This file contains a table where each row stands for a timezone
+# where civil timestamps are predicted to agree from now on.
+# This file is like zone1970.tab (see zone1970.tab's coments),
+# but with the following changes:
+#
+# 1.  Each timezone corresponds to a set of clocks that are planned
+#     to agree from now on.  This is a larger set of clocks than in
+#     zone1970.tab, where each timezone's clocks must agree from 1970 on.
+# 2.  The first column is irrelevant and ignored.
+# 3.  The table is sorted in a different way:
+#     first by standard time UTC offset;
+#     then, if DST is used, by daylight saving UTC offset;
+#     then by time zone abbreviation.
+# 4.  Every timezone has a nonempty comments column, with wording
+#     distinguishing the timezone only from other timezones with the
+#     same UTC offset at some point during the year.
+#
+# The format of this table is experimental, and may change in future versions.
+#
+# This table is intended as an aid for users, to help them select timezones
+# appropriate for their practical needs.  It is not intended to take or
+# endorse any position on legal or territorial claims.
+#
+#XX	coordinates	TZ	comments
+#
+# -11 - SST
+XX	-1416-17042	Pacific/Pago_Pago	Midway; Samoa ("SST")
+#
+# -11
+XX	-1901-16955	Pacific/Niue	Niue
+#
+# -10 - HST
+XX	+211825-1575130	Pacific/Honolulu	Hawaii ("HST")
+#
+# -10
+XX	-1732-14934	Pacific/Tahiti	Tahiti; Cook Islands
+#
+# -10/-09 - HST / HDT (North America DST)
+XX	+515248-1763929	America/Adak	western Aleutians in Alaska ("HST/HDT")
+#
+# -09:30
+XX	-0900-13930	Pacific/Marquesas	Marquesas
+#
+# -09
+XX	-2308-13457	Pacific/Gambier	Gambier
+#
+# -09/-08 - AKST/AKDT (North America DST)
+XX	+611305-1495401	America/Anchorage	most of Alaska ("AKST/AKDT")
+#
+# -08
+XX	-2504-13005	Pacific/Pitcairn	Pitcairn
+#
+# -08/-07 - PST/PDT (North America DST)
+XX	+340308-1181434	America/Los_Angeles	Pacific ("PST/PDT") - US & Canada; Mexico near US border
+#
+# -07 - MST
+XX	+332654-1120424	America/Phoenix	Mountain Standard ("MST") - Arizona; western Mexico; Yukon
+#
+# -07/-06 - MST/MDT (North America DST)
+XX	+394421-1045903	America/Denver	Mountain ("MST/MDT") - US & Canada; Mexico near US border
+#
+# -06
+XX	-0054-08936	Pacific/Galapagos	Galápagos
+#
+# -06 - CST
+XX	+1924-09909	America/Mexico_City	Central Standard ("CST") - Saskatchewan; central Mexico; Central America
+#
+# -06/-05 (Chile DST)
+XX	-2709-10926	Pacific/Easter	Easter Island
+#
+# -06/-05 - CST/CDT (North America DST)
+XX	+415100-0873900	America/Chicago	Central ("CST/CDT") - US & Canada; Mexico near US border
+#
+# -05
+XX	-1203-07703	America/Lima	eastern South America
+#
+# -05 - EST
+XX	+175805-0764736	America/Jamaica	Eastern Standard ("EST") - Caymans; Jamaica; eastern Mexico; Panama
+#
+# -05/-04 - CST/CDT (Cuba DST)
+XX	+2308-08222	America/Havana	Cuba
+#
+# -05/-04 - EST/EDT (North America DST)
+XX	+404251-0740023	America/New_York	Eastern ("EST/EDT") - US & Canada
+#
+# -04
+XX	+1030-06656	America/Caracas	western South America
+#
+# -04 - AST
+XX	+1828-06954	America/Santo_Domingo	Atlantic Standard ("AST") - eastern Caribbean
+#
+# -04/-03 (Chile DST)
+XX	-3327-07040	America/Santiago	most of Chile
+#
+# -04/-03 (Paraguay DST)
+XX	-2516-05740	America/Asuncion	Paraguay
+#
+# -04/-03 - AST/ADT (North America DST)
+XX	+4439-06336	America/Halifax	Atlantic ("AST/ADT") - Canada; Bermuda
+#
+# -03:30/-02:30 - NST/NDT (North America DST)
+XX	+4734-05243	America/St_Johns	Newfoundland ("NST/NDT")
+#
+# -03
+XX	-2332-04637	America/Sao_Paulo	eastern South America
+#
+# -03/-02 (North America DST)
+XX	+4703-05620	America/Miquelon	St Pierre & Miquelon
+#
+# -02
+XX	-0351-03225	America/Noronha	Fernando de Noronha; South Georgia
+#
+# -02/-01 (EU DST)
+XX	+6411-05144	America/Nuuk	most of Greenland
+#
+# -01
+XX	+1455-02331	Atlantic/Cape_Verde	Cape Verde
+#
+# -01/+00 (EU DST)
+XX	+3744-02540	Atlantic/Azores	Azores
+# -01/+00 (EU DST) until 2024-03-31; then -02/-01 (EU DST)
+XX	+7029-02158	America/Scoresbysund	Ittoqqortoormiit
+#
+# +00 - GMT
+XX	+0519-00402	Africa/Abidjan	far western Africa; Iceland ("GMT")
+#
+# +00/+01 - GMT/BST (EU DST)
+XX	+513030-0000731	Europe/London	United Kingdom ("GMT/BST")
+#
+# +00/+01 - WET/WEST (EU DST)
+XX	+3843-00908	Europe/Lisbon	western Europe ("WET/WEST")
+#
+# +00/+02 - Troll DST
+XX	-720041+0023206	Antarctica/Troll	Troll Station in Antarctica
+#
+# +01 - CET
+XX	+3647+00303	Africa/Algiers	Algeria, Tunisia ("CET")
+#
+# +01 - WAT
+XX	+0627+00324	Africa/Lagos	western Africa ("WAT")
+#
+# +01/+00 - IST/GMT (EU DST in reverse)
+XX	+5320-00615	Europe/Dublin	Ireland ("IST/GMT")
+#
+# +01/+00 - (Morocco DST)
+XX	+3339-00735	Africa/Casablanca	Morocco
+#
+# +01/+02 - CET/CEST (EU DST)
+XX	+4852+00220	Europe/Paris	central Europe ("CET/CEST")
+#
+# +02 - CAT
+XX	-2558+03235	Africa/Maputo	central Africa ("CAT")
+#
+# +02 - EET
+XX	+3254+01311	Africa/Tripoli	Libya; Kaliningrad ("EET")
+#
+# +02 - SAST
+XX	-2615+02800	Africa/Johannesburg	southern Africa ("SAST")
+#
+# +02/+03 - EET/EEST (EU DST)
+XX	+3758+02343	Europe/Athens	eastern Europe ("EET/EEST")
+#
+# +02/+03 - EET/EEST (Egypt DST)
+XX	+3003+03115	Africa/Cairo	Egypt
+#
+# +02/+03 - EET/EEST (Lebanon DST)
+XX	+3353+03530	Asia/Beirut	Lebanon
+#
+# +02/+03 - EET/EEST (Moldova DST)
+XX	+4700+02850	Europe/Chisinau	Moldova
+#
+# +02/+03 - EET/EEST (Palestine DST)
+XX	+3130+03428	Asia/Gaza	Palestine
+#
+# +02/+03 - IST/IDT (Israel DST)
+XX	+314650+0351326	Asia/Jerusalem	Israel
+#
+# +03
+XX	+4101+02858	Europe/Istanbul	Near East; Belarus
+#
+# +03 - EAT
+XX	-0117+03649	Africa/Nairobi	eastern Africa ("EAT")
+#
+# +03 - MSK
+XX	+554521+0373704	Europe/Moscow	Moscow ("MSK")
+#
+# +03:30
+XX	+3540+05126	Asia/Tehran	Iran
+#
+# +04
+XX	+2518+05518	Asia/Dubai	Russia; Caucasus; Persian Gulf; Seychelles; Réunion
+#
+# +04:30
+XX	+3431+06912	Asia/Kabul	Afghanistan
+#
+# +05
+XX	+4120+06918	Asia/Tashkent	Russia; Tajikistan; Turkmenistan; Uzbekistan; Maldives
+#
+# +05 - PKT
+XX	+2452+06703	Asia/Karachi	Pakistan ("PKT")
+#
+# +05:30
+XX	+0656+07951	Asia/Colombo	Sri Lanka
+#
+# +05:30 - IST
+XX	+2232+08822	Asia/Kolkata	India ("IST")
+#
+# +05:45
+XX	+2743+08519	Asia/Kathmandu	Nepal
+#
+# +06
+XX	+2343+09025	Asia/Dhaka	Russia; Kyrgyzstan; Bhutan; Bangladesh; Chagos
+#
+# +06:30
+XX	+1647+09610	Asia/Yangon	Myanmar; Cocos
+#
+# +07
+XX	+1345+10031	Asia/Bangkok	Russia; Indochina; Christmas Island
+#
+# +07 - WIB
+XX	-0610+10648	Asia/Jakarta	Indonesia ("WIB")
+#
+# +08
+XX	+0117+10351	Asia/Singapore	Russia; Brunei; Malaysia; Singapore
+#
+# +08 - AWST
+XX	-3157+11551	Australia/Perth	Western Australia ("AWST")
+#
+# +08 - CST
+XX	+3114+12128	Asia/Shanghai	China ("CST")
+#
+# +08 - HKT
+XX	+2217+11409	Asia/Hong_Kong	Hong Kong ("HKT")
+#
+# +08 - PHT
+XX	+1435+12100	Asia/Manila	Philippines ("PHT")
+#
+# +08 - WITA
+XX	-0507+11924	Asia/Makassar	Indonesia ("WITA")
+#
+# +08:45
+XX	-3143+12852	Australia/Eucla	Eucla
+#
+# +09
+XX	+5203+11328	Asia/Chita	Russia; Palau; East Timor
+#
+# +09 - JST
+XX	+353916+1394441	Asia/Tokyo	Japan ("JST")
+#
+# +09 - KST
+XX	+3733+12658	Asia/Seoul	Korea ("KST")
+#
+# +09 - WIT
+XX	-0232+14042	Asia/Jayapura	Indonesia ("WIT")
+#
+# +09:30 - ACST
+XX	-1228+13050	Australia/Darwin	Northern Territory ("ACST")
+#
+# +09:30/+10:30 - ACST/ACDT (Australia DST)
+XX	-3455+13835	Australia/Adelaide	South Australia ("ACST/ACDT")
+#
+# +10
+XX	+4310+13156	Asia/Vladivostok	Russia; Yap; Chuuk; Papua New Guinea; Dumont d'Urville
+#
+# +10 - AEST
+XX	-2728+15302	Australia/Brisbane	Queensland ("AEST")
+#
+# +10 - ChST
+XX	+1328+14445	Pacific/Guam	Mariana Islands ("ChST")
+#
+# +10/+11 - AEST/AEDT (Australia DST)
+XX	-3352+15113	Australia/Sydney	southeast Australia ("AEST/AEDT")
+#
+# +10:30/+11
+XX	-3133+15905	Australia/Lord_Howe	Lord Howe Island
+#
+# +11
+XX	-0613+15534	Pacific/Bougainville	Russia; Kosrae; Bougainville; Solomons
+#
+# +11/+12 (Australia DST)
+XX	-2903+16758	Pacific/Norfolk	Norfolk Island
+#
+# +12
+XX	+5301+15839	Asia/Kamchatka	Russia; Tuvalu; Fiji; etc.
+#
+# +12/+13 (New Zealand DST)
+XX	-3652+17446	Pacific/Auckland	New Zealand ("NZST/NZDT")
+#
+# +12:45/+13:45 (Chatham DST)
+XX	-4357-17633	Pacific/Chatham	Chatham Islands
+#
+# +13
+XX	-210800-1751200	Pacific/Tongatapu	Kanton; Tokelau; Samoa (western); Tonga
+#
+# +14
+XX	+0152-15720	Pacific/Kiritimati	Kiritimati
diff --git a/absl/types/BUILD.bazel b/absl/types/BUILD.bazel
index b57d3b9..ce8f605 100644
--- a/absl/types/BUILD.bazel
+++ b/absl/types/BUILD.bazel
@@ -20,7 +20,14 @@
     "ABSL_TEST_COPTS",
 )
 
-package(default_visibility = ["//visibility:public"])
+package(
+    default_visibility = ["//visibility:public"],
+    features = [
+        "header_modules",
+        "layering_check",
+        "parse_headers",
+    ],
+)
 
 licenses(["notice"])
 
@@ -79,6 +86,7 @@
         "//absl/base:exception_testing",
         "//absl/container:test_instance_tracker",
         "//absl/log",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -92,6 +100,7 @@
         ":any",
         "//absl/base:config",
         "//absl/base:exception_safety_testing",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -109,6 +118,7 @@
     deps = [
         "//absl/algorithm",
         "//absl/base:core_headers",
+        "//absl/base:nullability",
         "//absl/base:throw_delegate",
         "//absl/meta:type_traits",
     ],
@@ -129,6 +139,7 @@
         "//absl/container:inlined_vector",
         "//absl/hash:hash_testing",
         "//absl/strings",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -144,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",
@@ -188,6 +200,7 @@
         "//absl/log",
         "//absl/meta:type_traits",
         "//absl/strings",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -203,44 +216,7 @@
         ":optional",
         "//absl/base:config",
         "//absl/base:exception_safety_testing",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_library(
-    name = "conformance_testing",
-    testonly = 1,
-    hdrs = [
-        "internal/conformance_aliases.h",
-        "internal/conformance_archetype.h",
-        "internal/conformance_profile.h",
-        "internal/conformance_testing.h",
-        "internal/conformance_testing_helpers.h",
-        "internal/parentheses.h",
-        "internal/transform_args.h",
-    ],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        "//absl/algorithm:container",
-        "//absl/meta:type_traits",
-        "//absl/strings",
-        "//absl/utility",
         "@com_google_googletest//:gtest",
-    ],
-)
-
-cc_test(
-    name = "conformance_testing_test",
-    size = "small",
-    srcs = [
-        "internal/conformance_testing_test.cc",
-    ],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":conformance_testing",
-        "//absl/meta:type_traits",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -274,6 +250,7 @@
         "//absl/memory",
         "//absl/meta:type_traits",
         "//absl/strings",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -290,6 +267,7 @@
         ":variant",
         "//absl/utility",
         "@com_github_google_benchmark//:benchmark_main",
+        "@com_google_googletest//:gtest",
     ],
 )
 
@@ -306,6 +284,7 @@
         "//absl/base:config",
         "//absl/base:exception_safety_testing",
         "//absl/memory",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -316,6 +295,7 @@
     copts = ABSL_DEFAULT_COPTS,
     linkopts = ABSL_DEFAULT_LINKOPTS,
     deps = [
+        "//absl/base:config",
         "//absl/base:core_headers",
         "//absl/meta:type_traits",
     ],
@@ -331,6 +311,7 @@
     deps = [
         ":compare",
         "//absl/base",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
diff --git a/absl/types/CMakeLists.txt b/absl/types/CMakeLists.txt
index c0dcee7..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
@@ -240,59 +242,6 @@
     GTest::gmock_main
 )
 
-# Internal-only target, do not depend on directly.
-absl_cc_library(
-  NAME
-    conformance_testing
-  HDRS
-    "internal/conformance_aliases.h"
-    "internal/conformance_archetype.h"
-    "internal/conformance_profile.h"
-    "internal/conformance_testing.h"
-    "internal/conformance_testing_helpers.h"
-    "internal/parentheses.h"
-    "internal/transform_args.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::algorithm
-    absl::debugging
-    absl::type_traits
-    absl::strings
-    absl::utility
-    GTest::gmock_main
-  TESTONLY
-)
-
-absl_cc_test(
-  NAME
-    conformance_testing_test
-  SRCS
-    "internal/conformance_testing_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-    ${ABSL_EXCEPTIONS_FLAG}
-  LINKOPTS
-    ${ABSL_EXCEPTIONS_FLAG_LINKOPTS}
-  DEPS
-    absl::conformance_testing
-    absl::type_traits
-    GTest::gmock_main
-)
-
-absl_cc_test(
-  NAME
-    conformance_testing_test_no_exceptions
-  SRCS
-    "internal/conformance_testing_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::conformance_testing
-    absl::type_traits
-    GTest::gmock_main
-)
-
 absl_cc_library(
   NAME
     variant
@@ -337,6 +286,7 @@
   COPTS
     ${ABSL_DEFAULT_COPTS}
   DEPS
+    absl::config
     absl::core_headers
     absl::type_traits
   PUBLIC
diff --git a/absl/types/bad_any_cast.cc b/absl/types/bad_any_cast.cc
index b0592cc..22558b4 100644
--- a/absl/types/bad_any_cast.cc
+++ b/absl/types/bad_any_cast.cc
@@ -43,4 +43,22 @@
 ABSL_NAMESPACE_END
 }  // namespace absl
 
+#else
+
+// https://github.com/abseil/abseil-cpp/issues/1465
+// CMake builds on Apple platforms error when libraries are empty.
+// Our CMake configuration can avoid this error on header-only libraries,
+// but since this library is conditionally empty, including a single
+// variable is an easy workaround.
+#ifdef __APPLE__
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace types_internal {
+extern const char kAvoidEmptyBadAnyCastLibraryWarning;
+const char kAvoidEmptyBadAnyCastLibraryWarning = 0;
+}  // namespace types_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
+#endif  // __APPLE__
+
 #endif  // ABSL_USES_STD_ANY
diff --git a/absl/types/bad_optional_access.cc b/absl/types/bad_optional_access.cc
index 26aca70..2552cc8 100644
--- a/absl/types/bad_optional_access.cc
+++ b/absl/types/bad_optional_access.cc
@@ -45,4 +45,22 @@
 ABSL_NAMESPACE_END
 }  // namespace absl
 
+#else
+
+// https://github.com/abseil/abseil-cpp/issues/1465
+// CMake builds on Apple platforms error when libraries are empty.
+// Our CMake configuration can avoid this error on header-only libraries,
+// but since this library is conditionally empty, including a single
+// variable is an easy workaround.
+#ifdef __APPLE__
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace types_internal {
+extern const char kAvoidEmptyBadOptionalAccessLibraryWarning;
+const char kAvoidEmptyBadOptionalAccessLibraryWarning = 0;
+}  // namespace types_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
+#endif  // __APPLE__
+
 #endif  // ABSL_USES_STD_OPTIONAL
diff --git a/absl/types/bad_variant_access.cc b/absl/types/bad_variant_access.cc
index 3dc88cc..a76aa80 100644
--- a/absl/types/bad_variant_access.cc
+++ b/absl/types/bad_variant_access.cc
@@ -61,4 +61,22 @@
 ABSL_NAMESPACE_END
 }  // namespace absl
 
+#else
+
+// https://github.com/abseil/abseil-cpp/issues/1465
+// CMake builds on Apple platforms error when libraries are empty.
+// Our CMake configuration can avoid this error on header-only libraries,
+// but since this library is conditionally empty, including a single
+// variable is an easy workaround.
+#ifdef __APPLE__
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace types_internal {
+extern const char kAvoidEmptyBadVariantAccessLibraryWarning;
+const char kAvoidEmptyBadVariantAccessLibraryWarning = 0;
+}  // namespace types_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
+#endif  // __APPLE__
+
 #endif  // ABSL_USES_STD_VARIANT
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/internal/conformance_aliases.h b/absl/types/internal/conformance_aliases.h
deleted file mode 100644
index 0cc6884..0000000
--- a/absl/types/internal/conformance_aliases.h
+++ /dev/null
@@ -1,447 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// regularity_aliases.h
-// -----------------------------------------------------------------------------
-//
-// This file contains type aliases of common ConformanceProfiles and Archetypes
-// so that they can be directly used by name without creating them from scratch.
-
-#ifndef ABSL_TYPES_INTERNAL_CONFORMANCE_ALIASES_H_
-#define ABSL_TYPES_INTERNAL_CONFORMANCE_ALIASES_H_
-
-#include "absl/types/internal/conformance_archetype.h"
-#include "absl/types/internal/conformance_profile.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace types_internal {
-
-// Creates both a Profile and a corresponding Archetype with root name "name".
-#define ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(name, ...)                \
-  struct name##Profile : __VA_ARGS__ {};                                    \
-                                                                            \
-  using name##Archetype = ::absl::types_internal::Archetype<name##Profile>; \
-                                                                            \
-  template <class AbslInternalProfileTag>                                   \
-  using name##Archetype##_ = ::absl::types_internal::Archetype<             \
-      ::absl::types_internal::StrongProfileTypedef<name##Profile,           \
-                                                   AbslInternalProfileTag>>
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HasTrivialDefaultConstructor,
-    ConformanceProfile<default_constructible::trivial>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HasNothrowDefaultConstructor,
-    ConformanceProfile<default_constructible::nothrow>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HasDefaultConstructor, ConformanceProfile<default_constructible::yes>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HasTrivialMoveConstructor, ConformanceProfile<default_constructible::maybe,
-                                                  move_constructible::trivial>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HasNothrowMoveConstructor, ConformanceProfile<default_constructible::maybe,
-                                                  move_constructible::nothrow>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HasMoveConstructor,
-    ConformanceProfile<default_constructible::maybe, move_constructible::yes>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HasTrivialCopyConstructor,
-    ConformanceProfile<default_constructible::maybe, move_constructible::maybe,
-                       copy_constructible::trivial>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HasNothrowCopyConstructor,
-    ConformanceProfile<default_constructible::maybe, move_constructible::maybe,
-                       copy_constructible::nothrow>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HasCopyConstructor,
-    ConformanceProfile<default_constructible::maybe, move_constructible::maybe,
-                       copy_constructible::yes>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HasTrivialMoveAssign,
-    ConformanceProfile<default_constructible::maybe, move_constructible::maybe,
-                       copy_constructible::maybe, move_assignable::trivial>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HasNothrowMoveAssign,
-    ConformanceProfile<default_constructible::maybe, move_constructible::maybe,
-                       copy_constructible::maybe, move_assignable::nothrow>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HasMoveAssign,
-    ConformanceProfile<default_constructible::maybe, move_constructible::maybe,
-                       copy_constructible::maybe, move_assignable::yes>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HasTrivialCopyAssign,
-    ConformanceProfile<default_constructible::maybe, move_constructible::maybe,
-                       copy_constructible::maybe, move_assignable::maybe,
-                       copy_assignable::trivial>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HasNothrowCopyAssign,
-    ConformanceProfile<default_constructible::maybe, move_constructible::maybe,
-                       copy_constructible::maybe, move_assignable::maybe,
-                       copy_assignable::nothrow>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HasCopyAssign,
-    ConformanceProfile<default_constructible::maybe, move_constructible::maybe,
-                       copy_constructible::maybe, move_assignable::maybe,
-                       copy_assignable::yes>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HasTrivialDestructor,
-    ConformanceProfile<default_constructible::maybe, move_constructible::maybe,
-                       copy_constructible::maybe, move_assignable::maybe,
-                       copy_assignable::maybe, destructible::trivial>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HasNothrowDestructor,
-    ConformanceProfile<default_constructible::maybe, move_constructible::maybe,
-                       copy_constructible::maybe, move_assignable::maybe,
-                       copy_assignable::maybe, destructible::nothrow>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HasDestructor,
-    ConformanceProfile<default_constructible::maybe, move_constructible::maybe,
-                       copy_constructible::maybe, move_assignable::maybe,
-                       copy_assignable::maybe, destructible::yes>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HasNothrowEquality,
-    ConformanceProfile<default_constructible::maybe, move_constructible::maybe,
-                       copy_constructible::maybe, move_assignable::maybe,
-                       copy_assignable::maybe, destructible::maybe,
-                       equality_comparable::nothrow>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HasEquality,
-    ConformanceProfile<default_constructible::maybe, move_constructible::maybe,
-                       copy_constructible::maybe, move_assignable::maybe,
-                       copy_assignable::maybe, destructible::maybe,
-                       equality_comparable::yes>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HasNothrowInequality,
-    ConformanceProfile<default_constructible::maybe, move_constructible::maybe,
-                       copy_constructible::maybe, move_assignable::maybe,
-                       copy_assignable::maybe, destructible::maybe,
-                       equality_comparable::maybe,
-                       inequality_comparable::nothrow>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HasInequality,
-    ConformanceProfile<default_constructible::maybe, move_constructible::maybe,
-                       copy_constructible::maybe, move_assignable::maybe,
-                       copy_assignable::maybe, destructible::maybe,
-                       equality_comparable::maybe, inequality_comparable::yes>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HasNothrowLessThan,
-    ConformanceProfile<default_constructible::maybe, move_constructible::maybe,
-                       copy_constructible::maybe, move_assignable::maybe,
-                       copy_assignable::maybe, destructible::maybe,
-                       equality_comparable::maybe, inequality_comparable::maybe,
-                       less_than_comparable::nothrow>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HasLessThan,
-    ConformanceProfile<default_constructible::maybe, move_constructible::maybe,
-                       copy_constructible::maybe, move_assignable::maybe,
-                       copy_assignable::maybe, destructible::maybe,
-                       equality_comparable::maybe, inequality_comparable::maybe,
-                       less_than_comparable::yes>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HasNothrowLessEqual,
-    ConformanceProfile<default_constructible::maybe, move_constructible::maybe,
-                       copy_constructible::maybe, move_assignable::maybe,
-                       copy_assignable::maybe, destructible::maybe,
-                       equality_comparable::maybe, inequality_comparable::maybe,
-                       less_than_comparable::maybe,
-                       less_equal_comparable::nothrow>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HasLessEqual,
-    ConformanceProfile<default_constructible::maybe, move_constructible::maybe,
-                       copy_constructible::maybe, move_assignable::maybe,
-                       copy_assignable::maybe, destructible::maybe,
-                       equality_comparable::maybe, inequality_comparable::maybe,
-                       less_than_comparable::maybe,
-                       less_equal_comparable::yes>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HasNothrowGreaterEqual,
-    ConformanceProfile<
-        default_constructible::maybe, move_constructible::maybe,
-        copy_constructible::maybe, move_assignable::maybe,
-        copy_assignable::maybe, destructible::maybe, equality_comparable::maybe,
-        inequality_comparable::maybe, less_than_comparable::maybe,
-        less_equal_comparable::maybe, greater_equal_comparable::nothrow>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HasGreaterEqual,
-    ConformanceProfile<
-        default_constructible::maybe, move_constructible::maybe,
-        copy_constructible::maybe, move_assignable::maybe,
-        copy_assignable::maybe, destructible::maybe, equality_comparable::maybe,
-        inequality_comparable::maybe, less_than_comparable::maybe,
-        less_equal_comparable::maybe, greater_equal_comparable::yes>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HasNothrowGreaterThan,
-    ConformanceProfile<
-        default_constructible::maybe, move_constructible::maybe,
-        copy_constructible::maybe, move_assignable::maybe,
-        copy_assignable::maybe, destructible::maybe, equality_comparable::maybe,
-        inequality_comparable::maybe, less_than_comparable::maybe,
-        less_equal_comparable::maybe, greater_equal_comparable::maybe,
-        greater_than_comparable::nothrow>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HasGreaterThan,
-    ConformanceProfile<
-        default_constructible::maybe, move_constructible::maybe,
-        copy_constructible::maybe, move_assignable::maybe,
-        copy_assignable::maybe, destructible::maybe, equality_comparable::maybe,
-        inequality_comparable::maybe, less_than_comparable::maybe,
-        less_equal_comparable::maybe, greater_equal_comparable::maybe,
-        greater_than_comparable::yes>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HasNothrowSwap,
-    ConformanceProfile<
-        default_constructible::maybe, move_constructible::maybe,
-        copy_constructible::maybe, move_assignable::maybe,
-        copy_assignable::maybe, destructible::maybe, equality_comparable::maybe,
-        inequality_comparable::maybe, less_than_comparable::maybe,
-        less_equal_comparable::maybe, greater_equal_comparable::maybe,
-        greater_than_comparable::maybe, swappable::nothrow>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HasSwap,
-    ConformanceProfile<
-        default_constructible::maybe, move_constructible::maybe,
-        copy_constructible::maybe, move_assignable::maybe,
-        copy_assignable::maybe, destructible::maybe, equality_comparable::maybe,
-        inequality_comparable::maybe, less_than_comparable::maybe,
-        less_equal_comparable::maybe, greater_equal_comparable::maybe,
-        greater_than_comparable::maybe, swappable::yes>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HasStdHashSpecialization,
-    ConformanceProfile<
-        default_constructible::maybe, move_constructible::maybe,
-        copy_constructible::maybe, move_assignable::maybe,
-        copy_assignable::maybe, destructible::maybe, equality_comparable::maybe,
-        inequality_comparable::maybe, less_than_comparable::maybe,
-        less_equal_comparable::maybe, greater_equal_comparable::maybe,
-        greater_than_comparable::maybe, swappable::maybe, hashable::yes>);
-
-////////////////////////////////////////////////////////////////////////////////
-////     The remaining aliases are combinations of the previous aliases.    ////
-////////////////////////////////////////////////////////////////////////////////
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    Equatable, CombineProfiles<HasEqualityProfile, HasInequalityProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    Comparable,
-    CombineProfiles<EquatableProfile, HasLessThanProfile, HasLessEqualProfile,
-                    HasGreaterEqualProfile, HasGreaterThanProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    NothrowEquatable,
-    CombineProfiles<HasNothrowEqualityProfile, HasNothrowInequalityProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    NothrowComparable,
-    CombineProfiles<NothrowEquatableProfile, HasNothrowLessThanProfile,
-                    HasNothrowLessEqualProfile, HasNothrowGreaterEqualProfile,
-                    HasNothrowGreaterThanProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    Value,
-    CombineProfiles<HasNothrowMoveConstructorProfile, HasCopyConstructorProfile,
-                    HasNothrowMoveAssignProfile, HasCopyAssignProfile,
-                    HasNothrowDestructorProfile, HasNothrowSwapProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    EquatableValue, CombineProfiles<EquatableProfile, ValueProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    ComparableValue, CombineProfiles<ComparableProfile, ValueProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    DefaultConstructibleValue,
-    CombineProfiles<HasDefaultConstructorProfile, ValueProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    NothrowMoveConstructible, CombineProfiles<HasNothrowMoveConstructorProfile,
-                                              HasNothrowDestructorProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    EquatableNothrowMoveConstructible,
-    CombineProfiles<EquatableProfile, NothrowMoveConstructibleProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    ComparableNothrowMoveConstructible,
-    CombineProfiles<ComparableProfile, NothrowMoveConstructibleProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    DefaultConstructibleNothrowMoveConstructible,
-    CombineProfiles<HasDefaultConstructorProfile,
-                    NothrowMoveConstructibleProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    CopyConstructible,
-    CombineProfiles<HasNothrowMoveConstructorProfile, HasCopyConstructorProfile,
-                    HasNothrowDestructorProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    EquatableCopyConstructible,
-    CombineProfiles<EquatableProfile, CopyConstructibleProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    ComparableCopyConstructible,
-    CombineProfiles<ComparableProfile, CopyConstructibleProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    DefaultConstructibleCopyConstructible,
-    CombineProfiles<HasDefaultConstructorProfile, CopyConstructibleProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    NothrowMovable,
-    CombineProfiles<HasNothrowMoveConstructorProfile,
-                    HasNothrowMoveAssignProfile, HasNothrowDestructorProfile,
-                    HasNothrowSwapProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    EquatableNothrowMovable,
-    CombineProfiles<EquatableProfile, NothrowMovableProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    ComparableNothrowMovable,
-    CombineProfiles<ComparableProfile, NothrowMovableProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    DefaultConstructibleNothrowMovable,
-    CombineProfiles<HasDefaultConstructorProfile, NothrowMovableProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    TrivialSpecialMemberFunctions,
-    CombineProfiles<HasTrivialDefaultConstructorProfile,
-                    HasTrivialMoveConstructorProfile,
-                    HasTrivialCopyConstructorProfile,
-                    HasTrivialMoveAssignProfile, HasTrivialCopyAssignProfile,
-                    HasTrivialDestructorProfile, HasNothrowSwapProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    TriviallyComplete,
-    CombineProfiles<TrivialSpecialMemberFunctionsProfile, ComparableProfile,
-                    HasStdHashSpecializationProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HashableNothrowMoveConstructible,
-    CombineProfiles<HasStdHashSpecializationProfile,
-                    NothrowMoveConstructibleProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HashableCopyConstructible,
-    CombineProfiles<HasStdHashSpecializationProfile, CopyConstructibleProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HashableNothrowMovable,
-    CombineProfiles<HasStdHashSpecializationProfile, NothrowMovableProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HashableValue,
-    CombineProfiles<HasStdHashSpecializationProfile, ValueProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    ComparableHashableValue,
-    CombineProfiles<HashableValueProfile, ComparableProfile>);
-
-// The "preferred" profiles that we support in Abseil.
-template <template <class...> class Receiver>
-using ExpandBasicProfiles =
-    Receiver<NothrowMoveConstructibleProfile, CopyConstructibleProfile,
-             NothrowMovableProfile, ValueProfile>;
-
-// The basic profiles except that they are also all Equatable.
-template <template <class...> class Receiver>
-using ExpandBasicEquatableProfiles =
-    Receiver<EquatableNothrowMoveConstructibleProfile,
-             EquatableCopyConstructibleProfile, EquatableNothrowMovableProfile,
-             EquatableValueProfile>;
-
-// The basic profiles except that they are also all Comparable.
-template <template <class...> class Receiver>
-using ExpandBasicComparableProfiles =
-    Receiver<ComparableNothrowMoveConstructibleProfile,
-             ComparableCopyConstructibleProfile,
-             ComparableNothrowMovableProfile, ComparableValueProfile>;
-
-// The basic profiles except that they are also all Hashable.
-template <template <class...> class Receiver>
-using ExpandBasicHashableProfiles =
-    Receiver<HashableNothrowMoveConstructibleProfile,
-             HashableCopyConstructibleProfile, HashableNothrowMovableProfile,
-             HashableValueProfile>;
-
-// The basic profiles except that they are also all DefaultConstructible.
-template <template <class...> class Receiver>
-using ExpandBasicDefaultConstructibleProfiles =
-    Receiver<DefaultConstructibleNothrowMoveConstructibleProfile,
-             DefaultConstructibleCopyConstructibleProfile,
-             DefaultConstructibleNothrowMovableProfile,
-             DefaultConstructibleValueProfile>;
-
-// The type profiles that we support in Abseil (all of the previous lists).
-template <template <class...> class Receiver>
-using ExpandSupportedProfiles = Receiver<
-    NothrowMoveConstructibleProfile, CopyConstructibleProfile,
-    NothrowMovableProfile, ValueProfile,
-    EquatableNothrowMoveConstructibleProfile, EquatableCopyConstructibleProfile,
-    EquatableNothrowMovableProfile, EquatableValueProfile,
-    ComparableNothrowMoveConstructibleProfile,
-    ComparableCopyConstructibleProfile, ComparableNothrowMovableProfile,
-    ComparableValueProfile, DefaultConstructibleNothrowMoveConstructibleProfile,
-    DefaultConstructibleCopyConstructibleProfile,
-    DefaultConstructibleNothrowMovableProfile, DefaultConstructibleValueProfile,
-    HashableNothrowMoveConstructibleProfile, HashableCopyConstructibleProfile,
-    HashableNothrowMovableProfile, HashableValueProfile>;
-
-// TODO(calabrese) Include types that have throwing move constructors, since in
-// practice we still need to support them because of standard library types with
-// (potentially) non-noexcept moves.
-
-}  // namespace types_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#undef ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS
-
-#endif  // ABSL_TYPES_INTERNAL_CONFORMANCE_ALIASES_H_
diff --git a/absl/types/internal/conformance_archetype.h b/absl/types/internal/conformance_archetype.h
deleted file mode 100644
index 2349e0f..0000000
--- a/absl/types/internal/conformance_archetype.h
+++ /dev/null
@@ -1,978 +0,0 @@
-// Copyright 2019 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.
-//
-// -----------------------------------------------------------------------------
-// conformance_archetype.h
-// -----------------------------------------------------------------------------
-//
-// This file contains a facility for generating "archetypes" of out of
-// "Conformance Profiles" (see "conformance_profiles.h" for more information
-// about Conformance Profiles). An archetype is a type that aims to support the
-// bare minimum requirements of a given Conformance Profile. For instance, an
-// archetype that corresponds to an ImmutableProfile has exactly a nothrow
-// move-constructor, a potentially-throwing copy constructor, a nothrow
-// destructor, with all other special-member-functions deleted. These archetypes
-// are useful for testing to make sure that templates are able to work with the
-// kinds of types that they claim to support (i.e. that they do not accidentally
-// under-constrain),
-//
-// The main type template in this file is the Archetype template, which takes
-// a Conformance Profile as a template argument and its instantiations are a
-// minimum-conforming model of that profile.
-
-#ifndef ABSL_TYPES_INTERNAL_CONFORMANCE_ARCHETYPE_H_
-#define ABSL_TYPES_INTERNAL_CONFORMANCE_ARCHETYPE_H_
-
-#include <cstddef>
-#include <functional>
-#include <type_traits>
-#include <utility>
-
-#include "absl/meta/type_traits.h"
-#include "absl/types/internal/conformance_profile.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace types_internal {
-
-// A minimum-conforming implementation of a type with properties specified in
-// `Prof`, where `Prof` is a valid Conformance Profile.
-template <class Prof, class /*Enabler*/ = void>
-class Archetype;
-
-// Given an Archetype, obtain the properties of the profile associated with that
-// archetype.
-template <class Archetype>
-struct PropertiesOfArchetype;
-
-template <class Prof>
-struct PropertiesOfArchetype<Archetype<Prof>> {
-  using type = PropertiesOfT<Prof>;
-};
-
-template <class Archetype>
-using PropertiesOfArchetypeT = typename PropertiesOfArchetype<Archetype>::type;
-
-// A metafunction to determine if a type is an `Archetype`.
-template <class T>
-struct IsArchetype : std::false_type {};
-
-template <class Prof>
-struct IsArchetype<Archetype<Prof>> : std::true_type {};
-
-// A constructor tag type used when creating an Archetype with internal state.
-struct MakeArchetypeState {};
-
-// Data stored within an archetype that is copied/compared/hashed when the
-// corresponding operations are used.
-using ArchetypeState = std::size_t;
-
-////////////////////////////////////////////////////////////////////////////////
-//   This section of the file defines a chain of base classes for Archetype,  //
-//   where each base defines a specific special member function with the      //
-//   appropriate properties (deleted, noexcept(false), noexcept, or trivial). //
-////////////////////////////////////////////////////////////////////////////////
-
-// The bottom-most base, which contains the state and the default constructor.
-template <default_constructible DefaultConstructibleValue>
-struct ArchetypeStateBase {
-  static_assert(DefaultConstructibleValue == default_constructible::yes ||
-                    DefaultConstructibleValue == default_constructible::nothrow,
-                "");
-
-  ArchetypeStateBase() noexcept(
-      DefaultConstructibleValue ==
-      default_constructible::
-          nothrow) /*Vacuous archetype_state initialization*/ {}
-  explicit ArchetypeStateBase(MakeArchetypeState, ArchetypeState state) noexcept
-      : archetype_state(state) {}
-
-  ArchetypeState archetype_state;
-};
-
-template <>
-struct ArchetypeStateBase<default_constructible::maybe> {
-  explicit ArchetypeStateBase() = delete;
-  explicit ArchetypeStateBase(MakeArchetypeState, ArchetypeState state) noexcept
-      : archetype_state(state) {}
-
-  ArchetypeState archetype_state;
-};
-
-template <>
-struct ArchetypeStateBase<default_constructible::trivial> {
-  ArchetypeStateBase() = default;
-  explicit ArchetypeStateBase(MakeArchetypeState, ArchetypeState state) noexcept
-      : archetype_state(state) {}
-
-  ArchetypeState archetype_state;
-};
-
-// The move-constructor base
-template <default_constructible DefaultConstructibleValue,
-          move_constructible MoveConstructibleValue>
-struct ArchetypeMoveConstructor
-    : ArchetypeStateBase<DefaultConstructibleValue> {
-  static_assert(MoveConstructibleValue == move_constructible::yes ||
-                    MoveConstructibleValue == move_constructible::nothrow,
-                "");
-
-  explicit ArchetypeMoveConstructor(MakeArchetypeState,
-                                    ArchetypeState state) noexcept
-      : ArchetypeStateBase<DefaultConstructibleValue>(MakeArchetypeState(),
-                                                      state) {}
-
-  ArchetypeMoveConstructor() = default;
-  ArchetypeMoveConstructor(ArchetypeMoveConstructor&& other) noexcept(
-      MoveConstructibleValue == move_constructible::nothrow)
-      : ArchetypeStateBase<DefaultConstructibleValue>(MakeArchetypeState(),
-                                                      other.archetype_state) {}
-  ArchetypeMoveConstructor(const ArchetypeMoveConstructor&) = default;
-  ArchetypeMoveConstructor& operator=(ArchetypeMoveConstructor&&) = default;
-  ArchetypeMoveConstructor& operator=(const ArchetypeMoveConstructor&) =
-      default;
-};
-
-template <default_constructible DefaultConstructibleValue>
-struct ArchetypeMoveConstructor<DefaultConstructibleValue,
-                                move_constructible::trivial>
-    : ArchetypeStateBase<DefaultConstructibleValue> {
-  explicit ArchetypeMoveConstructor(MakeArchetypeState,
-                                    ArchetypeState state) noexcept
-      : ArchetypeStateBase<DefaultConstructibleValue>(MakeArchetypeState(),
-                                                      state) {}
-
-  ArchetypeMoveConstructor() = default;
-};
-
-// The copy-constructor base
-template <default_constructible DefaultConstructibleValue,
-          move_constructible MoveConstructibleValue,
-          copy_constructible CopyConstructibleValue>
-struct ArchetypeCopyConstructor
-    : ArchetypeMoveConstructor<DefaultConstructibleValue,
-                               MoveConstructibleValue> {
-  static_assert(CopyConstructibleValue == copy_constructible::yes ||
-                    CopyConstructibleValue == copy_constructible::nothrow,
-                "");
-  explicit ArchetypeCopyConstructor(MakeArchetypeState,
-                                    ArchetypeState state) noexcept
-      : ArchetypeMoveConstructor<DefaultConstructibleValue,
-                                 MoveConstructibleValue>(MakeArchetypeState(),
-                                                         state) {}
-
-  ArchetypeCopyConstructor() = default;
-  ArchetypeCopyConstructor(ArchetypeCopyConstructor&&) = default;
-  ArchetypeCopyConstructor(const ArchetypeCopyConstructor& other) noexcept(
-      CopyConstructibleValue == copy_constructible::nothrow)
-      : ArchetypeMoveConstructor<DefaultConstructibleValue,
-                                 MoveConstructibleValue>(
-            MakeArchetypeState(), other.archetype_state) {}
-  ArchetypeCopyConstructor& operator=(ArchetypeCopyConstructor&&) = default;
-  ArchetypeCopyConstructor& operator=(const ArchetypeCopyConstructor&) =
-      default;
-};
-
-template <default_constructible DefaultConstructibleValue,
-          move_constructible MoveConstructibleValue>
-struct ArchetypeCopyConstructor<DefaultConstructibleValue,
-                                MoveConstructibleValue,
-                                copy_constructible::maybe>
-    : ArchetypeMoveConstructor<DefaultConstructibleValue,
-                               MoveConstructibleValue> {
-  explicit ArchetypeCopyConstructor(MakeArchetypeState,
-                                    ArchetypeState state) noexcept
-      : ArchetypeMoveConstructor<DefaultConstructibleValue,
-                                 MoveConstructibleValue>(MakeArchetypeState(),
-                                                         state) {}
-
-  ArchetypeCopyConstructor() = default;
-  ArchetypeCopyConstructor(ArchetypeCopyConstructor&&) = default;
-  ArchetypeCopyConstructor(const ArchetypeCopyConstructor&) = delete;
-  ArchetypeCopyConstructor& operator=(ArchetypeCopyConstructor&&) = default;
-  ArchetypeCopyConstructor& operator=(const ArchetypeCopyConstructor&) =
-      default;
-};
-
-template <default_constructible DefaultConstructibleValue,
-          move_constructible MoveConstructibleValue>
-struct ArchetypeCopyConstructor<DefaultConstructibleValue,
-                                MoveConstructibleValue,
-                                copy_constructible::trivial>
-    : ArchetypeMoveConstructor<DefaultConstructibleValue,
-                               MoveConstructibleValue> {
-  explicit ArchetypeCopyConstructor(MakeArchetypeState,
-                                    ArchetypeState state) noexcept
-      : ArchetypeMoveConstructor<DefaultConstructibleValue,
-                                 MoveConstructibleValue>(MakeArchetypeState(),
-                                                         state) {}
-
-  ArchetypeCopyConstructor() = default;
-};
-
-// The move-assign base
-template <default_constructible DefaultConstructibleValue,
-          move_constructible MoveConstructibleValue,
-          copy_constructible CopyConstructibleValue,
-          move_assignable MoveAssignableValue>
-struct ArchetypeMoveAssign
-    : ArchetypeCopyConstructor<DefaultConstructibleValue,
-                               MoveConstructibleValue, CopyConstructibleValue> {
-  static_assert(MoveAssignableValue == move_assignable::yes ||
-                    MoveAssignableValue == move_assignable::nothrow,
-                "");
-  explicit ArchetypeMoveAssign(MakeArchetypeState,
-                               ArchetypeState state) noexcept
-      : ArchetypeCopyConstructor<DefaultConstructibleValue,
-                                 MoveConstructibleValue,
-                                 CopyConstructibleValue>(MakeArchetypeState(),
-                                                         state) {}
-
-  ArchetypeMoveAssign() = default;
-  ArchetypeMoveAssign(ArchetypeMoveAssign&&) = default;
-  ArchetypeMoveAssign(const ArchetypeMoveAssign&) = default;
-  ArchetypeMoveAssign& operator=(ArchetypeMoveAssign&& other) noexcept(
-      MoveAssignableValue == move_assignable::nothrow) {
-    this->archetype_state = other.archetype_state;
-    return *this;
-  }
-
-  ArchetypeMoveAssign& operator=(const ArchetypeMoveAssign&) = default;
-};
-
-template <default_constructible DefaultConstructibleValue,
-          move_constructible MoveConstructibleValue,
-          copy_constructible CopyConstructibleValue>
-struct ArchetypeMoveAssign<DefaultConstructibleValue, MoveConstructibleValue,
-                           CopyConstructibleValue, move_assignable::trivial>
-    : ArchetypeCopyConstructor<DefaultConstructibleValue,
-                               MoveConstructibleValue, CopyConstructibleValue> {
-  explicit ArchetypeMoveAssign(MakeArchetypeState,
-                               ArchetypeState state) noexcept
-      : ArchetypeCopyConstructor<DefaultConstructibleValue,
-                                 MoveConstructibleValue,
-                                 CopyConstructibleValue>(MakeArchetypeState(),
-                                                         state) {}
-
-  ArchetypeMoveAssign() = default;
-};
-
-// The copy-assign base
-template <default_constructible DefaultConstructibleValue,
-          move_constructible MoveConstructibleValue,
-          copy_constructible CopyConstructibleValue,
-          move_assignable MoveAssignableValue,
-          copy_assignable CopyAssignableValue>
-struct ArchetypeCopyAssign
-    : ArchetypeMoveAssign<DefaultConstructibleValue, MoveConstructibleValue,
-                          CopyConstructibleValue, MoveAssignableValue> {
-  static_assert(CopyAssignableValue == copy_assignable::yes ||
-                    CopyAssignableValue == copy_assignable::nothrow,
-                "");
-  explicit ArchetypeCopyAssign(MakeArchetypeState,
-                               ArchetypeState state) noexcept
-      : ArchetypeMoveAssign<DefaultConstructibleValue, MoveConstructibleValue,
-                            CopyConstructibleValue, MoveAssignableValue>(
-            MakeArchetypeState(), state) {}
-
-  ArchetypeCopyAssign() = default;
-  ArchetypeCopyAssign(ArchetypeCopyAssign&&) = default;
-  ArchetypeCopyAssign(const ArchetypeCopyAssign&) = default;
-  ArchetypeCopyAssign& operator=(ArchetypeCopyAssign&&) = default;
-
-  ArchetypeCopyAssign& operator=(const ArchetypeCopyAssign& other) noexcept(
-      CopyAssignableValue == copy_assignable::nothrow) {
-    this->archetype_state = other.archetype_state;
-    return *this;
-  }
-};
-
-template <default_constructible DefaultConstructibleValue,
-          move_constructible MoveConstructibleValue,
-          copy_constructible CopyConstructibleValue,
-          move_assignable MoveAssignableValue>
-struct ArchetypeCopyAssign<DefaultConstructibleValue, MoveConstructibleValue,
-                           CopyConstructibleValue, MoveAssignableValue,
-                           copy_assignable::maybe>
-    : ArchetypeMoveAssign<DefaultConstructibleValue, MoveConstructibleValue,
-                          CopyConstructibleValue, MoveAssignableValue> {
-  explicit ArchetypeCopyAssign(MakeArchetypeState,
-                               ArchetypeState state) noexcept
-      : ArchetypeMoveAssign<DefaultConstructibleValue, MoveConstructibleValue,
-                            CopyConstructibleValue, MoveAssignableValue>(
-            MakeArchetypeState(), state) {}
-
-  ArchetypeCopyAssign() = default;
-  ArchetypeCopyAssign(ArchetypeCopyAssign&&) = default;
-  ArchetypeCopyAssign(const ArchetypeCopyAssign&) = default;
-  ArchetypeCopyAssign& operator=(ArchetypeCopyAssign&&) = default;
-  ArchetypeCopyAssign& operator=(const ArchetypeCopyAssign&) = delete;
-};
-
-template <default_constructible DefaultConstructibleValue,
-          move_constructible MoveConstructibleValue,
-          copy_constructible CopyConstructibleValue,
-          move_assignable MoveAssignableValue>
-struct ArchetypeCopyAssign<DefaultConstructibleValue, MoveConstructibleValue,
-                           CopyConstructibleValue, MoveAssignableValue,
-                           copy_assignable::trivial>
-    : ArchetypeMoveAssign<DefaultConstructibleValue, MoveConstructibleValue,
-                          CopyConstructibleValue, MoveAssignableValue> {
-  explicit ArchetypeCopyAssign(MakeArchetypeState,
-                               ArchetypeState state) noexcept
-      : ArchetypeMoveAssign<DefaultConstructibleValue, MoveConstructibleValue,
-                            CopyConstructibleValue, MoveAssignableValue>(
-            MakeArchetypeState(), state) {}
-
-  ArchetypeCopyAssign() = default;
-};
-
-// The destructor base
-template <default_constructible DefaultConstructibleValue,
-          move_constructible MoveConstructibleValue,
-          copy_constructible CopyConstructibleValue,
-          move_assignable MoveAssignableValue,
-          copy_assignable CopyAssignableValue, destructible DestructibleValue>
-struct ArchetypeDestructor
-    : ArchetypeCopyAssign<DefaultConstructibleValue, MoveConstructibleValue,
-                          CopyConstructibleValue, MoveAssignableValue,
-                          CopyAssignableValue> {
-  static_assert(DestructibleValue == destructible::yes ||
-                    DestructibleValue == destructible::nothrow,
-                "");
-
-  explicit ArchetypeDestructor(MakeArchetypeState,
-                               ArchetypeState state) noexcept
-      : ArchetypeCopyAssign<DefaultConstructibleValue, MoveConstructibleValue,
-                            CopyConstructibleValue, MoveAssignableValue,
-                            CopyAssignableValue>(MakeArchetypeState(), state) {}
-
-  ArchetypeDestructor() = default;
-  ArchetypeDestructor(ArchetypeDestructor&&) = default;
-  ArchetypeDestructor(const ArchetypeDestructor&) = default;
-  ArchetypeDestructor& operator=(ArchetypeDestructor&&) = default;
-  ArchetypeDestructor& operator=(const ArchetypeDestructor&) = default;
-  ~ArchetypeDestructor() noexcept(DestructibleValue == destructible::nothrow) {}
-};
-
-template <default_constructible DefaultConstructibleValue,
-          move_constructible MoveConstructibleValue,
-          copy_constructible CopyConstructibleValue,
-          move_assignable MoveAssignableValue,
-          copy_assignable CopyAssignableValue>
-struct ArchetypeDestructor<DefaultConstructibleValue, MoveConstructibleValue,
-                           CopyConstructibleValue, MoveAssignableValue,
-                           CopyAssignableValue, destructible::trivial>
-    : ArchetypeCopyAssign<DefaultConstructibleValue, MoveConstructibleValue,
-                          CopyConstructibleValue, MoveAssignableValue,
-                          CopyAssignableValue> {
-  explicit ArchetypeDestructor(MakeArchetypeState,
-                               ArchetypeState state) noexcept
-      : ArchetypeCopyAssign<DefaultConstructibleValue, MoveConstructibleValue,
-                            CopyConstructibleValue, MoveAssignableValue,
-                            CopyAssignableValue>(MakeArchetypeState(), state) {}
-
-  ArchetypeDestructor() = default;
-};
-
-// An alias to the top of the chain of bases for special-member functions.
-// NOTE: move_constructible::maybe, move_assignable::maybe, and
-// destructible::maybe are handled in the top-level type by way of SFINAE.
-// Because of this, we never instantiate the base classes with
-// move_constructible::maybe, move_assignable::maybe, or destructible::maybe so
-// that we minimize the number of different possible type-template
-// instantiations.
-template <default_constructible DefaultConstructibleValue,
-          move_constructible MoveConstructibleValue,
-          copy_constructible CopyConstructibleValue,
-          move_assignable MoveAssignableValue,
-          copy_assignable CopyAssignableValue, destructible DestructibleValue>
-using ArchetypeSpecialMembersBase = ArchetypeDestructor<
-    DefaultConstructibleValue,
-    MoveConstructibleValue != move_constructible::maybe
-        ? MoveConstructibleValue
-        : move_constructible::nothrow,
-    CopyConstructibleValue,
-    MoveAssignableValue != move_assignable::maybe ? MoveAssignableValue
-                                                  : move_assignable::nothrow,
-    CopyAssignableValue,
-    DestructibleValue != destructible::maybe ? DestructibleValue
-                                             : destructible::nothrow>;
-
-// A function that is used to create an archetype with some associated state.
-template <class Arch>
-Arch MakeArchetype(ArchetypeState state) noexcept {
-  static_assert(IsArchetype<Arch>::value,
-                "The explicit template argument to MakeArchetype is required "
-                "to be an Archetype.");
-  return Arch(MakeArchetypeState(), state);
-}
-
-// This is used to conditionally delete "copy" and "move" constructors in a way
-// that is consistent with what the ConformanceProfile requires and that also
-// strictly enforces the arguments to the copy/move to not come from implicit
-// conversions when dealing with the Archetype.
-template <class Prof, class T>
-constexpr bool ShouldDeleteConstructor() {
-  return !((PropertiesOfT<Prof>::move_constructible_support !=
-                move_constructible::maybe &&
-            std::is_same<T, Archetype<Prof>>::value) ||
-           (PropertiesOfT<Prof>::copy_constructible_support !=
-                copy_constructible::maybe &&
-            (std::is_same<T, const Archetype<Prof>&>::value ||
-             std::is_same<T, Archetype<Prof>&>::value ||
-             std::is_same<T, const Archetype<Prof>>::value)));
-}
-
-// This is used to conditionally delete "copy" and "move" assigns in a way
-// that is consistent with what the ConformanceProfile requires and that also
-// strictly enforces the arguments to the copy/move to not come from implicit
-// conversions when dealing with the Archetype.
-template <class Prof, class T>
-constexpr bool ShouldDeleteAssign() {
-  return !(
-      (PropertiesOfT<Prof>::move_assignable_support != move_assignable::maybe &&
-       std::is_same<T, Archetype<Prof>>::value) ||
-      (PropertiesOfT<Prof>::copy_assignable_support != copy_assignable::maybe &&
-       (std::is_same<T, const Archetype<Prof>&>::value ||
-        std::is_same<T, Archetype<Prof>&>::value ||
-        std::is_same<T, const Archetype<Prof>>::value)));
-}
-
-// TODO(calabrese) Inherit from a chain of secondary bases to pull in the
-// associated functions of other concepts.
-template <class Prof, class Enabler>
-class Archetype : ArchetypeSpecialMembersBase<
-                      PropertiesOfT<Prof>::default_constructible_support,
-                      PropertiesOfT<Prof>::move_constructible_support,
-                      PropertiesOfT<Prof>::copy_constructible_support,
-                      PropertiesOfT<Prof>::move_assignable_support,
-                      PropertiesOfT<Prof>::copy_assignable_support,
-                      PropertiesOfT<Prof>::destructible_support> {
-  static_assert(std::is_same<Enabler, void>::value,
-                "An explicit type must not be passed as the second template "
-                "argument to 'Archetype`.");
-
-  // The cases mentioned in these static_asserts are expected to be handled in
-  // the partial template specializations of Archetype that follow this
-  // definition.
-  static_assert(PropertiesOfT<Prof>::destructible_support !=
-                    destructible::maybe,
-                "");
-  static_assert(PropertiesOfT<Prof>::move_constructible_support !=
-                        move_constructible::maybe ||
-                    PropertiesOfT<Prof>::copy_constructible_support ==
-                        copy_constructible::maybe,
-                "");
-  static_assert(PropertiesOfT<Prof>::move_assignable_support !=
-                        move_assignable::maybe ||
-                    PropertiesOfT<Prof>::copy_assignable_support ==
-                        copy_assignable::maybe,
-                "");
-
- public:
-  Archetype() = default;
-
-  // Disallow moves when requested, and disallow implicit conversions.
-  template <class T, typename std::enable_if<
-                         ShouldDeleteConstructor<Prof, T>()>::type* = nullptr>
-  Archetype(T&&) = delete;
-
-  // Disallow moves when requested, and disallow implicit conversions.
-  template <class T, typename std::enable_if<
-                         ShouldDeleteAssign<Prof, T>()>::type* = nullptr>
-  Archetype& operator=(T&&) = delete;
-
-  using ArchetypeSpecialMembersBase<
-      PropertiesOfT<Prof>::default_constructible_support,
-      PropertiesOfT<Prof>::move_constructible_support,
-      PropertiesOfT<Prof>::copy_constructible_support,
-      PropertiesOfT<Prof>::move_assignable_support,
-      PropertiesOfT<Prof>::copy_assignable_support,
-      PropertiesOfT<Prof>::destructible_support>::archetype_state;
-
- private:
-  explicit Archetype(MakeArchetypeState, ArchetypeState state) noexcept
-      : ArchetypeSpecialMembersBase<
-            PropertiesOfT<Prof>::default_constructible_support,
-            PropertiesOfT<Prof>::move_constructible_support,
-            PropertiesOfT<Prof>::copy_constructible_support,
-            PropertiesOfT<Prof>::move_assignable_support,
-            PropertiesOfT<Prof>::copy_assignable_support,
-            PropertiesOfT<Prof>::destructible_support>(MakeArchetypeState(),
-                                                       state) {}
-
-  friend Archetype MakeArchetype<Archetype>(ArchetypeState) noexcept;
-};
-
-template <class Prof>
-class Archetype<Prof, typename std::enable_if<
-                          PropertiesOfT<Prof>::move_constructible_support !=
-                              move_constructible::maybe &&
-                          PropertiesOfT<Prof>::move_assignable_support ==
-                              move_assignable::maybe &&
-                          PropertiesOfT<Prof>::destructible_support !=
-                              destructible::maybe>::type>
-    : ArchetypeSpecialMembersBase<
-          PropertiesOfT<Prof>::default_constructible_support,
-          PropertiesOfT<Prof>::move_constructible_support,
-          PropertiesOfT<Prof>::copy_constructible_support,
-          PropertiesOfT<Prof>::move_assignable_support,
-          PropertiesOfT<Prof>::copy_assignable_support,
-          PropertiesOfT<Prof>::destructible_support> {
- public:
-  Archetype() = default;
-  Archetype(Archetype&&) = default;
-  Archetype(const Archetype&) = default;
-  Archetype& operator=(Archetype&&) = delete;
-  Archetype& operator=(const Archetype&) = default;
-
-  // Disallow moves when requested, and disallow implicit conversions.
-  template <class T, typename std::enable_if<
-                         ShouldDeleteConstructor<Prof, T>()>::type* = nullptr>
-  Archetype(T&&) = delete;
-
-  // Disallow moves when requested, and disallow implicit conversions.
-  template <class T, typename std::enable_if<
-                         ShouldDeleteAssign<Prof, T>()>::type* = nullptr>
-  Archetype& operator=(T&&) = delete;
-
-  using ArchetypeSpecialMembersBase<
-      PropertiesOfT<Prof>::default_constructible_support,
-      PropertiesOfT<Prof>::move_constructible_support,
-      PropertiesOfT<Prof>::copy_constructible_support,
-      PropertiesOfT<Prof>::move_assignable_support,
-      PropertiesOfT<Prof>::copy_assignable_support,
-      PropertiesOfT<Prof>::destructible_support>::archetype_state;
-
- private:
-  explicit Archetype(MakeArchetypeState, ArchetypeState state) noexcept
-      : ArchetypeSpecialMembersBase<
-            PropertiesOfT<Prof>::default_constructible_support,
-            PropertiesOfT<Prof>::move_constructible_support,
-            PropertiesOfT<Prof>::copy_constructible_support,
-            PropertiesOfT<Prof>::move_assignable_support,
-            PropertiesOfT<Prof>::copy_assignable_support,
-            PropertiesOfT<Prof>::destructible_support>(MakeArchetypeState(),
-                                                       state) {}
-
-  friend Archetype MakeArchetype<Archetype>(ArchetypeState) noexcept;
-};
-
-template <class Prof>
-class Archetype<Prof, typename std::enable_if<
-                          PropertiesOfT<Prof>::move_constructible_support ==
-                              move_constructible::maybe &&
-                          PropertiesOfT<Prof>::move_assignable_support ==
-                              move_assignable::maybe &&
-                          PropertiesOfT<Prof>::destructible_support !=
-                              destructible::maybe>::type>
-    : ArchetypeSpecialMembersBase<
-          PropertiesOfT<Prof>::default_constructible_support,
-          PropertiesOfT<Prof>::move_constructible_support,
-          PropertiesOfT<Prof>::copy_constructible_support,
-          PropertiesOfT<Prof>::move_assignable_support,
-          PropertiesOfT<Prof>::copy_assignable_support,
-          PropertiesOfT<Prof>::destructible_support> {
- public:
-  Archetype() = default;
-  Archetype(Archetype&&) = delete;
-  Archetype(const Archetype&) = default;
-  Archetype& operator=(Archetype&&) = delete;
-  Archetype& operator=(const Archetype&) = default;
-
-  // Disallow moves when requested, and disallow implicit conversions.
-  template <class T, typename std::enable_if<
-                         ShouldDeleteConstructor<Prof, T>()>::type* = nullptr>
-  Archetype(T&&) = delete;
-
-  // Disallow moves when requested, and disallow implicit conversions.
-  template <class T, typename std::enable_if<
-                         ShouldDeleteAssign<Prof, T>()>::type* = nullptr>
-  Archetype& operator=(T&&) = delete;
-
-  using ArchetypeSpecialMembersBase<
-      PropertiesOfT<Prof>::default_constructible_support,
-      PropertiesOfT<Prof>::move_constructible_support,
-      PropertiesOfT<Prof>::copy_constructible_support,
-      PropertiesOfT<Prof>::move_assignable_support,
-      PropertiesOfT<Prof>::copy_assignable_support,
-      PropertiesOfT<Prof>::destructible_support>::archetype_state;
-
- private:
-  explicit Archetype(MakeArchetypeState, ArchetypeState state) noexcept
-      : ArchetypeSpecialMembersBase<
-            PropertiesOfT<Prof>::default_constructible_support,
-            PropertiesOfT<Prof>::move_constructible_support,
-            PropertiesOfT<Prof>::copy_constructible_support,
-            PropertiesOfT<Prof>::move_assignable_support,
-            PropertiesOfT<Prof>::copy_assignable_support,
-            PropertiesOfT<Prof>::destructible_support>(MakeArchetypeState(),
-                                                       state) {}
-
-  friend Archetype MakeArchetype<Archetype>(ArchetypeState) noexcept;
-};
-
-template <class Prof>
-class Archetype<Prof, typename std::enable_if<
-                          PropertiesOfT<Prof>::move_constructible_support ==
-                              move_constructible::maybe &&
-                          PropertiesOfT<Prof>::move_assignable_support !=
-                              move_assignable::maybe &&
-                          PropertiesOfT<Prof>::destructible_support !=
-                              destructible::maybe>::type>
-    : ArchetypeSpecialMembersBase<
-          PropertiesOfT<Prof>::default_constructible_support,
-          PropertiesOfT<Prof>::move_constructible_support,
-          PropertiesOfT<Prof>::copy_constructible_support,
-          PropertiesOfT<Prof>::move_assignable_support,
-          PropertiesOfT<Prof>::copy_assignable_support,
-          PropertiesOfT<Prof>::destructible_support> {
- public:
-  Archetype() = default;
-  Archetype(Archetype&&) = delete;
-  Archetype(const Archetype&) = default;
-  Archetype& operator=(Archetype&&) = default;
-  Archetype& operator=(const Archetype&) = default;
-
-  // Disallow moves when requested, and disallow implicit conversions.
-  template <class T, typename std::enable_if<
-                         ShouldDeleteConstructor<Prof, T>()>::type* = nullptr>
-  Archetype(T&&) = delete;
-
-  // Disallow moves when requested, and disallow implicit conversions.
-  template <class T, typename std::enable_if<
-                         ShouldDeleteAssign<Prof, T>()>::type* = nullptr>
-  Archetype& operator=(T&&) = delete;
-
-  using ArchetypeSpecialMembersBase<
-      PropertiesOfT<Prof>::default_constructible_support,
-      PropertiesOfT<Prof>::move_constructible_support,
-      PropertiesOfT<Prof>::copy_constructible_support,
-      PropertiesOfT<Prof>::move_assignable_support,
-      PropertiesOfT<Prof>::copy_assignable_support,
-      PropertiesOfT<Prof>::destructible_support>::archetype_state;
-
- private:
-  explicit Archetype(MakeArchetypeState, ArchetypeState state) noexcept
-      : ArchetypeSpecialMembersBase<
-            PropertiesOfT<Prof>::default_constructible_support,
-            PropertiesOfT<Prof>::move_constructible_support,
-            PropertiesOfT<Prof>::copy_constructible_support,
-            PropertiesOfT<Prof>::move_assignable_support,
-            PropertiesOfT<Prof>::copy_assignable_support,
-            PropertiesOfT<Prof>::destructible_support>(MakeArchetypeState(),
-                                                       state) {}
-
-  friend Archetype MakeArchetype<Archetype>(ArchetypeState) noexcept;
-};
-
-template <class Prof>
-class Archetype<Prof, typename std::enable_if<
-                          PropertiesOfT<Prof>::move_constructible_support !=
-                              move_constructible::maybe &&
-                          PropertiesOfT<Prof>::move_assignable_support ==
-                              move_assignable::maybe &&
-                          PropertiesOfT<Prof>::destructible_support ==
-                              destructible::maybe>::type>
-    : ArchetypeSpecialMembersBase<
-          PropertiesOfT<Prof>::default_constructible_support,
-          PropertiesOfT<Prof>::move_constructible_support,
-          PropertiesOfT<Prof>::copy_constructible_support,
-          PropertiesOfT<Prof>::move_assignable_support,
-          PropertiesOfT<Prof>::copy_assignable_support,
-          PropertiesOfT<Prof>::destructible_support> {
- public:
-  Archetype() = default;
-  Archetype(Archetype&&) = default;
-  Archetype(const Archetype&) = default;
-  Archetype& operator=(Archetype&&) = delete;
-  Archetype& operator=(const Archetype&) = default;
-  ~Archetype() = delete;
-
-  // Disallow moves when requested, and disallow implicit conversions.
-  template <class T, typename std::enable_if<
-                         ShouldDeleteConstructor<Prof, T>()>::type* = nullptr>
-  Archetype(T&&) = delete;
-
-  // Disallow moves when requested, and disallow implicit conversions.
-  template <class T, typename std::enable_if<
-                         ShouldDeleteAssign<Prof, T>()>::type* = nullptr>
-  Archetype& operator=(T&&) = delete;
-
-  using ArchetypeSpecialMembersBase<
-      PropertiesOfT<Prof>::default_constructible_support,
-      PropertiesOfT<Prof>::move_constructible_support,
-      PropertiesOfT<Prof>::copy_constructible_support,
-      PropertiesOfT<Prof>::move_assignable_support,
-      PropertiesOfT<Prof>::copy_assignable_support,
-      PropertiesOfT<Prof>::destructible_support>::archetype_state;
-
- private:
-  explicit Archetype(MakeArchetypeState, ArchetypeState state) noexcept
-      : ArchetypeSpecialMembersBase<
-            PropertiesOfT<Prof>::default_constructible_support,
-            PropertiesOfT<Prof>::move_constructible_support,
-            PropertiesOfT<Prof>::copy_constructible_support,
-            PropertiesOfT<Prof>::move_assignable_support,
-            PropertiesOfT<Prof>::copy_assignable_support,
-            PropertiesOfT<Prof>::destructible_support>(MakeArchetypeState(),
-                                                       state) {}
-
-  friend Archetype MakeArchetype<Archetype>(ArchetypeState) noexcept;
-};
-
-template <class Prof>
-class Archetype<Prof, typename std::enable_if<
-                          PropertiesOfT<Prof>::move_constructible_support ==
-                              move_constructible::maybe &&
-                          PropertiesOfT<Prof>::move_assignable_support ==
-                              move_assignable::maybe &&
-                          PropertiesOfT<Prof>::destructible_support ==
-                              destructible::maybe>::type>
-    : ArchetypeSpecialMembersBase<
-          PropertiesOfT<Prof>::default_constructible_support,
-          PropertiesOfT<Prof>::move_constructible_support,
-          PropertiesOfT<Prof>::copy_constructible_support,
-          PropertiesOfT<Prof>::move_assignable_support,
-          PropertiesOfT<Prof>::copy_assignable_support,
-          PropertiesOfT<Prof>::destructible_support> {
- public:
-  Archetype() = default;
-  Archetype(Archetype&&) = delete;
-  Archetype(const Archetype&) = default;
-  Archetype& operator=(Archetype&&) = delete;
-  Archetype& operator=(const Archetype&) = default;
-  ~Archetype() = delete;
-
-  // Disallow moves when requested, and disallow implicit conversions.
-  template <class T, typename std::enable_if<
-                         ShouldDeleteConstructor<Prof, T>()>::type* = nullptr>
-  Archetype(T&&) = delete;
-
-  // Disallow moves when requested, and disallow implicit conversions.
-  template <class T, typename std::enable_if<
-                         ShouldDeleteAssign<Prof, T>()>::type* = nullptr>
-  Archetype& operator=(T&&) = delete;
-
-  using ArchetypeSpecialMembersBase<
-      PropertiesOfT<Prof>::default_constructible_support,
-      PropertiesOfT<Prof>::move_constructible_support,
-      PropertiesOfT<Prof>::copy_constructible_support,
-      PropertiesOfT<Prof>::move_assignable_support,
-      PropertiesOfT<Prof>::copy_assignable_support,
-      PropertiesOfT<Prof>::destructible_support>::archetype_state;
-
- private:
-  explicit Archetype(MakeArchetypeState, ArchetypeState state) noexcept
-      : ArchetypeSpecialMembersBase<
-            PropertiesOfT<Prof>::default_constructible_support,
-            PropertiesOfT<Prof>::move_constructible_support,
-            PropertiesOfT<Prof>::copy_constructible_support,
-            PropertiesOfT<Prof>::move_assignable_support,
-            PropertiesOfT<Prof>::copy_assignable_support,
-            PropertiesOfT<Prof>::destructible_support>(MakeArchetypeState(),
-                                                       state) {}
-
-  friend Archetype MakeArchetype<Archetype>(ArchetypeState) noexcept;
-};
-
-template <class Prof>
-class Archetype<Prof, typename std::enable_if<
-                          PropertiesOfT<Prof>::move_constructible_support ==
-                              move_constructible::maybe &&
-                          PropertiesOfT<Prof>::move_assignable_support !=
-                              move_assignable::maybe &&
-                          PropertiesOfT<Prof>::destructible_support ==
-                              destructible::maybe>::type>
-    : ArchetypeSpecialMembersBase<
-          PropertiesOfT<Prof>::default_constructible_support,
-          PropertiesOfT<Prof>::move_constructible_support,
-          PropertiesOfT<Prof>::copy_constructible_support,
-          PropertiesOfT<Prof>::move_assignable_support,
-          PropertiesOfT<Prof>::copy_assignable_support,
-          PropertiesOfT<Prof>::destructible_support> {
- public:
-  Archetype() = default;
-  Archetype(Archetype&&) = delete;
-  Archetype(const Archetype&) = default;
-  Archetype& operator=(Archetype&&) = default;
-  Archetype& operator=(const Archetype&) = default;
-  ~Archetype() = delete;
-
-  // Disallow moves when requested, and disallow implicit conversions.
-  template <class T, typename std::enable_if<
-                         ShouldDeleteConstructor<Prof, T>()>::type* = nullptr>
-  Archetype(T&&) = delete;
-
-  // Disallow moves when requested, and disallow implicit conversions.
-  template <class T, typename std::enable_if<
-                         ShouldDeleteAssign<Prof, T>()>::type* = nullptr>
-  Archetype& operator=(T&&) = delete;
-
-  using ArchetypeSpecialMembersBase<
-      PropertiesOfT<Prof>::default_constructible_support,
-      PropertiesOfT<Prof>::move_constructible_support,
-      PropertiesOfT<Prof>::copy_constructible_support,
-      PropertiesOfT<Prof>::move_assignable_support,
-      PropertiesOfT<Prof>::copy_assignable_support,
-      PropertiesOfT<Prof>::destructible_support>::archetype_state;
-
- private:
-  explicit Archetype(MakeArchetypeState, ArchetypeState state) noexcept
-      : ArchetypeSpecialMembersBase<
-            PropertiesOfT<Prof>::default_constructible_support,
-            PropertiesOfT<Prof>::move_constructible_support,
-            PropertiesOfT<Prof>::copy_constructible_support,
-            PropertiesOfT<Prof>::move_assignable_support,
-            PropertiesOfT<Prof>::copy_assignable_support,
-            PropertiesOfT<Prof>::destructible_support>(MakeArchetypeState(),
-                                                       state) {}
-
-  friend Archetype MakeArchetype<Archetype>(ArchetypeState) noexcept;
-};
-
-// Explicitly deleted swap for Archetype if the profile does not require swap.
-// It is important to delete it rather than simply leave it out so that the
-// "using std::swap;" idiom will result in this deleted overload being picked.
-template <class Prof,
-          absl::enable_if_t<!PropertiesOfT<Prof>::is_swappable, int> = 0>
-void swap(Archetype<Prof>&, Archetype<Prof>&) = delete;  // NOLINT
-
-// A conditionally-noexcept swap implementation for Archetype when the profile
-// supports swap.
-template <class Prof,
-          absl::enable_if_t<PropertiesOfT<Prof>::is_swappable, int> = 0>
-void swap(Archetype<Prof>& lhs, Archetype<Prof>& rhs)  // NOLINT
-    noexcept(PropertiesOfT<Prof>::swappable_support != swappable::yes) {
-  std::swap(lhs.archetype_state, rhs.archetype_state);
-}
-
-// A convertible-to-bool type that is used as the return type of comparison
-// operators since the standard doesn't always require exactly bool.
-struct NothrowBool {
-  explicit NothrowBool() = delete;
-  ~NothrowBool() = default;
-
-  // TODO(calabrese) Delete the copy constructor in C++17 mode since guaranteed
-  // elision makes it not required when returning from a function.
-  // NothrowBool(NothrowBool const&) = delete;
-
-  NothrowBool& operator=(NothrowBool const&) = delete;
-
-  explicit operator bool() const noexcept { return value; }
-
-  static NothrowBool make(bool const value) noexcept {
-    return NothrowBool(value);
-  }
-
- private:
-  explicit NothrowBool(bool const value) noexcept : value(value) {}
-
-  bool value;
-};
-
-// A convertible-to-bool type that is used as the return type of comparison
-// operators since the standard doesn't always require exactly bool.
-// Note: ExceptionalBool has a conversion operator that is not noexcept, so
-// that even when a comparison operator is noexcept, that operation may still
-// potentially throw when converted to bool.
-struct ExceptionalBool {
-  explicit ExceptionalBool() = delete;
-  ~ExceptionalBool() = default;
-
-  // TODO(calabrese) Delete the copy constructor in C++17 mode since guaranteed
-  // elision makes it not required when returning from a function.
-  // ExceptionalBool(ExceptionalBool const&) = delete;
-
-  ExceptionalBool& operator=(ExceptionalBool const&) = delete;
-
-  explicit operator bool() const { return value; }  // NOLINT
-
-  static ExceptionalBool make(bool const value) noexcept {
-    return ExceptionalBool(value);
-  }
-
- private:
-  explicit ExceptionalBool(bool const value) noexcept : value(value) {}
-
-  bool value;
-};
-
-// The following macro is only used as a helper in this file to stamp out
-// comparison operator definitions. It is undefined after usage.
-//
-// NOTE: Non-nothrow operators throw via their result's conversion to bool even
-// though the operation itself is noexcept.
-#define ABSL_TYPES_INTERNAL_OP(enum_name, op)                                \
-  template <class Prof>                                                      \
-  absl::enable_if_t<!PropertiesOfT<Prof>::is_##enum_name, bool> operator op( \
-      const Archetype<Prof>&, const Archetype<Prof>&) = delete;              \
-                                                                             \
-  template <class Prof>                                                      \
-  typename absl::enable_if_t<                                                \
-      PropertiesOfT<Prof>::is_##enum_name,                                   \
-      std::conditional<PropertiesOfT<Prof>::enum_name##_support ==           \
-                           enum_name::nothrow,                               \
-                       NothrowBool, ExceptionalBool>>::type                  \
-  operator op(const Archetype<Prof>& lhs,                                    \
-              const Archetype<Prof>& rhs) noexcept {                         \
-    return absl::conditional_t<                                              \
-        PropertiesOfT<Prof>::enum_name##_support == enum_name::nothrow,      \
-        NothrowBool, ExceptionalBool>::make(lhs.archetype_state op           \
-                                                rhs.archetype_state);        \
-  }
-
-ABSL_TYPES_INTERNAL_OP(equality_comparable, ==);
-ABSL_TYPES_INTERNAL_OP(inequality_comparable, !=);
-ABSL_TYPES_INTERNAL_OP(less_than_comparable, <);
-ABSL_TYPES_INTERNAL_OP(less_equal_comparable, <=);
-ABSL_TYPES_INTERNAL_OP(greater_equal_comparable, >=);
-ABSL_TYPES_INTERNAL_OP(greater_than_comparable, >);
-
-#undef ABSL_TYPES_INTERNAL_OP
-
-// Base class for std::hash specializations when an Archetype doesn't support
-// hashing.
-struct PoisonedHash {
-  PoisonedHash() = delete;
-  PoisonedHash(const PoisonedHash&) = delete;
-  PoisonedHash& operator=(const PoisonedHash&) = delete;
-};
-
-// Base class for std::hash specializations when an Archetype supports hashing.
-template <class Prof>
-struct EnabledHash {
-  using argument_type = Archetype<Prof>;
-  using result_type = std::size_t;
-  result_type operator()(const argument_type& arg) const {
-    return std::hash<ArchetypeState>()(arg.archetype_state);
-  }
-};
-
-}  // namespace types_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-namespace std {
-
-template <class Prof>  // NOLINT
-struct hash<::absl::types_internal::Archetype<Prof>>
-    : conditional<::absl::types_internal::PropertiesOfT<Prof>::is_hashable,
-                  ::absl::types_internal::EnabledHash<Prof>,
-                  ::absl::types_internal::PoisonedHash>::type {};
-
-}  // namespace std
-
-#endif  // ABSL_TYPES_INTERNAL_CONFORMANCE_ARCHETYPE_H_
diff --git a/absl/types/internal/conformance_profile.h b/absl/types/internal/conformance_profile.h
deleted file mode 100644
index 37b017d..0000000
--- a/absl/types/internal/conformance_profile.h
+++ /dev/null
@@ -1,933 +0,0 @@
-// Copyright 2019 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.
-//
-// -----------------------------------------------------------------------------
-// conformance_profiles.h
-// -----------------------------------------------------------------------------
-//
-// This file contains templates for representing "Regularity Profiles" and
-// concisely-named versions of commonly used Regularity Profiles.
-//
-// A Regularity Profile is a compile-time description of the types of operations
-// that a given type supports, along with properties of those operations when
-// they do exist. For instance, a Regularity Profile may describe a type that
-// has a move-constructor that is noexcept and a copy constructor that is not
-// noexcept. This description can then be examined and passed around to other
-// templates for the purposes of asserting expectations on user-defined types
-// via a series trait checks, or for determining what kinds of run-time tests
-// are able to be performed.
-//
-// Regularity Profiles are also used when creating "archetypes," which are
-// minimum-conforming types that meet all of the requirements of a given
-// Regularity Profile. For more information regarding archetypes, see
-// "conformance_archetypes.h".
-
-#ifndef ABSL_TYPES_INTERNAL_CONFORMANCE_PROFILE_H_
-#define ABSL_TYPES_INTERNAL_CONFORMANCE_PROFILE_H_
-
-#include <set>
-#include <type_traits>
-#include <utility>
-#include <vector>
-
-#include "gtest/gtest.h"
-#include "absl/algorithm/container.h"
-#include "absl/meta/type_traits.h"
-#include "absl/strings/ascii.h"
-#include "absl/strings/str_cat.h"
-#include "absl/strings/string_view.h"
-#include "absl/types/internal/conformance_testing_helpers.h"
-#include "absl/utility/utility.h"
-
-// TODO(calabrese) Add support for extending profiles.
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace types_internal {
-
-// Converts an enum to its underlying integral value.
-template <typename Enum>
-constexpr absl::underlying_type_t<Enum> UnderlyingValue(Enum value) {
-  return static_cast<absl::underlying_type_t<Enum>>(value);
-}
-
-// A tag type used in place of a matcher when checking that an assertion result
-// does not actually contain any errors.
-struct NoError {};
-
-// -----------------------------------------------------------------------------
-// ConformanceErrors
-// -----------------------------------------------------------------------------
-class ConformanceErrors {
- public:
-  // Setup the error reporting mechanism by seeding it with the name of the type
-  // that is being tested.
-  explicit ConformanceErrors(std::string type_name)
-      : assertion_result_(false), type_name_(std::move(type_name)) {
-    assertion_result_ << "\n\n"
-                         "Assuming the following type alias:\n"
-                         "\n"
-                         "  using _T = "
-                      << type_name_ << ";\n\n";
-    outputDivider();
-  }
-
-  // Adds the test name to the list of successfully run tests iff it was not
-  // previously reported as failing. This behavior is useful for tests that
-  // have multiple parts, where failures and successes are reported individually
-  // with the same test name.
-  void addTestSuccess(absl::string_view test_name) {
-    auto normalized_test_name = absl::AsciiStrToLower(test_name);
-
-    // If the test is already reported as failing, do not add it to the list of
-    // successes.
-    if (test_failures_.find(normalized_test_name) == test_failures_.end()) {
-      test_successes_.insert(std::move(normalized_test_name));
-    }
-  }
-
-  // Streams a single error description into the internal buffer (a visual
-  // divider is automatically inserted after the error so that multiple errors
-  // are visibly distinct).
-  //
-  // This function increases the error count by 1.
-  //
-  // TODO(calabrese) Determine desired behavior when if this function throws.
-  template <class... P>
-  void addTestFailure(absl::string_view test_name, const P&... args) {
-    // Output a message related to the test failure.
-    assertion_result_ << "\n\n"
-                         "Failed test: "
-                      << test_name << "\n\n";
-    addTestFailureImpl(args...);
-    assertion_result_ << "\n\n";
-    outputDivider();
-
-    auto normalized_test_name = absl::AsciiStrToLower(test_name);
-
-    // If previous parts of this test succeeded, remove it from that set.
-    test_successes_.erase(normalized_test_name);
-
-    // Add the test name to the list of failed tests.
-    test_failures_.insert(std::move(normalized_test_name));
-
-    has_error_ = true;
-  }
-
-  // Convert this object into a testing::AssertionResult instance such that it
-  // can be used with gtest.
-  ::testing::AssertionResult assertionResult() const {
-    return has_error_ ? assertion_result_ : ::testing::AssertionSuccess();
-  }
-
-  // Convert this object into a testing::AssertionResult instance such that it
-  // can be used with gtest. This overload expects errors, using the specified
-  // matcher.
-  ::testing::AssertionResult expectFailedTests(
-      const std::set<std::string>& test_names) const {
-    // Since we are expecting nonconformance, output an error message when the
-    // type actually conformed to the specified profile.
-    if (!has_error_) {
-      return ::testing::AssertionFailure()
-             << "Unexpected conformance of type:\n"
-                "    "
-             << type_name_ << "\n\n";
-    }
-
-    // Get a list of all expected failures that did not actually fail
-    // (or that were not run).
-    std::vector<std::string> nonfailing_tests;
-    absl::c_set_difference(test_names, test_failures_,
-                           std::back_inserter(nonfailing_tests));
-
-    // Get a list of all "expected failures" that were never actually run.
-    std::vector<std::string> unrun_tests;
-    absl::c_set_difference(nonfailing_tests, test_successes_,
-                           std::back_inserter(unrun_tests));
-
-    // Report when the user specified tests that were not run.
-    if (!unrun_tests.empty()) {
-      const bool tests_were_run =
-          !(test_failures_.empty() && test_successes_.empty());
-
-      // Prepare an assertion result used in the case that tests pass that were
-      // expected to fail.
-      ::testing::AssertionResult result = ::testing::AssertionFailure();
-      result << "When testing type:\n    " << type_name_
-             << "\n\nThe following tests were expected to fail but were not "
-                "run";
-
-      if (tests_were_run) result << " (was the test name spelled correctly?)";
-
-      result << ":\n\n";
-
-      // List all of the tests that unexpectedly passed.
-      for (const auto& test_name : unrun_tests) {
-        result << "    " << test_name << "\n";
-      }
-
-      if (!tests_were_run) result << "\nNo tests were run.";
-
-      if (!test_failures_.empty()) {
-        // List test failures
-        result << "\nThe tests that were run and failed are:\n\n";
-        for (const auto& test_name : test_failures_) {
-          result << "    " << test_name << "\n";
-        }
-      }
-
-      if (!test_successes_.empty()) {
-        // List test successes
-        result << "\nThe tests that were run and succeeded are:\n\n";
-        for (const auto& test_name : test_successes_) {
-          result << "    " << test_name << "\n";
-        }
-      }
-
-      return result;
-    }
-
-    // If some tests passed when they were expected to fail, alert the caller.
-    if (nonfailing_tests.empty()) return ::testing::AssertionSuccess();
-
-    // Prepare an assertion result used in the case that tests pass that were
-    // expected to fail.
-    ::testing::AssertionResult unexpected_successes =
-        ::testing::AssertionFailure();
-    unexpected_successes << "When testing type:\n    " << type_name_
-                         << "\n\nThe following tests passed when they were "
-                            "expected to fail:\n\n";
-
-    // List all of the tests that unexpectedly passed.
-    for (const auto& test_name : nonfailing_tests) {
-      unexpected_successes << "    " << test_name << "\n";
-    }
-
-    return unexpected_successes;
-  }
-
- private:
-  void outputDivider() {
-    assertion_result_ << "========================================";
-  }
-
-  void addTestFailureImpl() {}
-
-  template <class H, class... T>
-  void addTestFailureImpl(const H& head, const T&... tail) {
-    assertion_result_ << head;
-    addTestFailureImpl(tail...);
-  }
-
-  ::testing::AssertionResult assertion_result_;
-  std::set<std::string> test_failures_;
-  std::set<std::string> test_successes_;
-  std::string type_name_;
-  bool has_error_ = false;
-};
-
-template <class T, class /*Enabler*/ = void>
-struct PropertiesOfImpl {};
-
-template <class T>
-struct PropertiesOfImpl<T, absl::void_t<typename T::properties>> {
-  using type = typename T::properties;
-};
-
-template <class T>
-struct PropertiesOfImpl<T, absl::void_t<typename T::profile_alias_of>> {
-  using type = typename PropertiesOfImpl<typename T::profile_alias_of>::type;
-};
-
-template <class T>
-struct PropertiesOf : PropertiesOfImpl<T> {};
-
-template <class T>
-using PropertiesOfT = typename PropertiesOf<T>::type;
-
-// NOTE: These enums use this naming convention to be consistent with the
-// standard trait names, which is useful since it allows us to match up each
-// enum name with a corresponding trait name in macro definitions.
-
-// An enum that describes the various expectations on an operations existence.
-enum class function_support { maybe, yes, nothrow, trivial };
-
-constexpr const char* PessimisticPropertyDescription(function_support v) {
-  return v == function_support::maybe
-             ? "no"
-             : v == function_support::yes
-                   ? "yes, potentially throwing"
-                   : v == function_support::nothrow ? "yes, nothrow"
-                                                    : "yes, trivial";
-}
-
-// Return a string that describes the kind of property support that was
-// expected.
-inline std::string ExpectedFunctionKindList(function_support min,
-                                            function_support max) {
-  if (min == max) {
-    std::string result =
-        absl::StrCat("Expected:\n  ",
-                     PessimisticPropertyDescription(
-                         static_cast<function_support>(UnderlyingValue(min))),
-                     "\n");
-    return result;
-  }
-
-  std::string result = "Expected one of:\n";
-  for (auto curr_support = UnderlyingValue(min);
-       curr_support <= UnderlyingValue(max); ++curr_support) {
-    absl::StrAppend(&result, "  ",
-                    PessimisticPropertyDescription(
-                        static_cast<function_support>(curr_support)),
-                    "\n");
-  }
-
-  return result;
-}
-
-template <class Enum>
-void ExpectModelOfImpl(ConformanceErrors* errors, Enum min_support,
-                       Enum max_support, Enum kind) {
-  const auto kind_value = UnderlyingValue(kind);
-  const auto min_support_value = UnderlyingValue(min_support);
-  const auto max_support_value = UnderlyingValue(max_support);
-
-  if (!(kind_value >= min_support_value && kind_value <= max_support_value)) {
-    errors->addTestFailure(
-        PropertyName(kind), "**Failed property expectation**\n\n",
-        ExpectedFunctionKindList(
-            static_cast<function_support>(min_support_value),
-            static_cast<function_support>(max_support_value)),
-        '\n', "Actual:\n  ",
-        PessimisticPropertyDescription(
-            static_cast<function_support>(kind_value)));
-  } else {
-    errors->addTestSuccess(PropertyName(kind));
-  }
-}
-
-#define ABSL_INTERNAL_SPECIAL_MEMBER_FUNCTION_ENUM(description, name) \
-  enum class name { maybe, yes, nothrow, trivial };                   \
-                                                                      \
-  constexpr const char* PropertyName(name v) { return description; }  \
-  static_assert(true, "")  // Force a semicolon when using this macro.
-
-ABSL_INTERNAL_SPECIAL_MEMBER_FUNCTION_ENUM("support for default construction",
-                                           default_constructible);
-ABSL_INTERNAL_SPECIAL_MEMBER_FUNCTION_ENUM("support for move construction",
-                                           move_constructible);
-ABSL_INTERNAL_SPECIAL_MEMBER_FUNCTION_ENUM("support for copy construction",
-                                           copy_constructible);
-ABSL_INTERNAL_SPECIAL_MEMBER_FUNCTION_ENUM("support for move assignment",
-                                           move_assignable);
-ABSL_INTERNAL_SPECIAL_MEMBER_FUNCTION_ENUM("support for copy assignment",
-                                           copy_assignable);
-ABSL_INTERNAL_SPECIAL_MEMBER_FUNCTION_ENUM("support for destruction",
-                                           destructible);
-
-#undef ABSL_INTERNAL_SPECIAL_MEMBER_FUNCTION_ENUM
-
-#define ABSL_INTERNAL_INTRINSIC_FUNCTION_ENUM(description, name)     \
-  enum class name { maybe, yes, nothrow };                           \
-                                                                     \
-  constexpr const char* PropertyName(name v) { return description; } \
-  static_assert(true, "")  // Force a semicolon when using this macro.
-
-ABSL_INTERNAL_INTRINSIC_FUNCTION_ENUM("support for ==", equality_comparable);
-ABSL_INTERNAL_INTRINSIC_FUNCTION_ENUM("support for !=", inequality_comparable);
-ABSL_INTERNAL_INTRINSIC_FUNCTION_ENUM("support for <", less_than_comparable);
-ABSL_INTERNAL_INTRINSIC_FUNCTION_ENUM("support for <=", less_equal_comparable);
-ABSL_INTERNAL_INTRINSIC_FUNCTION_ENUM("support for >=",
-                                      greater_equal_comparable);
-ABSL_INTERNAL_INTRINSIC_FUNCTION_ENUM("support for >", greater_than_comparable);
-
-ABSL_INTERNAL_INTRINSIC_FUNCTION_ENUM("support for swap", swappable);
-
-#undef ABSL_INTERNAL_INTRINSIC_FUNCTION_ENUM
-
-enum class hashable { maybe, yes };
-
-constexpr const char* PropertyName(hashable v) {
-  return "support for std::hash";
-}
-
-template <class T>
-using AlwaysFalse = std::false_type;
-
-#define ABSL_INTERNAL_PESSIMISTIC_MODEL_OF_SPECIAL_MEMBER(name, property)   \
-  template <class T>                                                        \
-  constexpr property property##_support_of() {                              \
-    return std::is_##property<T>::value                                     \
-               ? std::is_nothrow_##property<T>::value                       \
-                     ? absl::is_trivially_##property<T>::value              \
-                           ? property::trivial                              \
-                           : property::nothrow                              \
-                     : property::yes                                        \
-               : property::maybe;                                           \
-  }                                                                         \
-                                                                            \
-  template <class T, class MinProf, class MaxProf>                          \
-  void ExpectModelOf##name(ConformanceErrors* errors) {                     \
-    (ExpectModelOfImpl)(errors, PropertiesOfT<MinProf>::property##_support, \
-                        PropertiesOfT<MaxProf>::property##_support,         \
-                        property##_support_of<T>());                        \
-  }
-
-ABSL_INTERNAL_PESSIMISTIC_MODEL_OF_SPECIAL_MEMBER(DefaultConstructible,
-                                                  default_constructible);
-
-ABSL_INTERNAL_PESSIMISTIC_MODEL_OF_SPECIAL_MEMBER(MoveConstructible,
-                                                  move_constructible);
-
-ABSL_INTERNAL_PESSIMISTIC_MODEL_OF_SPECIAL_MEMBER(CopyConstructible,
-                                                  copy_constructible);
-
-ABSL_INTERNAL_PESSIMISTIC_MODEL_OF_SPECIAL_MEMBER(MoveAssignable,
-                                                  move_assignable);
-
-ABSL_INTERNAL_PESSIMISTIC_MODEL_OF_SPECIAL_MEMBER(CopyAssignable,
-                                                  copy_assignable);
-
-ABSL_INTERNAL_PESSIMISTIC_MODEL_OF_SPECIAL_MEMBER(Destructible, destructible);
-
-#undef ABSL_INTERNAL_PESSIMISTIC_MODEL_OF_SPECIAL_MEMBER
-
-void BoolFunction(bool) noexcept;
-
-////////////////////////////////////////////////////////////////////////////////
-//
-// A metafunction for checking if an operation exists through SFINAE.
-//
-// `T` is the type to test and Op is an alias containing the expression to test.
-template <class T, template <class...> class Op, class = void>
-struct IsOpableImpl : std::false_type {};
-
-template <class T, template <class...> class Op>
-struct IsOpableImpl<T, Op, absl::void_t<Op<T>>> : std::true_type {};
-
-template <template <class...> class Op>
-struct IsOpable {
-  template <class T>
-  using apply = typename IsOpableImpl<T, Op>::type;
-};
-//
-////////////////////////////////////////////////////////////////////////////////
-
-////////////////////////////////////////////////////////////////////////////////
-//
-// A metafunction for checking if an operation exists and is also noexcept
-// through SFINAE and the noexcept operator.
-///
-// `T` is the type to test and Op is an alias containing the expression to test.
-template <class T, template <class...> class Op, class = void>
-struct IsNothrowOpableImpl : std::false_type {};
-
-template <class T, template <class...> class Op>
-struct IsNothrowOpableImpl<T, Op, absl::enable_if_t<Op<T>::value>>
-    : std::true_type {};
-
-template <template <class...> class Op>
-struct IsNothrowOpable {
-  template <class T>
-  using apply = typename IsNothrowOpableImpl<T, Op>::type;
-};
-//
-////////////////////////////////////////////////////////////////////////////////
-
-////////////////////////////////////////////////////////////////////////////////
-//
-// A macro that produces the necessary function for reporting what kind of
-// support a specific comparison operation has and a function for reporting an
-// error if a given type's support for that operation does not meet the expected
-// requirements.
-#define ABSL_INTERNAL_PESSIMISTIC_MODEL_OF_COMPARISON(name, property, op)      \
-  template <class T,                                                           \
-            class Result = std::integral_constant<                             \
-                bool, noexcept((BoolFunction)(std::declval<const T&>() op      \
-                                                  std::declval<const T&>()))>> \
-  using name = Result;                                                         \
-                                                                               \
-  template <class T>                                                           \
-  constexpr property property##_support_of() {                                 \
-    return IsOpable<name>::apply<T>::value                                     \
-               ? IsNothrowOpable<name>::apply<T>::value ? property::nothrow    \
-                                                        : property::yes        \
-               : property::maybe;                                              \
-  }                                                                            \
-                                                                               \
-  template <class T, class MinProf, class MaxProf>                             \
-  void ExpectModelOf##name(ConformanceErrors* errors) {                        \
-    (ExpectModelOfImpl)(errors, PropertiesOfT<MinProf>::property##_support,    \
-                        PropertiesOfT<MaxProf>::property##_support,            \
-                        property##_support_of<T>());                           \
-  }
-//
-////////////////////////////////////////////////////////////////////////////////
-
-////////////////////////////////////////////////////////////////////////////////
-//
-// Generate the necessary support-checking and error reporting functions for
-// each of the comparison operators.
-ABSL_INTERNAL_PESSIMISTIC_MODEL_OF_COMPARISON(EqualityComparable,
-                                              equality_comparable, ==);
-
-ABSL_INTERNAL_PESSIMISTIC_MODEL_OF_COMPARISON(InequalityComparable,
-                                              inequality_comparable, !=);
-
-ABSL_INTERNAL_PESSIMISTIC_MODEL_OF_COMPARISON(LessThanComparable,
-                                              less_than_comparable, <);
-
-ABSL_INTERNAL_PESSIMISTIC_MODEL_OF_COMPARISON(LessEqualComparable,
-                                              less_equal_comparable, <=);
-
-ABSL_INTERNAL_PESSIMISTIC_MODEL_OF_COMPARISON(GreaterEqualComparable,
-                                              greater_equal_comparable, >=);
-
-ABSL_INTERNAL_PESSIMISTIC_MODEL_OF_COMPARISON(GreaterThanComparable,
-                                              greater_than_comparable, >);
-
-#undef ABSL_INTERNAL_PESSIMISTIC_MODEL_OF_COMPARISON
-//
-////////////////////////////////////////////////////////////////////////////////
-
-////////////////////////////////////////////////////////////////////////////////
-//
-// The necessary support-checking and error-reporting functions for swap.
-template <class T>
-constexpr swappable swappable_support_of() {
-  return type_traits_internal::IsSwappable<T>::value
-             ? type_traits_internal::IsNothrowSwappable<T>::value
-                   ? swappable::nothrow
-                   : swappable::yes
-             : swappable::maybe;
-}
-
-template <class T, class MinProf, class MaxProf>
-void ExpectModelOfSwappable(ConformanceErrors* errors) {
-  (ExpectModelOfImpl)(errors, PropertiesOfT<MinProf>::swappable_support,
-                      PropertiesOfT<MaxProf>::swappable_support,
-                      swappable_support_of<T>());
-}
-//
-////////////////////////////////////////////////////////////////////////////////
-
-////////////////////////////////////////////////////////////////////////////////
-//
-// The necessary support-checking and error-reporting functions for std::hash.
-template <class T>
-constexpr hashable hashable_support_of() {
-  return type_traits_internal::IsHashable<T>::value ? hashable::yes
-                                                    : hashable::maybe;
-}
-
-template <class T, class MinProf, class MaxProf>
-void ExpectModelOfHashable(ConformanceErrors* errors) {
-  (ExpectModelOfImpl)(errors, PropertiesOfT<MinProf>::hashable_support,
-                      PropertiesOfT<MaxProf>::hashable_support,
-                      hashable_support_of<T>());
-}
-//
-////////////////////////////////////////////////////////////////////////////////
-
-template <
-    default_constructible DefaultConstructibleValue =
-        default_constructible::maybe,
-    move_constructible MoveConstructibleValue = move_constructible::maybe,
-    copy_constructible CopyConstructibleValue = copy_constructible::maybe,
-    move_assignable MoveAssignableValue = move_assignable::maybe,
-    copy_assignable CopyAssignableValue = copy_assignable::maybe,
-    destructible DestructibleValue = destructible::maybe,
-    equality_comparable EqualityComparableValue = equality_comparable::maybe,
-    inequality_comparable InequalityComparableValue =
-        inequality_comparable::maybe,
-    less_than_comparable LessThanComparableValue = less_than_comparable::maybe,
-    less_equal_comparable LessEqualComparableValue =
-        less_equal_comparable::maybe,
-    greater_equal_comparable GreaterEqualComparableValue =
-        greater_equal_comparable::maybe,
-    greater_than_comparable GreaterThanComparableValue =
-        greater_than_comparable::maybe,
-    swappable SwappableValue = swappable::maybe,
-    hashable HashableValue = hashable::maybe>
-struct ConformanceProfile {
-  using properties = ConformanceProfile;
-
-  static constexpr default_constructible
-      default_constructible_support =  // NOLINT
-      DefaultConstructibleValue;
-
-  static constexpr move_constructible move_constructible_support =  // NOLINT
-      MoveConstructibleValue;
-
-  static constexpr copy_constructible copy_constructible_support =  // NOLINT
-      CopyConstructibleValue;
-
-  static constexpr move_assignable move_assignable_support =  // NOLINT
-      MoveAssignableValue;
-
-  static constexpr copy_assignable copy_assignable_support =  // NOLINT
-      CopyAssignableValue;
-
-  static constexpr destructible destructible_support =  // NOLINT
-      DestructibleValue;
-
-  static constexpr equality_comparable equality_comparable_support =  // NOLINT
-      EqualityComparableValue;
-
-  static constexpr inequality_comparable
-      inequality_comparable_support =  // NOLINT
-      InequalityComparableValue;
-
-  static constexpr less_than_comparable
-      less_than_comparable_support =  // NOLINT
-      LessThanComparableValue;
-
-  static constexpr less_equal_comparable
-      less_equal_comparable_support =  // NOLINT
-      LessEqualComparableValue;
-
-  static constexpr greater_equal_comparable
-      greater_equal_comparable_support =  // NOLINT
-      GreaterEqualComparableValue;
-
-  static constexpr greater_than_comparable
-      greater_than_comparable_support =  // NOLINT
-      GreaterThanComparableValue;
-
-  static constexpr swappable swappable_support = SwappableValue;  // NOLINT
-
-  static constexpr hashable hashable_support = HashableValue;  // NOLINT
-
-  static constexpr bool is_default_constructible =  // NOLINT
-      DefaultConstructibleValue != default_constructible::maybe;
-
-  static constexpr bool is_move_constructible =  // NOLINT
-      MoveConstructibleValue != move_constructible::maybe;
-
-  static constexpr bool is_copy_constructible =  // NOLINT
-      CopyConstructibleValue != copy_constructible::maybe;
-
-  static constexpr bool is_move_assignable =  // NOLINT
-      MoveAssignableValue != move_assignable::maybe;
-
-  static constexpr bool is_copy_assignable =  // NOLINT
-      CopyAssignableValue != copy_assignable::maybe;
-
-  static constexpr bool is_destructible =  // NOLINT
-      DestructibleValue != destructible::maybe;
-
-  static constexpr bool is_equality_comparable =  // NOLINT
-      EqualityComparableValue != equality_comparable::maybe;
-
-  static constexpr bool is_inequality_comparable =  // NOLINT
-      InequalityComparableValue != inequality_comparable::maybe;
-
-  static constexpr bool is_less_than_comparable =  // NOLINT
-      LessThanComparableValue != less_than_comparable::maybe;
-
-  static constexpr bool is_less_equal_comparable =  // NOLINT
-      LessEqualComparableValue != less_equal_comparable::maybe;
-
-  static constexpr bool is_greater_equal_comparable =  // NOLINT
-      GreaterEqualComparableValue != greater_equal_comparable::maybe;
-
-  static constexpr bool is_greater_than_comparable =  // NOLINT
-      GreaterThanComparableValue != greater_than_comparable::maybe;
-
-  static constexpr bool is_swappable =  // NOLINT
-      SwappableValue != swappable::maybe;
-
-  static constexpr bool is_hashable =  // NOLINT
-      HashableValue != hashable::maybe;
-};
-
-////////////////////////////////////////////////////////////////////////////////
-//
-// Compliant SFINAE-friendliness is not always present on the standard library
-// implementations that we support. This helper-struct (and associated enum) is
-// used as a means to conditionally check the hashability support of a type.
-enum class CheckHashability { no, yes };
-
-template <class T, CheckHashability ShouldCheckHashability>
-struct conservative_hashable_support_of;
-
-template <class T>
-struct conservative_hashable_support_of<T, CheckHashability::no> {
-  static constexpr hashable Invoke() { return hashable::maybe; }
-};
-
-template <class T>
-struct conservative_hashable_support_of<T, CheckHashability::yes> {
-  static constexpr hashable Invoke() { return hashable_support_of<T>(); }
-};
-//
-////////////////////////////////////////////////////////////////////////////////
-
-// The ConformanceProfile that is expected based on introspection into the type
-// by way of trait checks.
-template <class T, CheckHashability ShouldCheckHashability>
-struct SyntacticConformanceProfileOf {
-  using properties = ConformanceProfile<
-      default_constructible_support_of<T>(), move_constructible_support_of<T>(),
-      copy_constructible_support_of<T>(), move_assignable_support_of<T>(),
-      copy_assignable_support_of<T>(), destructible_support_of<T>(),
-      equality_comparable_support_of<T>(),
-      inequality_comparable_support_of<T>(),
-      less_than_comparable_support_of<T>(),
-      less_equal_comparable_support_of<T>(),
-      greater_equal_comparable_support_of<T>(),
-      greater_than_comparable_support_of<T>(), swappable_support_of<T>(),
-      conservative_hashable_support_of<T, ShouldCheckHashability>::Invoke()>;
-};
-
-#define ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF_IMPL(type, name)     \
-  template <default_constructible DefaultConstructibleValue,                   \
-            move_constructible MoveConstructibleValue,                         \
-            copy_constructible CopyConstructibleValue,                         \
-            move_assignable MoveAssignableValue,                               \
-            copy_assignable CopyAssignableValue,                               \
-            destructible DestructibleValue,                                    \
-            equality_comparable EqualityComparableValue,                       \
-            inequality_comparable InequalityComparableValue,                   \
-            less_than_comparable LessThanComparableValue,                      \
-            less_equal_comparable LessEqualComparableValue,                    \
-            greater_equal_comparable GreaterEqualComparableValue,              \
-            greater_than_comparable GreaterThanComparableValue,                \
-            swappable SwappableValue, hashable HashableValue>                  \
-  constexpr type ConformanceProfile<                                           \
-      DefaultConstructibleValue, MoveConstructibleValue,                       \
-      CopyConstructibleValue, MoveAssignableValue, CopyAssignableValue,        \
-      DestructibleValue, EqualityComparableValue, InequalityComparableValue,   \
-      LessThanComparableValue, LessEqualComparableValue,                       \
-      GreaterEqualComparableValue, GreaterThanComparableValue, SwappableValue, \
-      HashableValue>::name
-
-#define ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(type)           \
-  ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF_IMPL(type,            \
-                                                         type##_support); \
-  ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF_IMPL(bool, is_##type)
-
-#ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL
-ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(default_constructible);
-ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(move_constructible);
-ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(copy_constructible);
-ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(move_assignable);
-ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(copy_assignable);
-ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(destructible);
-ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(equality_comparable);
-ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(inequality_comparable);
-ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(less_than_comparable);
-ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(less_equal_comparable);
-ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(greater_equal_comparable);
-ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(greater_than_comparable);
-ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(swappable);
-ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(hashable);
-#endif
-
-#undef ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF
-#undef ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF_IMPL
-
-// Retrieve the enum with the minimum underlying value.
-// Note: std::min is not constexpr in C++11, which is why this is necessary.
-template <class H>
-constexpr H MinEnum(H head) {
-  return head;
-}
-
-template <class H, class N, class... T>
-constexpr H MinEnum(H head, N next, T... tail) {
-  return (UnderlyingValue)(head) < (UnderlyingValue)(next)
-             ? (MinEnum)(head, tail...)
-             : (MinEnum)(next, tail...);
-}
-
-template <class... Profs>
-struct MinimalProfiles {
-  static constexpr default_constructible
-      default_constructible_support =  // NOLINT
-      (MinEnum)(PropertiesOfT<Profs>::default_constructible_support...);
-
-  static constexpr move_constructible move_constructible_support =  // NOLINT
-      (MinEnum)(PropertiesOfT<Profs>::move_constructible_support...);
-
-  static constexpr copy_constructible copy_constructible_support =  // NOLINT
-      (MinEnum)(PropertiesOfT<Profs>::copy_constructible_support...);
-
-  static constexpr move_assignable move_assignable_support =  // NOLINT
-      (MinEnum)(PropertiesOfT<Profs>::move_assignable_support...);
-
-  static constexpr copy_assignable copy_assignable_support =  // NOLINT
-      (MinEnum)(PropertiesOfT<Profs>::copy_assignable_support...);
-
-  static constexpr destructible destructible_support =  // NOLINT
-      (MinEnum)(PropertiesOfT<Profs>::destructible_support...);
-
-  static constexpr equality_comparable equality_comparable_support =  // NOLINT
-      (MinEnum)(PropertiesOfT<Profs>::equality_comparable_support...);
-
-  static constexpr inequality_comparable
-      inequality_comparable_support =  // NOLINT
-      (MinEnum)(PropertiesOfT<Profs>::inequality_comparable_support...);
-
-  static constexpr less_than_comparable
-      less_than_comparable_support =  // NOLINT
-      (MinEnum)(PropertiesOfT<Profs>::less_than_comparable_support...);
-
-  static constexpr less_equal_comparable
-      less_equal_comparable_support =  // NOLINT
-      (MinEnum)(PropertiesOfT<Profs>::less_equal_comparable_support...);
-
-  static constexpr greater_equal_comparable
-      greater_equal_comparable_support =  // NOLINT
-      (MinEnum)(PropertiesOfT<Profs>::greater_equal_comparable_support...);
-
-  static constexpr greater_than_comparable
-      greater_than_comparable_support =  // NOLINT
-      (MinEnum)(PropertiesOfT<Profs>::greater_than_comparable_support...);
-
-  static constexpr swappable swappable_support =  // NOLINT
-      (MinEnum)(PropertiesOfT<Profs>::swappable_support...);
-
-  static constexpr hashable hashable_support =  // NOLINT
-      (MinEnum)(PropertiesOfT<Profs>::hashable_support...);
-
-  using properties = ConformanceProfile<
-      default_constructible_support, move_constructible_support,
-      copy_constructible_support, move_assignable_support,
-      copy_assignable_support, destructible_support,
-      equality_comparable_support, inequality_comparable_support,
-      less_than_comparable_support, less_equal_comparable_support,
-      greater_equal_comparable_support, greater_than_comparable_support,
-      swappable_support, hashable_support>;
-};
-
-// Retrieve the enum with the greatest underlying value.
-// Note: std::max is not constexpr in C++11, which is why this is necessary.
-template <class H>
-constexpr H MaxEnum(H head) {
-  return head;
-}
-
-template <class H, class N, class... T>
-constexpr H MaxEnum(H head, N next, T... tail) {
-  return (UnderlyingValue)(next) < (UnderlyingValue)(head)
-             ? (MaxEnum)(head, tail...)
-             : (MaxEnum)(next, tail...);
-}
-
-template <class... Profs>
-struct CombineProfilesImpl {
-  static constexpr default_constructible
-      default_constructible_support =  // NOLINT
-      (MaxEnum)(PropertiesOfT<Profs>::default_constructible_support...);
-
-  static constexpr move_constructible move_constructible_support =  // NOLINT
-      (MaxEnum)(PropertiesOfT<Profs>::move_constructible_support...);
-
-  static constexpr copy_constructible copy_constructible_support =  // NOLINT
-      (MaxEnum)(PropertiesOfT<Profs>::copy_constructible_support...);
-
-  static constexpr move_assignable move_assignable_support =  // NOLINT
-      (MaxEnum)(PropertiesOfT<Profs>::move_assignable_support...);
-
-  static constexpr copy_assignable copy_assignable_support =  // NOLINT
-      (MaxEnum)(PropertiesOfT<Profs>::copy_assignable_support...);
-
-  static constexpr destructible destructible_support =  // NOLINT
-      (MaxEnum)(PropertiesOfT<Profs>::destructible_support...);
-
-  static constexpr equality_comparable equality_comparable_support =  // NOLINT
-      (MaxEnum)(PropertiesOfT<Profs>::equality_comparable_support...);
-
-  static constexpr inequality_comparable
-      inequality_comparable_support =  // NOLINT
-      (MaxEnum)(PropertiesOfT<Profs>::inequality_comparable_support...);
-
-  static constexpr less_than_comparable
-      less_than_comparable_support =  // NOLINT
-      (MaxEnum)(PropertiesOfT<Profs>::less_than_comparable_support...);
-
-  static constexpr less_equal_comparable
-      less_equal_comparable_support =  // NOLINT
-      (MaxEnum)(PropertiesOfT<Profs>::less_equal_comparable_support...);
-
-  static constexpr greater_equal_comparable
-      greater_equal_comparable_support =  // NOLINT
-      (MaxEnum)(PropertiesOfT<Profs>::greater_equal_comparable_support...);
-
-  static constexpr greater_than_comparable
-      greater_than_comparable_support =  // NOLINT
-      (MaxEnum)(PropertiesOfT<Profs>::greater_than_comparable_support...);
-
-  static constexpr swappable swappable_support =  // NOLINT
-      (MaxEnum)(PropertiesOfT<Profs>::swappable_support...);
-
-  static constexpr hashable hashable_support =  // NOLINT
-      (MaxEnum)(PropertiesOfT<Profs>::hashable_support...);
-
-  using properties = ConformanceProfile<
-      default_constructible_support, move_constructible_support,
-      copy_constructible_support, move_assignable_support,
-      copy_assignable_support, destructible_support,
-      equality_comparable_support, inequality_comparable_support,
-      less_than_comparable_support, less_equal_comparable_support,
-      greater_equal_comparable_support, greater_than_comparable_support,
-      swappable_support, hashable_support>;
-};
-
-// NOTE: We use this as opposed to a direct alias of CombineProfilesImpl so that
-// when named aliases of CombineProfiles are created (such as in
-// conformance_aliases.h), we only pay for the combination algorithm on the
-// profiles that are actually used.
-template <class... Profs>
-struct CombineProfiles {
-  using profile_alias_of = CombineProfilesImpl<Profs...>;
-};
-
-template <>
-struct CombineProfiles<> {
-  using properties = ConformanceProfile<>;
-};
-
-template <class Profile, class Tag>
-struct StrongProfileTypedef {
-  using properties = PropertiesOfT<Profile>;
-};
-
-template <class T, class /*Enabler*/ = void>
-struct IsProfileImpl : std::false_type {};
-
-template <class T>
-struct IsProfileImpl<T, absl::void_t<PropertiesOfT<T>>> : std::true_type {};
-
-template <class T>
-struct IsProfile : IsProfileImpl<T>::type {};
-
-// A tag that describes which set of properties we will check when the user
-// requires a strict match in conformance (as opposed to a loose match which
-// allows more-refined support of any given operation).
-//
-// Currently only the RegularityDomain exists and it includes all operations
-// that the conformance testing suite knows about. The intent is that if the
-// suite is expanded to support extension, such as for checking conformance of
-// concepts like Iterators or Containers, additional corresponding domains can
-// be created.
-struct RegularityDomain {};
-
-}  // namespace types_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_TYPES_INTERNAL_CONFORMANCE_PROFILE_H_
diff --git a/absl/types/internal/conformance_testing.h b/absl/types/internal/conformance_testing.h
deleted file mode 100644
index 487b0f7..0000000
--- a/absl/types/internal/conformance_testing.h
+++ /dev/null
@@ -1,1386 +0,0 @@
-// Copyright 2019 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.
-//
-// -----------------------------------------------------------------------------
-// conformance_testing.h
-// -----------------------------------------------------------------------------
-//
-
-#ifndef ABSL_TYPES_INTERNAL_CONFORMANCE_TESTING_H_
-#define ABSL_TYPES_INTERNAL_CONFORMANCE_TESTING_H_
-
-////////////////////////////////////////////////////////////////////////////////
-//                                                                            //
-// Many templates in this file take a `T` and a `Prof` type as explicit       //
-// template arguments. These are a type to be checked and a                   //
-// "Regularity Profile" that describes what operations that type `T` is       //
-// expected to support. See "regularity_profiles.h" for more details          //
-// regarding Regularity Profiles.                                             //
-//                                                                            //
-////////////////////////////////////////////////////////////////////////////////
-
-#include <cstddef>
-#include <set>
-#include <tuple>
-#include <type_traits>
-#include <utility>
-
-#include "gtest/gtest.h"
-#include "absl/meta/type_traits.h"
-#include "absl/strings/ascii.h"
-#include "absl/strings/str_cat.h"
-#include "absl/strings/string_view.h"
-#include "absl/types/internal/conformance_aliases.h"
-#include "absl/types/internal/conformance_archetype.h"
-#include "absl/types/internal/conformance_profile.h"
-#include "absl/types/internal/conformance_testing_helpers.h"
-#include "absl/types/internal/parentheses.h"
-#include "absl/types/internal/transform_args.h"
-#include "absl/utility/utility.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace types_internal {
-
-// Returns true if the compiler incorrectly greedily instantiates constexpr
-// templates in any unevaluated context.
-constexpr bool constexpr_instantiation_when_unevaluated() {
-#if defined(__apple_build_version__)  // TODO(calabrese) Make more specific
-  return true;
-#elif defined(__clang__)
-  return __clang_major__ < 4;
-#elif defined(__GNUC__)
-  // TODO(calabrese) Figure out why gcc 7 fails (seems like a different bug)
-  return __GNUC__ < 5 || (__GNUC__ == 5 && __GNUC_MINOR__ < 2) || __GNUC__ >= 7;
-#else
-  return false;
-#endif
-}
-
-// Returns true if the standard library being used incorrectly produces an error
-// when instantiating the definition of a poisoned std::hash specialization.
-constexpr bool poisoned_hash_fails_instantiation() {
-#if defined(_MSC_VER) && !defined(_LIBCPP_VERSION)
-  return _MSC_VER < 1914;
-#else
-  return false;
-#endif
-}
-
-template <class Fun>
-struct GeneratorType {
-  decltype(std::declval<const Fun&>()()) operator()() const
-      noexcept(noexcept(std::declval<const Fun&>()())) {
-    return fun();
-  }
-
-  Fun fun;
-  const char* description;
-};
-
-// A "make" function for the GeneratorType template that deduces the function
-// object type.
-template <class Fun,
-          absl::enable_if_t<IsNullaryCallable<Fun>::value>** = nullptr>
-GeneratorType<Fun> Generator(Fun fun, const char* description) {
-  return GeneratorType<Fun>{absl::move(fun), description};
-}
-
-// A type that contains a set of nullary function objects that each return an
-// instance of the same type and value (though possibly different
-// representations, such as +0 and -0 or two vectors with the same elements but
-// with different capacities).
-template <class... Funs>
-struct EquivalenceClassType {
-  std::tuple<GeneratorType<Funs>...> generators;
-};
-
-// A "make" function for the EquivalenceClassType template that deduces the
-// function object types and is constrained such that a user can only pass in
-// function objects that all have the same return type.
-template <class... Funs, absl::enable_if_t<AreGeneratorsWithTheSameReturnType<
-                             Funs...>::value>** = nullptr>
-EquivalenceClassType<Funs...> EquivalenceClass(GeneratorType<Funs>... funs) {
-  return {std::make_tuple(absl::move(funs)...)};
-}
-
-// A type that contains an ordered series of EquivalenceClassTypes, from
-// smallest value to largest value.
-template <class... EqClasses>
-struct OrderedEquivalenceClasses {
-  std::tuple<EqClasses...> eq_classes;
-};
-
-// An object containing the parts of a given (name, initialization expression),
-// and is capable of generating a string that describes the given.
-struct GivenDeclaration {
-  std::string outputDeclaration(std::size_t width) const {
-    const std::size_t indent_size = 2;
-    std::string result = absl::StrCat("  ", name);
-
-    if (!expression.empty()) {
-      // Indent
-      result.resize(indent_size + width, ' ');
-      absl::StrAppend(&result, " = ", expression, ";\n");
-    } else {
-      absl::StrAppend(&result, ";\n");
-    }
-
-    return result;
-  }
-
-  std::string name;
-  std::string expression;
-};
-
-// Produce a string that contains all of the givens of an error report.
-template <class... Decls>
-std::string PrepareGivenContext(const Decls&... decls) {
-  const std::size_t width = (std::max)({decls.name.size()...});
-  return absl::StrCat("Given:\n", decls.outputDeclaration(width)..., "\n");
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Function objects that perform a check for each comparison operator         //
-////////////////////////////////////////////////////////////////////////////////
-
-#define ABSL_INTERNAL_EXPECT_OP(name, op)                                   \
-  struct Expect##name {                                                     \
-    template <class T>                                                      \
-    void operator()(absl::string_view test_name, absl::string_view context, \
-                    const T& lhs, const T& rhs, absl::string_view lhs_name, \
-                    absl::string_view rhs_name) const {                     \
-      if (!static_cast<bool>(lhs op rhs)) {                                 \
-        errors->addTestFailure(                                             \
-            test_name, absl::StrCat(context,                                \
-                                    "**Unexpected comparison result**\n"    \
-                                    "\n"                                    \
-                                    "Expression:\n"                         \
-                                    "  ",                                   \
-                                    lhs_name, " " #op " ", rhs_name,        \
-                                    "\n"                                    \
-                                    "\n"                                    \
-                                    "Expected: true\n"                      \
-                                    "  Actual: false"));                    \
-      } else {                                                              \
-        errors->addTestSuccess(test_name);                                  \
-      }                                                                     \
-    }                                                                       \
-                                                                            \
-    ConformanceErrors* errors;                                              \
-  };                                                                        \
-                                                                            \
-  struct ExpectNot##name {                                                  \
-    template <class T>                                                      \
-    void operator()(absl::string_view test_name, absl::string_view context, \
-                    const T& lhs, const T& rhs, absl::string_view lhs_name, \
-                    absl::string_view rhs_name) const {                     \
-      if (lhs op rhs) {                                                     \
-        errors->addTestFailure(                                             \
-            test_name, absl::StrCat(context,                                \
-                                    "**Unexpected comparison result**\n"    \
-                                    "\n"                                    \
-                                    "Expression:\n"                         \
-                                    "  ",                                   \
-                                    lhs_name, " " #op " ", rhs_name,        \
-                                    "\n"                                    \
-                                    "\n"                                    \
-                                    "Expected: false\n"                     \
-                                    "  Actual: true"));                     \
-      } else {                                                              \
-        errors->addTestSuccess(test_name);                                  \
-      }                                                                     \
-    }                                                                       \
-                                                                            \
-    ConformanceErrors* errors;                                              \
-  }
-
-ABSL_INTERNAL_EXPECT_OP(Eq, ==);
-ABSL_INTERNAL_EXPECT_OP(Ne, !=);
-ABSL_INTERNAL_EXPECT_OP(Lt, <);
-ABSL_INTERNAL_EXPECT_OP(Le, <=);
-ABSL_INTERNAL_EXPECT_OP(Ge, >=);
-ABSL_INTERNAL_EXPECT_OP(Gt, >);
-
-#undef ABSL_INTERNAL_EXPECT_OP
-
-// A function object that verifies that two objects hash to the same value by
-// way of the std::hash specialization.
-struct ExpectSameHash {
-  template <class T>
-  void operator()(absl::string_view test_name, absl::string_view context,
-                  const T& lhs, const T& rhs, absl::string_view lhs_name,
-                  absl::string_view rhs_name) const {
-    if (std::hash<T>()(lhs) != std::hash<T>()(rhs)) {
-      errors->addTestFailure(
-          test_name, absl::StrCat(context,
-                                  "**Unexpected hash result**\n"
-                                  "\n"
-                                  "Expression:\n"
-                                  "  std::hash<T>()(",
-                                  lhs_name, ") == std::hash<T>()(", rhs_name,
-                                  ")\n"
-                                  "\n"
-                                  "Expected: true\n"
-                                  "  Actual: false"));
-    } else {
-      errors->addTestSuccess(test_name);
-    }
-  }
-
-  ConformanceErrors* errors;
-};
-
-// A function template that takes two objects and verifies that each comparison
-// operator behaves in a way that is consistent with equality. It has "OneWay"
-// in the name because the first argument will always be the left-hand operand
-// of the corresponding comparison operator and the second argument will
-// always be the right-hand operand. It will never switch that order.
-// At a higher level in the test suite, the one-way form is called once for each
-// of the two possible orders whenever lhs and rhs are not the same initializer.
-template <class T, class Prof>
-void ExpectOneWayEquality(ConformanceErrors* errors,
-                          absl::string_view test_name,
-                          absl::string_view context, const T& lhs, const T& rhs,
-                          absl::string_view lhs_name,
-                          absl::string_view rhs_name) {
-  If<PropertiesOfT<Prof>::is_equality_comparable>::Invoke(
-      ExpectEq{errors}, test_name, context, lhs, rhs, lhs_name, rhs_name);
-
-  If<PropertiesOfT<Prof>::is_inequality_comparable>::Invoke(
-      ExpectNotNe{errors}, test_name, context, lhs, rhs, lhs_name, rhs_name);
-
-  If<PropertiesOfT<Prof>::is_less_than_comparable>::Invoke(
-      ExpectNotLt{errors}, test_name, context, lhs, rhs, lhs_name, rhs_name);
-
-  If<PropertiesOfT<Prof>::is_less_equal_comparable>::Invoke(
-      ExpectLe{errors}, test_name, context, lhs, rhs, lhs_name, rhs_name);
-
-  If<PropertiesOfT<Prof>::is_greater_equal_comparable>::Invoke(
-      ExpectGe{errors}, test_name, context, lhs, rhs, lhs_name, rhs_name);
-
-  If<PropertiesOfT<Prof>::is_greater_than_comparable>::Invoke(
-      ExpectNotGt{errors}, test_name, context, lhs, rhs, lhs_name, rhs_name);
-
-  If<PropertiesOfT<Prof>::is_hashable>::Invoke(
-      ExpectSameHash{errors}, test_name, context, lhs, rhs, lhs_name, rhs_name);
-}
-
-// A function template that takes two objects and verifies that each comparison
-// operator behaves in a way that is consistent with equality. This function
-// differs from ExpectOneWayEquality in that this will do checks with argument
-// order reversed in addition to in-order.
-template <class T, class Prof>
-void ExpectEquality(ConformanceErrors* errors, absl::string_view test_name,
-                    absl::string_view context, const T& lhs, const T& rhs,
-                    absl::string_view lhs_name, absl::string_view rhs_name) {
-  (ExpectOneWayEquality<T, Prof>)(errors, test_name, context, lhs, rhs,
-                                  lhs_name, rhs_name);
-  (ExpectOneWayEquality<T, Prof>)(errors, test_name, context, rhs, lhs,
-                                  rhs_name, lhs_name);
-}
-
-// Given a generator, makes sure that a generated value and a moved-from
-// generated value are equal.
-template <class T, class Prof>
-struct ExpectMoveConstructOneGenerator {
-  template <class Fun>
-  void operator()(const Fun& generator) const {
-    const T object = generator();
-    const T moved_object = absl::move(generator());  // Force no elision.
-
-    (ExpectEquality<T, Prof>)(errors, "Move construction",
-                              PrepareGivenContext(
-                                  GivenDeclaration{"const _T object",
-                                                   generator.description},
-                                  GivenDeclaration{"const _T moved_object",
-                                                   std::string("std::move(") +
-                                                       generator.description +
-                                                       ")"}),
-                              object, moved_object, "object", "moved_object");
-  }
-
-  ConformanceErrors* errors;
-};
-
-// Given a generator, makes sure that a generated value and a copied-from
-// generated value are equal.
-template <class T, class Prof>
-struct ExpectCopyConstructOneGenerator {
-  template <class Fun>
-  void operator()(const Fun& generator) const {
-    const T object = generator();
-    const T copied_object = static_cast<const T&>(generator());
-
-    (ExpectEquality<T, Prof>)(errors, "Copy construction",
-                              PrepareGivenContext(
-                                  GivenDeclaration{"const _T object",
-                                                   generator.description},
-                                  GivenDeclaration{
-                                      "const _T copied_object",
-                                      std::string("static_cast<const _T&>(") +
-                                          generator.description + ")"}),
-                              object, copied_object, "object", "copied_object");
-  }
-
-  ConformanceErrors* errors;
-};
-
-// Default-construct and do nothing before destruction.
-//
-// This is useful in exercising the codepath of default construction followed by
-// destruction, but does not explicitly test anything. An example of where this
-// might fail is a default destructor that default-initializes a scalar and a
-// destructor reads the value of that member. Sanitizers can catch this as long
-// as our test attempts to execute such a case.
-template <class T>
-struct ExpectDefaultConstructWithDestruct {
-  void operator()() const {
-    // Scoped so that destructor gets called before reporting success.
-    {
-      T object;
-      static_cast<void>(object);
-    }
-
-    errors->addTestSuccess("Default construction");
-  }
-
-  ConformanceErrors* errors;
-};
-
-// Check move-assign into a default-constructed object.
-template <class T, class Prof>
-struct ExpectDefaultConstructWithMoveAssign {
-  template <class Fun>
-  void operator()(const Fun& generator) const {
-    const T source_of_truth = generator();
-    T object;
-    object = generator();
-
-    (ExpectEquality<T, Prof>)(errors, "Move assignment",
-                              PrepareGivenContext(
-                                  GivenDeclaration{"const _T object",
-                                                   generator.description},
-                                  GivenDeclaration{"_T object", ""},
-                                  GivenDeclaration{"object",
-                                                   generator.description}),
-                              object, source_of_truth, "std::as_const(object)",
-                              "source_of_truth");
-  }
-
-  ConformanceErrors* errors;
-};
-
-// Check copy-assign into a default-constructed object.
-template <class T, class Prof>
-struct ExpectDefaultConstructWithCopyAssign {
-  template <class Fun>
-  void operator()(const Fun& generator) const {
-    const T source_of_truth = generator();
-    T object;
-    object = static_cast<const T&>(generator());
-
-    (ExpectEquality<T, Prof>)(errors, "Copy assignment",
-                              PrepareGivenContext(
-                                  GivenDeclaration{"const _T source_of_truth",
-                                                   generator.description},
-                                  GivenDeclaration{"_T object", ""},
-                                  GivenDeclaration{
-                                      "object",
-                                      std::string("static_cast<const _T&>(") +
-                                          generator.description + ")"}),
-                              object, source_of_truth, "std::as_const(object)",
-                              "source_of_truth");
-  }
-
-  ConformanceErrors* errors;
-};
-
-// Perform a self move-assign.
-template <class T, class Prof>
-struct ExpectSelfMoveAssign {
-  template <class Fun>
-  void operator()(const Fun& generator) const {
-    T object = generator();
-    object = absl::move(object);
-
-    // NOTE: Self move-assign results in a valid-but-unspecified state.
-
-    (ExpectEquality<T, Prof>)(errors, "Move assignment",
-                              PrepareGivenContext(
-                                  GivenDeclaration{"_T object",
-                                                   generator.description},
-                                  GivenDeclaration{"object",
-                                                   "std::move(object)"}),
-                              object, object, "object", "object");
-  }
-
-  ConformanceErrors* errors;
-};
-
-// Perform a self copy-assign.
-template <class T, class Prof>
-struct ExpectSelfCopyAssign {
-  template <class Fun>
-  void operator()(const Fun& generator) const {
-    const T source_of_truth = generator();
-    T object = generator();
-    const T& const_object = object;
-    object = const_object;
-
-    (ExpectEquality<T, Prof>)(errors, "Copy assignment",
-                              PrepareGivenContext(
-                                  GivenDeclaration{"const _T source_of_truth",
-                                                   generator.description},
-                                  GivenDeclaration{"_T object",
-                                                   generator.description},
-                                  GivenDeclaration{"object",
-                                                   "std::as_const(object)"}),
-                              const_object, source_of_truth,
-                              "std::as_const(object)", "source_of_truth");
-  }
-
-  ConformanceErrors* errors;
-};
-
-// Perform a self-swap.
-template <class T, class Prof>
-struct ExpectSelfSwap {
-  template <class Fun>
-  void operator()(const Fun& generator) const {
-    const T source_of_truth = generator();
-    T object = generator();
-
-    type_traits_internal::Swap(object, object);
-
-    std::string preliminary_info = absl::StrCat(
-        PrepareGivenContext(
-            GivenDeclaration{"const _T source_of_truth", generator.description},
-            GivenDeclaration{"_T object", generator.description}),
-        "After performing a self-swap:\n"
-        "  using std::swap;\n"
-        "  swap(object, object);\n"
-        "\n");
-
-    (ExpectEquality<T, Prof>)(errors, "Swap", std::move(preliminary_info),
-                              object, source_of_truth, "std::as_const(object)",
-                              "source_of_truth");
-  }
-
-  ConformanceErrors* errors;
-};
-
-// Perform each of the single-generator checks when necessary operations are
-// supported.
-template <class T, class Prof>
-struct ExpectSelfComparison {
-  template <class Fun>
-  void operator()(const Fun& generator) const {
-    const T object = generator();
-    (ExpectOneWayEquality<T, Prof>)(errors, "Comparison",
-                                    PrepareGivenContext(GivenDeclaration{
-                                        "const _T object",
-                                        generator.description}),
-                                    object, object, "object", "object");
-  }
-
-  ConformanceErrors* errors;
-};
-
-// Perform each of the single-generator checks when necessary operations are
-// supported.
-template <class T, class Prof>
-struct ExpectConsistency {
-  template <class Fun>
-  void operator()(const Fun& generator) const {
-    If<PropertiesOfT<Prof>::is_move_constructible>::Invoke(
-        ExpectMoveConstructOneGenerator<T, Prof>{errors}, generator);
-
-    If<PropertiesOfT<Prof>::is_copy_constructible>::Invoke(
-        ExpectCopyConstructOneGenerator<T, Prof>{errors}, generator);
-
-    If<PropertiesOfT<Prof>::is_default_constructible &&
-       PropertiesOfT<Prof>::is_move_assignable>::
-        Invoke(ExpectDefaultConstructWithMoveAssign<T, Prof>{errors},
-               generator);
-
-    If<PropertiesOfT<Prof>::is_default_constructible &&
-       PropertiesOfT<Prof>::is_copy_assignable>::
-        Invoke(ExpectDefaultConstructWithCopyAssign<T, Prof>{errors},
-               generator);
-
-    If<PropertiesOfT<Prof>::is_move_assignable>::Invoke(
-        ExpectSelfMoveAssign<T, Prof>{errors}, generator);
-
-    If<PropertiesOfT<Prof>::is_copy_assignable>::Invoke(
-        ExpectSelfCopyAssign<T, Prof>{errors}, generator);
-
-    If<PropertiesOfT<Prof>::is_swappable>::Invoke(
-        ExpectSelfSwap<T, Prof>{errors}, generator);
-  }
-
-  ConformanceErrors* errors;
-};
-
-// Check move-assign with two different values.
-template <class T, class Prof>
-struct ExpectMoveAssign {
-  template <class Fun0, class Fun1>
-  void operator()(const Fun0& generator0, const Fun1& generator1) const {
-    const T source_of_truth1 = generator1();
-    T object = generator0();
-    object = generator1();
-
-    (ExpectEquality<T, Prof>)(errors, "Move assignment",
-                              PrepareGivenContext(
-                                  GivenDeclaration{"const _T source_of_truth1",
-                                                   generator1.description},
-                                  GivenDeclaration{"_T object",
-                                                   generator0.description},
-                                  GivenDeclaration{"object",
-                                                   generator1.description}),
-                              object, source_of_truth1, "std::as_const(object)",
-                              "source_of_truth1");
-  }
-
-  ConformanceErrors* errors;
-};
-
-// Check copy-assign with two different values.
-template <class T, class Prof>
-struct ExpectCopyAssign {
-  template <class Fun0, class Fun1>
-  void operator()(const Fun0& generator0, const Fun1& generator1) const {
-    const T source_of_truth1 = generator1();
-    T object = generator0();
-    object = static_cast<const T&>(generator1());
-
-    (ExpectEquality<T, Prof>)(errors, "Copy assignment",
-                              PrepareGivenContext(
-                                  GivenDeclaration{"const _T source_of_truth1",
-                                                   generator1.description},
-                                  GivenDeclaration{"_T object",
-                                                   generator0.description},
-                                  GivenDeclaration{
-                                      "object",
-                                      std::string("static_cast<const _T&>(") +
-                                          generator1.description + ")"}),
-                              object, source_of_truth1, "std::as_const(object)",
-                              "source_of_truth1");
-  }
-
-  ConformanceErrors* errors;
-};
-
-// Check swap with two different values.
-template <class T, class Prof>
-struct ExpectSwap {
-  template <class Fun0, class Fun1>
-  void operator()(const Fun0& generator0, const Fun1& generator1) const {
-    const T source_of_truth0 = generator0();
-    const T source_of_truth1 = generator1();
-    T object0 = generator0();
-    T object1 = generator1();
-
-    type_traits_internal::Swap(object0, object1);
-
-    const std::string context =
-        PrepareGivenContext(
-            GivenDeclaration{"const _T source_of_truth0",
-                             generator0.description},
-            GivenDeclaration{"const _T source_of_truth1",
-                             generator1.description},
-            GivenDeclaration{"_T object0", generator0.description},
-            GivenDeclaration{"_T object1", generator1.description}) +
-        "After performing a swap:\n"
-        "  using std::swap;\n"
-        "  swap(object0, object1);\n"
-        "\n";
-
-    (ExpectEquality<T, Prof>)(errors, "Swap", context, object0,
-                              source_of_truth1, "std::as_const(object0)",
-                              "source_of_truth1");
-    (ExpectEquality<T, Prof>)(errors, "Swap", context, object1,
-                              source_of_truth0, "std::as_const(object1)",
-                              "source_of_truth0");
-  }
-
-  ConformanceErrors* errors;
-};
-
-// Validate that `generator0` and `generator1` produce values that are equal.
-template <class T, class Prof>
-struct ExpectEquivalenceClassComparison {
-  template <class Fun0, class Fun1>
-  void operator()(const Fun0& generator0, const Fun1& generator1) const {
-    const T object0 = generator0();
-    const T object1 = generator1();
-
-    (ExpectEquality<T, Prof>)(errors, "Comparison",
-                              PrepareGivenContext(
-                                  GivenDeclaration{"const _T object0",
-                                                   generator0.description},
-                                  GivenDeclaration{"const _T object1",
-                                                   generator1.description}),
-                              object0, object1, "object0", "object1");
-  }
-
-  ConformanceErrors* errors;
-};
-
-// Validate that all objects in the same equivalence-class have the same value.
-template <class T, class Prof>
-struct ExpectEquivalenceClassConsistency {
-  template <class Fun0, class Fun1>
-  void operator()(const Fun0& generator0, const Fun1& generator1) const {
-    If<PropertiesOfT<Prof>::is_move_assignable>::Invoke(
-        ExpectMoveAssign<T, Prof>{errors}, generator0, generator1);
-
-    If<PropertiesOfT<Prof>::is_copy_assignable>::Invoke(
-        ExpectCopyAssign<T, Prof>{errors}, generator0, generator1);
-
-    If<PropertiesOfT<Prof>::is_swappable>::Invoke(ExpectSwap<T, Prof>{errors},
-                                                  generator0, generator1);
-  }
-
-  ConformanceErrors* errors;
-};
-
-// Given a "lesser" object and a "greater" object, perform every combination of
-// comparison operators supported for the type, expecting consistent results.
-template <class T, class Prof>
-void ExpectOrdered(ConformanceErrors* errors, absl::string_view context,
-                   const T& small, const T& big, absl::string_view small_name,
-                   absl::string_view big_name) {
-  const absl::string_view test_name = "Comparison";
-
-  If<PropertiesOfT<Prof>::is_equality_comparable>::Invoke(
-      ExpectNotEq{errors}, test_name, context, small, big, small_name,
-      big_name);
-  If<PropertiesOfT<Prof>::is_equality_comparable>::Invoke(
-      ExpectNotEq{errors}, test_name, context, big, small, big_name,
-      small_name);
-
-  If<PropertiesOfT<Prof>::is_inequality_comparable>::Invoke(
-      ExpectNe{errors}, test_name, context, small, big, small_name, big_name);
-  If<PropertiesOfT<Prof>::is_inequality_comparable>::Invoke(
-      ExpectNe{errors}, test_name, context, big, small, big_name, small_name);
-
-  If<PropertiesOfT<Prof>::is_less_than_comparable>::Invoke(
-      ExpectLt{errors}, test_name, context, small, big, small_name, big_name);
-  If<PropertiesOfT<Prof>::is_less_than_comparable>::Invoke(
-      ExpectNotLt{errors}, test_name, context, big, small, big_name,
-      small_name);
-
-  If<PropertiesOfT<Prof>::is_less_equal_comparable>::Invoke(
-      ExpectLe{errors}, test_name, context, small, big, small_name, big_name);
-  If<PropertiesOfT<Prof>::is_less_equal_comparable>::Invoke(
-      ExpectNotLe{errors}, test_name, context, big, small, big_name,
-      small_name);
-
-  If<PropertiesOfT<Prof>::is_greater_equal_comparable>::Invoke(
-      ExpectNotGe{errors}, test_name, context, small, big, small_name,
-      big_name);
-  If<PropertiesOfT<Prof>::is_greater_equal_comparable>::Invoke(
-      ExpectGe{errors}, test_name, context, big, small, big_name, small_name);
-
-  If<PropertiesOfT<Prof>::is_greater_than_comparable>::Invoke(
-      ExpectNotGt{errors}, test_name, context, small, big, small_name,
-      big_name);
-  If<PropertiesOfT<Prof>::is_greater_than_comparable>::Invoke(
-      ExpectGt{errors}, test_name, context, big, small, big_name, small_name);
-}
-
-// For every two elements of an equivalence class, makes sure that those two
-// elements compare equal, including checks with the same argument passed as
-// both operands.
-template <class T, class Prof>
-struct ExpectEquivalenceClassComparisons {
-  template <class... Funs>
-  void operator()(EquivalenceClassType<Funs...> eq_class) const {
-    (ForEachTupleElement)(ExpectSelfComparison<T, Prof>{errors},
-                          eq_class.generators);
-
-    (ForEveryTwo)(ExpectEquivalenceClassComparison<T, Prof>{errors},
-                  eq_class.generators);
-  }
-
-  ConformanceErrors* errors;
-};
-
-// For every element of an equivalence class, makes sure that the element is
-// self-consistent (in other words, if any of move/copy/swap are defined,
-// perform those operations and make such that results and operands still
-// compare equal to known values whenever it is required for that operation.
-template <class T, class Prof>
-struct ExpectEquivalenceClass {
-  template <class... Funs>
-  void operator()(EquivalenceClassType<Funs...> eq_class) const {
-    (ForEachTupleElement)(ExpectConsistency<T, Prof>{errors},
-                          eq_class.generators);
-
-    (ForEveryTwo)(ExpectEquivalenceClassConsistency<T, Prof>{errors},
-                  eq_class.generators);
-  }
-
-  ConformanceErrors* errors;
-};
-
-// Validate that the passed-in argument is a generator of a greater value than
-// the one produced by the "small_gen" datamember with respect to all of the
-// comparison operators that Prof requires, with both argument orders to test.
-template <class T, class Prof, class SmallGenerator>
-struct ExpectBiggerGeneratorThanComparisons {
-  template <class BigGenerator>
-  void operator()(BigGenerator big_gen) const {
-    const T small = small_gen();
-    const T big = big_gen();
-
-    (ExpectOrdered<T, Prof>)(errors,
-                             PrepareGivenContext(
-                                 GivenDeclaration{"const _T small",
-                                                  small_gen.description},
-                                 GivenDeclaration{"const _T big",
-                                                  big_gen.description}),
-                             small, big, "small", "big");
-  }
-
-  SmallGenerator small_gen;
-  ConformanceErrors* errors;
-};
-
-// Perform all of the move, copy, and swap checks on the value generated by
-// `small_gen` and the value generated by `big_gen`.
-template <class T, class Prof, class SmallGenerator>
-struct ExpectBiggerGeneratorThan {
-  template <class BigGenerator>
-  void operator()(BigGenerator big_gen) const {
-    If<PropertiesOfT<Prof>::is_move_assignable>::Invoke(
-        ExpectMoveAssign<T, Prof>{errors}, small_gen, big_gen);
-    If<PropertiesOfT<Prof>::is_move_assignable>::Invoke(
-        ExpectMoveAssign<T, Prof>{errors}, big_gen, small_gen);
-
-    If<PropertiesOfT<Prof>::is_copy_assignable>::Invoke(
-        ExpectCopyAssign<T, Prof>{errors}, small_gen, big_gen);
-    If<PropertiesOfT<Prof>::is_copy_assignable>::Invoke(
-        ExpectCopyAssign<T, Prof>{errors}, big_gen, small_gen);
-
-    If<PropertiesOfT<Prof>::is_swappable>::Invoke(ExpectSwap<T, Prof>{errors},
-                                                  small_gen, big_gen);
-  }
-
-  SmallGenerator small_gen;
-  ConformanceErrors* errors;
-};
-
-// Validate that the result of a generator is greater than the results of all
-// generators in an equivalence class with respect to comparisons.
-template <class T, class Prof, class SmallGenerator>
-struct ExpectBiggerGeneratorThanEqClassesComparisons {
-  template <class BigEqClass>
-  void operator()(BigEqClass big_eq_class) const {
-    (ForEachTupleElement)(
-        ExpectBiggerGeneratorThanComparisons<T, Prof, SmallGenerator>{small_gen,
-                                                                      errors},
-        big_eq_class.generators);
-  }
-
-  SmallGenerator small_gen;
-  ConformanceErrors* errors;
-};
-
-// Validate that the non-comparison binary operations required by Prof are
-// correct for the result of each generator of big_eq_class and a generator of
-// the logically smaller value returned by small_gen.
-template <class T, class Prof, class SmallGenerator>
-struct ExpectBiggerGeneratorThanEqClasses {
-  template <class BigEqClass>
-  void operator()(BigEqClass big_eq_class) const {
-    (ForEachTupleElement)(
-        ExpectBiggerGeneratorThan<T, Prof, SmallGenerator>{small_gen, errors},
-        big_eq_class.generators);
-  }
-
-  SmallGenerator small_gen;
-  ConformanceErrors* errors;
-};
-
-// Validate that each equivalence class that is passed is logically less than
-// the equivalence classes that comes later on in the argument list.
-template <class T, class Prof>
-struct ExpectOrderedEquivalenceClassesComparisons {
-  template <class... BigEqClasses>
-  struct Impl {
-    // Validate that the value produced by `small_gen` is less than all of the
-    // values generated by those of the logically larger equivalence classes.
-    template <class SmallGenerator>
-    void operator()(SmallGenerator small_gen) const {
-      (ForEachTupleElement)(ExpectBiggerGeneratorThanEqClassesComparisons<
-                                T, Prof, SmallGenerator>{small_gen, errors},
-                            big_eq_classes);
-    }
-
-    std::tuple<BigEqClasses...> big_eq_classes;
-    ConformanceErrors* errors;
-  };
-
-  // When given no equivalence classes, no validation is necessary.
-  void operator()() const {}
-
-  template <class SmallEqClass, class... BigEqClasses>
-  void operator()(SmallEqClass small_eq_class,
-                  BigEqClasses... big_eq_classes) const {
-    // For each generator in the first equivalence class, make sure that it is
-    // less than each of those in the logically greater equivalence classes.
-    (ForEachTupleElement)(
-        Impl<BigEqClasses...>{std::make_tuple(absl::move(big_eq_classes)...),
-                              errors},
-        small_eq_class.generators);
-
-    // Recurse so that all equivalence class combinations are checked.
-    (*this)(absl::move(big_eq_classes)...);
-  }
-
-  ConformanceErrors* errors;
-};
-
-// Validate that the non-comparison binary operations required by Prof are
-// correct for the result of each generator of big_eq_classes and a generator of
-// the logically smaller value returned by small_gen.
-template <class T, class Prof>
-struct ExpectOrderedEquivalenceClasses {
-  template <class... BigEqClasses>
-  struct Impl {
-    template <class SmallGenerator>
-    void operator()(SmallGenerator small_gen) const {
-      (ForEachTupleElement)(
-          ExpectBiggerGeneratorThanEqClasses<T, Prof, SmallGenerator>{small_gen,
-                                                                      errors},
-          big_eq_classes);
-    }
-
-    std::tuple<BigEqClasses...> big_eq_classes;
-    ConformanceErrors* errors;
-  };
-
-  // Check that small_eq_class is logically consistent and also is logically
-  // less than all values in big_eq_classes.
-  template <class SmallEqClass, class... BigEqClasses>
-  void operator()(SmallEqClass small_eq_class,
-                  BigEqClasses... big_eq_classes) const {
-    (ForEachTupleElement)(
-        Impl<BigEqClasses...>{std::make_tuple(absl::move(big_eq_classes)...),
-                              errors},
-        small_eq_class.generators);
-
-    (*this)(absl::move(big_eq_classes)...);
-  }
-
-  // Terminating case of operator().
-  void operator()() const {}
-
-  ConformanceErrors* errors;
-};
-
-// Validate that a type meets the syntactic requirements of std::hash if the
-// range of profiles requires it.
-template <class T, class MinProf, class MaxProf>
-struct ExpectHashable {
-  void operator()() const {
-    ExpectModelOfHashable<T, MinProf, MaxProf>(errors);
-  }
-
-  ConformanceErrors* errors;
-};
-
-// Validate that the type `T` meets all of the requirements associated with
-// `MinProf` and without going beyond the syntactic properties of `MaxProf`.
-template <class T, class MinProf, class MaxProf>
-struct ExpectModels {
-  void operator()(ConformanceErrors* errors) const {
-    ExpectModelOfDefaultConstructible<T, MinProf, MaxProf>(errors);
-    ExpectModelOfMoveConstructible<T, MinProf, MaxProf>(errors);
-    ExpectModelOfCopyConstructible<T, MinProf, MaxProf>(errors);
-    ExpectModelOfMoveAssignable<T, MinProf, MaxProf>(errors);
-    ExpectModelOfCopyAssignable<T, MinProf, MaxProf>(errors);
-    ExpectModelOfDestructible<T, MinProf, MaxProf>(errors);
-    ExpectModelOfEqualityComparable<T, MinProf, MaxProf>(errors);
-    ExpectModelOfInequalityComparable<T, MinProf, MaxProf>(errors);
-    ExpectModelOfLessThanComparable<T, MinProf, MaxProf>(errors);
-    ExpectModelOfLessEqualComparable<T, MinProf, MaxProf>(errors);
-    ExpectModelOfGreaterEqualComparable<T, MinProf, MaxProf>(errors);
-    ExpectModelOfGreaterThanComparable<T, MinProf, MaxProf>(errors);
-    ExpectModelOfSwappable<T, MinProf, MaxProf>(errors);
-
-    // Only check hashability on compilers that have a compliant default-hash.
-    If<!poisoned_hash_fails_instantiation()>::Invoke(
-        ExpectHashable<T, MinProf, MaxProf>{errors});
-  }
-};
-
-// A metafunction that yields a Profile matching the set of properties that are
-// safe to be checked (lack-of-hashability is only checked on standard library
-// implementations that are standards compliant in that they provide a std::hash
-// primary template that is SFINAE-friendly)
-template <class LogicalProf, class T>
-struct MinimalCheckableProfile {
-  using type =
-      MinimalProfiles<PropertiesOfT<LogicalProf>,
-                      PropertiesOfT<SyntacticConformanceProfileOf<
-                          T, !PropertiesOfT<LogicalProf>::is_hashable &&
-                                     poisoned_hash_fails_instantiation()
-                                 ? CheckHashability::no
-                                 : CheckHashability::yes>>>;
-};
-
-// An identity metafunction
-template <class T>
-struct Always {
-  using type = T;
-};
-
-// Validate the T meets all of the necessary requirements of LogicalProf, with
-// syntactic requirements defined by the profile range [MinProf, MaxProf].
-template <class T, class LogicalProf, class MinProf, class MaxProf,
-          class... EqClasses>
-ConformanceErrors ExpectRegularityImpl(
-    OrderedEquivalenceClasses<EqClasses...> vals) {
-  ConformanceErrors errors((NameOf<T>()));
-
-  If<!constexpr_instantiation_when_unevaluated()>::Invoke(
-      ExpectModels<T, MinProf, MaxProf>(), &errors);
-
-  using minimal_profile = typename absl::conditional_t<
-      constexpr_instantiation_when_unevaluated(), Always<LogicalProf>,
-      MinimalCheckableProfile<LogicalProf, T>>::type;
-
-  If<PropertiesOfT<minimal_profile>::is_default_constructible>::Invoke(
-      ExpectDefaultConstructWithDestruct<T>{&errors});
-
-  //////////////////////////////////////////////////////////////////////////////
-  // Perform all comparison checks first, since later checks depend on their
-  // correctness.
-  //
-  // Check all of the comparisons for all values in the same equivalence
-  // class (equal with respect to comparison operators and hash the same).
-  (ForEachTupleElement)(
-      ExpectEquivalenceClassComparisons<T, minimal_profile>{&errors},
-      vals.eq_classes);
-
-  // Check all of the comparisons for each combination of values that are in
-  // different equivalence classes (not equal with respect to comparison
-  // operators).
-  absl::apply(
-      ExpectOrderedEquivalenceClassesComparisons<T, minimal_profile>{&errors},
-      vals.eq_classes);
-  //
-  //////////////////////////////////////////////////////////////////////////////
-
-  // Perform remaining checks, relying on comparisons.
-  // TODO(calabrese) short circuit if any comparisons above failed.
-  (ForEachTupleElement)(ExpectEquivalenceClass<T, minimal_profile>{&errors},
-                        vals.eq_classes);
-
-  absl::apply(ExpectOrderedEquivalenceClasses<T, minimal_profile>{&errors},
-              vals.eq_classes);
-
-  return errors;
-}
-
-// A type that represents a range of profiles that are acceptable to be matched.
-//
-// `MinProf` is the minimum set of syntactic requirements that must be met.
-//
-// `MaxProf` is the maximum set of syntactic requirements that must be met.
-// This maximum is particularly useful for certain "strictness" checking. Some
-// examples for when this is useful:
-//
-// * Making sure that a type is move-only (rather than simply movable)
-//
-// * Making sure that a member function is *not* noexcept in cases where it
-//   cannot be noexcept, such as if a dependent datamember has certain
-//   operations that are not noexcept.
-//
-// * Making sure that a type tightly matches a spec, such as the standard.
-//
-// `LogicalProf` is the Profile for which run-time testing is to take place.
-//
-// Note: The reason for `LogicalProf` is because it is often the case, when
-// dealing with templates, that a declaration of a given operation is specified,
-// but whose body would fail to instantiate. Examples include the
-// copy-constructor of a standard container when the element-type is move-only,
-// or the comparison operators of a standard container when the element-type
-// does not have the necessary comparison operations defined. The `LogicalProf`
-// parameter allows us to capture the intent of what should be tested at
-// run-time, even in the cases where syntactically it might otherwise appear as
-// though the type undergoing testing supports more than it actually does.
-template <class LogicalProf, class MinProf = LogicalProf,
-          class MaxProf = MinProf>
-struct ProfileRange {
-  using logical_profile = LogicalProf;
-  using min_profile = MinProf;
-  using max_profile = MaxProf;
-};
-
-// Similar to ProfileRange except that it creates a profile range that is
-// coupled with a Domain and is used when testing that a type matches exactly
-// the "minimum" requirements of LogicalProf.
-template <class StrictnessDomain, class LogicalProf,
-          class MinProf = LogicalProf, class MaxProf = MinProf>
-struct StrictProfileRange {
-  // We do not yet support extension.
-  static_assert(
-      std::is_same<StrictnessDomain, RegularityDomain>::value,
-      "Currently, the only valid StrictnessDomain is RegularityDomain.");
-  using strictness_domain = StrictnessDomain;
-  using logical_profile = LogicalProf;
-  using min_profile = MinProf;
-  using max_profile = MaxProf;
-};
-
-////////////////////////////////////////////////////////////////////////////////
-//
-// A metafunction that creates a StrictProfileRange from a Domain and either a
-// Profile or ProfileRange.
-template <class StrictnessDomain, class ProfOrRange>
-struct MakeStrictProfileRange;
-
-template <class StrictnessDomain, class LogicalProf>
-struct MakeStrictProfileRange {
-  using type = StrictProfileRange<StrictnessDomain, LogicalProf>;
-};
-
-template <class StrictnessDomain, class LogicalProf, class MinProf,
-          class MaxProf>
-struct MakeStrictProfileRange<StrictnessDomain,
-                              ProfileRange<LogicalProf, MinProf, MaxProf>> {
-  using type =
-      StrictProfileRange<StrictnessDomain, LogicalProf, MinProf, MaxProf>;
-};
-
-template <class StrictnessDomain, class ProfOrRange>
-using MakeStrictProfileRangeT =
-    typename MakeStrictProfileRange<StrictnessDomain, ProfOrRange>::type;
-//
-////////////////////////////////////////////////////////////////////////////////
-
-// A profile in the RegularityDomain with the strongest possible requirements.
-using MostStrictProfile =
-    CombineProfiles<TriviallyCompleteProfile, NothrowComparableProfile>;
-
-// Forms a ProfileRange that treats the Profile as the bare minimum requirements
-// of a type.
-template <class LogicalProf, class MinProf = LogicalProf>
-using LooseProfileRange = StrictProfileRange<RegularityDomain, LogicalProf,
-                                             MinProf, MostStrictProfile>;
-
-template <class Prof>
-using MakeLooseProfileRangeT = Prof;
-
-////////////////////////////////////////////////////////////////////////////////
-//
-// The following classes implement the metafunction ProfileRangeOfT<T> that
-// takes either a Profile or ProfileRange and yields the ProfileRange to be
-// used during testing.
-//
-template <class T, class /*Enabler*/ = void>
-struct ProfileRangeOfImpl;
-
-template <class T>
-struct ProfileRangeOfImpl<T, absl::void_t<PropertiesOfT<T>>> {
-  using type = LooseProfileRange<T>;
-};
-
-template <class T>
-struct ProfileRangeOf : ProfileRangeOfImpl<T> {};
-
-template <class StrictnessDomain, class LogicalProf, class MinProf,
-          class MaxProf>
-struct ProfileRangeOf<
-    StrictProfileRange<StrictnessDomain, LogicalProf, MinProf, MaxProf>> {
-  using type =
-      StrictProfileRange<StrictnessDomain, LogicalProf, MinProf, MaxProf>;
-};
-
-template <class T>
-using ProfileRangeOfT = typename ProfileRangeOf<T>::type;
-//
-////////////////////////////////////////////////////////////////////////////////
-
-// Extract the logical profile of a range (what will be runtime tested).
-template <class T>
-using LogicalProfileOfT = typename ProfileRangeOfT<T>::logical_profile;
-
-// Extract the minimal syntactic profile of a range (error if not at least).
-template <class T>
-using MinProfileOfT = typename ProfileRangeOfT<T>::min_profile;
-
-// Extract the maximum syntactic profile of a range (error if more than).
-template <class T>
-using MaxProfileOfT = typename ProfileRangeOfT<T>::max_profile;
-
-////////////////////////////////////////////////////////////////////////////////
-//
-template <class T>
-struct IsProfileOrProfileRange : IsProfile<T>::type {};
-
-template <class StrictnessDomain, class LogicalProf, class MinProf,
-          class MaxProf>
-struct IsProfileOrProfileRange<
-    StrictProfileRange<StrictnessDomain, LogicalProf, MinProf, MaxProf>>
-    : std::true_type {};
-//
-////////////////////////////////////////////////////////////////////////////////
-
-// TODO(calabrese): Consider naming the functions in this class the same as
-// the macros (defined later on) so that auto-complete leads to the correct name
-// and so that a user cannot accidentally call a function rather than the macro
-// form.
-template <bool ExpectSuccess, class T, class... EqClasses>
-struct ExpectConformanceOf {
-  // Add a value to be tested. Subsequent calls to this function on the same
-  // object must specify logically "larger" values with respect to the
-  // comparison operators of the type, if any.
-  //
-  // NOTE: This function should not be called directly. A stateless lambda is
-  // implicitly formed and passed when using the INITIALIZER macro at the bottom
-  // of this file.
-  template <class Fun,
-            absl::enable_if_t<std::is_same<
-                ResultOfGeneratorT<GeneratorType<Fun>>, T>::value>** = nullptr>
-  ABSL_MUST_USE_RESULT ExpectConformanceOf<ExpectSuccess, T, EqClasses...,
-                                           EquivalenceClassType<Fun>>
-  initializer(GeneratorType<Fun> fun) && {
-    return {
-        {std::tuple_cat(absl::move(ordered_vals.eq_classes),
-                        std::make_tuple((EquivalenceClass)(absl::move(fun))))},
-        std::move(expected_failed_tests)};
-  }
-
-  template <class... TestNames,
-            absl::enable_if_t<!ExpectSuccess && sizeof...(EqClasses) == 0 &&
-                              absl::conjunction<std::is_convertible<
-                                  TestNames, absl::string_view>...>::value>** =
-                nullptr>
-  ABSL_MUST_USE_RESULT ExpectConformanceOf<ExpectSuccess, T, EqClasses...>
-  due_to(TestNames&&... test_names) && {
-    (InsertEach)(&expected_failed_tests,
-                 absl::AsciiStrToLower(absl::string_view(test_names))...);
-
-    return {absl::move(ordered_vals), std::move(expected_failed_tests)};
-  }
-
-  template <class... TestNames, int = 0,  // MSVC disambiguator
-            absl::enable_if_t<ExpectSuccess && sizeof...(EqClasses) == 0 &&
-                              absl::conjunction<std::is_convertible<
-                                  TestNames, absl::string_view>...>::value>** =
-                nullptr>
-  ABSL_MUST_USE_RESULT ExpectConformanceOf<ExpectSuccess, T, EqClasses...>
-  due_to(TestNames&&... test_names) && {
-    // TODO(calabrese) Instead have DUE_TO only exist via a CRTP base.
-    // This would produce better errors messages than the static_assert.
-    static_assert(!ExpectSuccess,
-                  "DUE_TO cannot be called when conformance is expected -- did "
-                  "you mean to use ASSERT_NONCONFORMANCE_OF?");
-  }
-
-  // Add a value to be tested. Subsequent calls to this function on the same
-  // object must specify logically "larger" values with respect to the
-  // comparison operators of the type, if any.
-  //
-  // NOTE: This function should not be called directly. A stateful lambda is
-  // implicitly formed and passed when using the INITIALIZER macro at the bottom
-  // of this file.
-  template <class Fun,
-            absl::enable_if_t<std::is_same<
-                ResultOfGeneratorT<GeneratorType<Fun>>, T>::value>** = nullptr>
-  ABSL_MUST_USE_RESULT ExpectConformanceOf<ExpectSuccess, T, EqClasses...,
-                                           EquivalenceClassType<Fun>>
-  dont_class_directly_stateful_initializer(GeneratorType<Fun> fun) && {
-    return {
-        {std::tuple_cat(absl::move(ordered_vals.eq_classes),
-                        std::make_tuple((EquivalenceClass)(absl::move(fun))))},
-        std::move(expected_failed_tests)};
-  }
-
-  // Add a set of value to be tested, where each value is equal with respect to
-  // the comparison operators and std::hash specialization, if defined.
-  template <
-      class... Funs,
-      absl::void_t<absl::enable_if_t<std::is_same<
-          ResultOfGeneratorT<GeneratorType<Funs>>, T>::value>...>** = nullptr>
-  ABSL_MUST_USE_RESULT ExpectConformanceOf<ExpectSuccess, T, EqClasses...,
-                                           EquivalenceClassType<Funs...>>
-  equivalence_class(GeneratorType<Funs>... funs) && {
-    return {{std::tuple_cat(
-                absl::move(ordered_vals.eq_classes),
-                std::make_tuple((EquivalenceClass)(absl::move(funs)...)))},
-            std::move(expected_failed_tests)};
-  }
-
-  // Execute the tests for the captured set of values, strictly matching a range
-  // of expected profiles in a given domain.
-  template <
-      class ProfRange,
-      absl::enable_if_t<IsProfileOrProfileRange<ProfRange>::value>** = nullptr>
-  ABSL_MUST_USE_RESULT ::testing::AssertionResult with_strict_profile(
-      ProfRange /*profile*/) {
-    ConformanceErrors test_result =
-        (ExpectRegularityImpl<
-            T, LogicalProfileOfT<ProfRange>, MinProfileOfT<ProfRange>,
-            MaxProfileOfT<ProfRange>>)(absl::move(ordered_vals));
-
-    return ExpectSuccess ? test_result.assertionResult()
-                         : test_result.expectFailedTests(expected_failed_tests);
-  }
-
-  // Execute the tests for the captured set of values, loosely matching a range
-  // of expected profiles (loose in that an interface is allowed to be more
-  // refined that a profile suggests, such as a type having a noexcept copy
-  // constructor when all that is required is that the copy constructor exists).
-  template <class Prof, absl::enable_if_t<IsProfile<Prof>::value>** = nullptr>
-  ABSL_MUST_USE_RESULT ::testing::AssertionResult with_loose_profile(
-      Prof /*profile*/) {
-    ConformanceErrors test_result =
-        (ExpectRegularityImpl<
-            T, Prof, Prof,
-            CombineProfiles<TriviallyCompleteProfile,
-                            NothrowComparableProfile>>)(absl::
-                                                            move(ordered_vals));
-
-    return ExpectSuccess ? test_result.assertionResult()
-                         : test_result.expectFailedTests(expected_failed_tests);
-  }
-
-  OrderedEquivalenceClasses<EqClasses...> ordered_vals;
-  std::set<std::string> expected_failed_tests;
-};
-
-template <class T>
-using ExpectConformanceOfType = ExpectConformanceOf</*ExpectSuccess=*/true, T>;
-
-template <class T>
-using ExpectNonconformanceOfType =
-    ExpectConformanceOf</*ExpectSuccess=*/false, T>;
-
-struct EquivalenceClassMaker {
-  // TODO(calabrese) Constrain to callable
-  template <class Fun>
-  static GeneratorType<Fun> initializer(GeneratorType<Fun> fun) {
-    return fun;
-  }
-};
-
-// A top-level macro that begins the builder pattern.
-//
-// The argument here takes the datatype to be tested.
-#define ABSL_INTERNAL_ASSERT_CONFORMANCE_OF(...)                            \
-  GTEST_AMBIGUOUS_ELSE_BLOCKER_                                             \
-  if ABSL_INTERNAL_LPAREN                                                   \
-  const ::testing::AssertionResult gtest_ar =                               \
-      ABSL_INTERNAL_LPAREN ::absl::types_internal::ExpectConformanceOfType< \
-          __VA_ARGS__>()
-
-// Akin to ASSERT_CONFORMANCE_OF except that it expects failure and tries to
-// match text.
-#define ABSL_INTERNAL_ASSERT_NONCONFORMANCE_OF(...)                            \
-  GTEST_AMBIGUOUS_ELSE_BLOCKER_                                                \
-  if ABSL_INTERNAL_LPAREN                                                      \
-  const ::testing::AssertionResult gtest_ar =                                  \
-      ABSL_INTERNAL_LPAREN ::absl::types_internal::ExpectNonconformanceOfType< \
-          __VA_ARGS__>()
-
-////////////////////////////////////////////////////////////////////////////////
-// NOTE: The following macros look like they are recursive, but are not (macros
-// cannot recurse). These actually refer to member functions of the same name.
-// This is done intentionally so that a user cannot accidentally invoke a
-// member function of the conformance-testing suite without going through the
-// macro.
-////////////////////////////////////////////////////////////////////////////////
-
-// Specify expected test failures as comma-separated strings.
-#define DUE_TO(...) due_to(__VA_ARGS__)
-
-// Specify a value to be tested.
-//
-// Note: Internally, this takes an expression and turns it into the return value
-// of lambda that captures no data. The expression is stringized during
-// preprocessing so that it can be used in error reports.
-#define INITIALIZER(...)                         \
-  initializer(::absl::types_internal::Generator( \
-      [] { return __VA_ARGS__; }, ABSL_INTERNAL_STRINGIZE(__VA_ARGS__)))
-
-// Specify a value to be tested.
-//
-// Note: Internally, this takes an expression and turns it into the return value
-// of lambda that captures data by reference. The expression is stringized
-// during preprocessing so that it can be used in error reports.
-#define STATEFUL_INITIALIZER(...)                         \
-  stateful_initializer(::absl::types_internal::Generator( \
-      [&] { return __VA_ARGS__; }, ABSL_INTERNAL_STRINGIZE(__VA_ARGS__)))
-
-// Used in the builder-pattern.
-//
-// Takes a series of INITIALIZER and/or STATEFUL_INITIALIZER invocations and
-// forwards them along to be tested, grouping them such that the testing suite
-// knows that they are supposed to represent the same logical value (the values
-// compare the same, hash the same, etc.).
-#define EQUIVALENCE_CLASS(...)                    \
-  equivalence_class(ABSL_INTERNAL_TRANSFORM_ARGS( \
-      ABSL_INTERNAL_PREPEND_EQ_MAKER, __VA_ARGS__))
-
-// An invocation of this or WITH_STRICT_PROFILE must end the builder-pattern.
-// It takes a Profile as its argument.
-//
-// This executes the tests and allows types that are "more referined" than the
-// profile specifies, but not less. For instance, if the Profile specifies
-// noexcept copy-constructiblity, the test will fail if the copy-constructor is
-// not noexcept, however, it will succeed if the copy constructor is trivial.
-//
-// This is useful for testing that a type meets some minimum set of
-// requirements.
-#define WITH_LOOSE_PROFILE(...)                                      \
-  with_loose_profile(                                                \
-      ::absl::types_internal::MakeLooseProfileRangeT<__VA_ARGS__>()) \
-      ABSL_INTERNAL_RPAREN ABSL_INTERNAL_RPAREN;                     \
-  else GTEST_FATAL_FAILURE_(gtest_ar.failure_message())  // NOLINT
-
-// An invocation of this or WITH_STRICT_PROFILE must end the builder-pattern.
-// It takes a Domain and a Profile as its arguments.
-//
-// This executes the tests and disallows types that differ at all from the
-// properties of the Profile. For instance, if the Profile specifies noexcept
-// copy-constructiblity, the test will fail if the copy constructor is trivial.
-//
-// This is useful for testing that a type does not do anything more than a
-// specification requires, such as to minimize things like Hyrum's Law, or more
-// commonly, to prevent a type from being "accidentally" copy-constructible in
-// a way that may produce incorrect results, simply because the user forget to
-// delete that operation.
-#define WITH_STRICT_PROFILE(...)                                      \
-  with_strict_profile(                                                \
-      ::absl::types_internal::MakeStrictProfileRangeT<__VA_ARGS__>()) \
-      ABSL_INTERNAL_RPAREN ABSL_INTERNAL_RPAREN;                      \
-  else GTEST_FATAL_FAILURE_(gtest_ar.failure_message())  // NOLINT
-
-// Internal macro that is used in the internals of the EDSL when forming
-// equivalence classes.
-#define ABSL_INTERNAL_PREPEND_EQ_MAKER(arg) \
-  ::absl::types_internal::EquivalenceClassMaker().arg
-
-}  // namespace types_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_TYPES_INTERNAL_CONFORMANCE_TESTING_H_
diff --git a/absl/types/internal/conformance_testing_helpers.h b/absl/types/internal/conformance_testing_helpers.h
deleted file mode 100644
index 00775f9..0000000
--- a/absl/types/internal/conformance_testing_helpers.h
+++ /dev/null
@@ -1,391 +0,0 @@
-// Copyright 2019 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.
-
-#ifndef ABSL_TYPES_INTERNAL_CONFORMANCE_TESTING_HELPERS_H_
-#define ABSL_TYPES_INTERNAL_CONFORMANCE_TESTING_HELPERS_H_
-
-// Checks to determine whether or not we can use abi::__cxa_demangle
-#if (defined(__ANDROID__) || defined(ANDROID)) && !defined(OS_ANDROID)
-#define ABSL_INTERNAL_OS_ANDROID
-#endif
-
-// We support certain compilers only.  See demangle.h for details.
-#if defined(OS_ANDROID) && (defined(__i386__) || defined(__x86_64__))
-#define ABSL_TYPES_INTERNAL_HAS_CXA_DEMANGLE 0
-#elif (__GNUC__ >= 4 || (__GNUC__ >= 3 && __GNUC_MINOR__ >= 4)) && \
-    !defined(__mips__)
-#define ABSL_TYPES_INTERNAL_HAS_CXA_DEMANGLE 1
-#elif defined(__clang__) && !defined(_MSC_VER)
-#define ABSL_TYPES_INTERNAL_HAS_CXA_DEMANGLE 1
-#else
-#define ABSL_TYPES_INTERNAL_HAS_CXA_DEMANGLE 0
-#endif
-
-#include <tuple>
-#include <type_traits>
-#include <utility>
-
-#include "absl/meta/type_traits.h"
-#include "absl/strings/string_view.h"
-#include "absl/utility/utility.h"
-
-#if ABSL_TYPES_INTERNAL_HAS_CXA_DEMANGLE
-#include <cxxabi.h>
-
-#include <cstdlib>
-#endif
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace types_internal {
-
-// Return a readable name for type T.
-template <class T>
-absl::string_view NameOfImpl() {
-// TODO(calabrese) Investigate using debugging:internal_demangle as a fallback.
-#if ABSL_TYPES_INTERNAL_HAS_CXA_DEMANGLE
-  int status = 0;
-  char* demangled_name = nullptr;
-
-  demangled_name =
-      abi::__cxa_demangle(typeid(T).name(), nullptr, nullptr, &status);
-
-  if (status == 0 && demangled_name != nullptr) {
-    return demangled_name;
-  } else {
-    return typeid(T).name();
-  }
-#else
-  return typeid(T).name();
-#endif
-  // NOTE: We intentionally leak demangled_name so that it remains valid
-  // throughout the remainder of the program.
-}
-
-// Given a type, returns as nice of a type name as we can produce (demangled).
-//
-// Note: This currently strips cv-qualifiers and references, but that is okay
-// because we only use this internally with unqualified object types.
-template <class T>
-std::string NameOf() {
-  static const absl::string_view result = NameOfImpl<T>();
-  return std::string(result);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-//
-// Metafunction to check if a type is callable with no explicit arguments
-template <class Fun, class /*Enabler*/ = void>
-struct IsNullaryCallableImpl : std::false_type {};
-
-template <class Fun>
-struct IsNullaryCallableImpl<
-    Fun, absl::void_t<decltype(std::declval<const Fun&>()())>>
-    : std::true_type {
-  using result_type = decltype(std::declval<const Fun&>()());
-
-  template <class ValueType>
-  using for_type = std::is_same<ValueType, result_type>;
-
-  using void_if_true = void;
-};
-
-template <class Fun>
-struct IsNullaryCallable : IsNullaryCallableImpl<Fun> {};
-//
-////////////////////////////////////////////////////////////////////////////////
-
-// A type that contains a function object that returns an instance of a type
-// that is undergoing conformance testing. This function is required to always
-// return the same value upon invocation.
-template <class Fun>
-struct GeneratorType;
-
-// A type that contains a tuple of GeneratorType<Fun> where each Fun has the
-// same return type. The result of each of the different generators should all
-// be equal values, though the underlying object representation may differ (such
-// as if one returns 0.0 and another return -0.0, or if one returns an empty
-// vector and another returns an empty vector with a different capacity.
-template <class... Funs>
-struct EquivalenceClassType;
-
-////////////////////////////////////////////////////////////////////////////////
-//
-// A metafunction to check if a type is a specialization of EquivalenceClassType
-template <class T>
-struct IsEquivalenceClass : std::false_type {};
-
-template <>
-struct IsEquivalenceClass<EquivalenceClassType<>> : std::true_type {
-  using self = IsEquivalenceClass;
-
-  // A metafunction to check if this EquivalenceClassType is a valid
-  // EquivalenceClassType for a type `ValueType` that is undergoing testing
-  template <class ValueType>
-  using for_type = std::true_type;
-};
-
-template <class Head, class... Tail>
-struct IsEquivalenceClass<EquivalenceClassType<Head, Tail...>>
-    : std::true_type {
-  using self = IsEquivalenceClass;
-
-  // The type undergoing conformance testing that this EquivalenceClass
-  // corresponds to
-  using result_type = typename IsNullaryCallable<Head>::result_type;
-
-  // A metafunction to check if this EquivalenceClassType is a valid
-  // EquivalenceClassType for a type `ValueType` that is undergoing testing
-  template <class ValueType>
-  using for_type = std::is_same<ValueType, result_type>;
-};
-//
-////////////////////////////////////////////////////////////////////////////////
-
-// A type that contains an ordered series of EquivalenceClassTypes, where the
-// the function object of each underlying GeneratorType has the same return type
-//
-// These equivalence classes are required to be in a logical ascending order
-// that is consistent with comparison operators that are defined for the return
-// type of each GeneratorType, if any.
-template <class... EqClasses>
-struct OrderedEquivalenceClasses;
-
-////////////////////////////////////////////////////////////////////////////////
-//
-// A metafunction to determine the return type of the function object contained
-// in a GeneratorType specialization.
-template <class T>
-struct ResultOfGenerator {};
-
-template <class Fun>
-struct ResultOfGenerator<GeneratorType<Fun>> {
-  using type = decltype(std::declval<const Fun&>()());
-};
-
-template <class Fun>
-using ResultOfGeneratorT = typename ResultOfGenerator<GeneratorType<Fun>>::type;
-//
-////////////////////////////////////////////////////////////////////////////////
-
-////////////////////////////////////////////////////////////////////////////////
-//
-// A metafunction that yields true iff each of Funs is a GeneratorType
-// specialization and they all contain functions with the same return type
-template <class /*Enabler*/, class... Funs>
-struct AreGeneratorsWithTheSameReturnTypeImpl : std::false_type {};
-
-template <>
-struct AreGeneratorsWithTheSameReturnTypeImpl<void> : std::true_type {};
-
-template <class Head, class... Tail>
-struct AreGeneratorsWithTheSameReturnTypeImpl<
-    typename std::enable_if<absl::conjunction<std::is_same<
-        ResultOfGeneratorT<Head>, ResultOfGeneratorT<Tail>>...>::value>::type,
-    Head, Tail...> : std::true_type {};
-
-template <class... Funs>
-struct AreGeneratorsWithTheSameReturnType
-    : AreGeneratorsWithTheSameReturnTypeImpl<void, Funs...>::type {};
-//
-////////////////////////////////////////////////////////////////////////////////
-
-////////////////////////////////////////////////////////////////////////////////
-//
-// A metafunction that yields true iff each of Funs is an EquivalenceClassType
-// specialization and they all contain GeneratorType specializations that have
-// the same return type
-template <class... EqClasses>
-struct AreEquivalenceClassesOfTheSameType {
-  static_assert(sizeof...(EqClasses) != sizeof...(EqClasses), "");
-};
-
-template <>
-struct AreEquivalenceClassesOfTheSameType<> : std::true_type {
-  using self = AreEquivalenceClassesOfTheSameType;
-
-  // Metafunction to check that a type is the same as all of the equivalence
-  // classes, if any.
-  // Note: In this specialization there are no equivalence classes, so the
-  // value type is always compatible.
-  template <class /*ValueType*/>
-  using for_type = std::true_type;
-};
-
-template <class... Funs>
-struct AreEquivalenceClassesOfTheSameType<EquivalenceClassType<Funs...>>
-    : std::true_type {
-  using self = AreEquivalenceClassesOfTheSameType;
-
-  // Metafunction to check that a type is the same as all of the equivalence
-  // classes, if any.
-  template <class ValueType>
-  using for_type = typename IsEquivalenceClass<
-      EquivalenceClassType<Funs...>>::template for_type<ValueType>;
-};
-
-template <class... TailEqClasses>
-struct AreEquivalenceClassesOfTheSameType<
-    EquivalenceClassType<>, EquivalenceClassType<>, TailEqClasses...>
-    : AreEquivalenceClassesOfTheSameType<TailEqClasses...>::self {};
-
-template <class HeadNextFun, class... TailNextFuns, class... TailEqClasses>
-struct AreEquivalenceClassesOfTheSameType<
-    EquivalenceClassType<>, EquivalenceClassType<HeadNextFun, TailNextFuns...>,
-    TailEqClasses...>
-    : AreEquivalenceClassesOfTheSameType<
-          EquivalenceClassType<HeadNextFun, TailNextFuns...>,
-          TailEqClasses...>::self {};
-
-template <class HeadHeadFun, class... TailHeadFuns, class... TailEqClasses>
-struct AreEquivalenceClassesOfTheSameType<
-    EquivalenceClassType<HeadHeadFun, TailHeadFuns...>, EquivalenceClassType<>,
-    TailEqClasses...>
-    : AreEquivalenceClassesOfTheSameType<
-          EquivalenceClassType<HeadHeadFun, TailHeadFuns...>,
-          TailEqClasses...>::self {};
-
-template <class HeadHeadFun, class... TailHeadFuns, class HeadNextFun,
-          class... TailNextFuns, class... TailEqClasses>
-struct AreEquivalenceClassesOfTheSameType<
-    EquivalenceClassType<HeadHeadFun, TailHeadFuns...>,
-    EquivalenceClassType<HeadNextFun, TailNextFuns...>, TailEqClasses...>
-    : absl::conditional_t<
-          IsNullaryCallable<HeadNextFun>::template for_type<
-              typename IsNullaryCallable<HeadHeadFun>::result_type>::value,
-          AreEquivalenceClassesOfTheSameType<
-              EquivalenceClassType<HeadHeadFun, TailHeadFuns...>,
-              TailEqClasses...>,
-          std::false_type> {};
-//
-////////////////////////////////////////////////////////////////////////////////
-
-// Execute a function for each passed-in parameter.
-template <class Fun, class... Cases>
-void ForEachParameter(const Fun& fun, const Cases&... cases) {
-  const std::initializer_list<bool> results = {
-      (static_cast<void>(fun(cases)), true)...};
-
-  (void)results;
-}
-
-// Execute a function on each passed-in parameter (using a bound function).
-template <class Fun>
-struct ForEachParameterFun {
-  template <class... T>
-  void operator()(const T&... cases) const {
-    (ForEachParameter)(fun, cases...);
-  }
-
-  Fun fun;
-};
-
-// Execute a function on each element of a tuple.
-template <class Fun, class Tup>
-void ForEachTupleElement(const Fun& fun, const Tup& tup) {
-  absl::apply(ForEachParameterFun<Fun>{fun}, tup);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-//
-// Execute a function for each combination of two elements of a tuple, including
-// combinations of an element with itself.
-template <class Fun, class... T>
-struct ForEveryTwoImpl {
-  template <class Lhs>
-  struct WithBoundLhs {
-    template <class Rhs>
-    void operator()(const Rhs& rhs) const {
-      fun(lhs, rhs);
-    }
-
-    Fun fun;
-    Lhs lhs;
-  };
-
-  template <class Lhs>
-  void operator()(const Lhs& lhs) const {
-    (ForEachTupleElement)(WithBoundLhs<Lhs>{fun, lhs}, args);
-  }
-
-  Fun fun;
-  std::tuple<T...> args;
-};
-
-template <class Fun, class... T>
-void ForEveryTwo(const Fun& fun, std::tuple<T...> args) {
-  (ForEachTupleElement)(ForEveryTwoImpl<Fun, T...>{fun, args}, args);
-}
-//
-////////////////////////////////////////////////////////////////////////////////
-
-////////////////////////////////////////////////////////////////////////////////
-//
-// Insert all values into an associative container
-template<class Container>
-void InsertEach(Container* cont) {
-}
-
-template<class Container, class H, class... T>
-void InsertEach(Container* cont, H&& head, T&&... tail) {
-  cont->insert(head);
-  (InsertEach)(cont, tail...);
-}
-//
-////////////////////////////////////////////////////////////////////////////////
-// A template with a nested "Invoke" static-member-function that executes a
-// passed-in Callable when `Condition` is true, otherwise it ignores the
-// Callable. This is useful for executing a function object with a condition
-// that corresponds to whether or not the Callable can be safely instantiated.
-// It has some overlapping uses with C++17 `if constexpr`.
-template <bool Condition>
-struct If;
-
-template <>
-struct If</*Condition =*/false> {
-  template <class Fun, class... P>
-  static void Invoke(const Fun& /*fun*/, P&&... /*args*/) {}
-};
-
-template <>
-struct If</*Condition =*/true> {
-  template <class Fun, class... P>
-  static void Invoke(const Fun& fun, P&&... args) {
-    // TODO(calabrese) Use std::invoke equivalent instead of function-call.
-    fun(absl::forward<P>(args)...);
-  }
-};
-
-//
-// ABSL_INTERNAL_STRINGIZE(...)
-//
-// This variadic macro transforms its arguments into a c-string literal after
-// expansion.
-//
-// Example:
-//
-//   ABSL_INTERNAL_STRINGIZE(std::array<int, 10>)
-//
-// Results in:
-//
-//   "std::array<int, 10>"
-#define ABSL_INTERNAL_STRINGIZE(...) ABSL_INTERNAL_STRINGIZE_IMPL((__VA_ARGS__))
-#define ABSL_INTERNAL_STRINGIZE_IMPL(arg) ABSL_INTERNAL_STRINGIZE_IMPL2 arg
-#define ABSL_INTERNAL_STRINGIZE_IMPL2(...) #__VA_ARGS__
-
-}  // namespace types_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_TYPES_INTERNAL_CONFORMANCE_TESTING_HELPERS_H_
diff --git a/absl/types/internal/conformance_testing_test.cc b/absl/types/internal/conformance_testing_test.cc
deleted file mode 100644
index cf262fa..0000000
--- a/absl/types/internal/conformance_testing_test.cc
+++ /dev/null
@@ -1,1556 +0,0 @@
-// Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/types/internal/conformance_testing.h"
-
-#include <new>
-#include <type_traits>
-#include <utility>
-
-#include "gtest/gtest.h"
-#include "absl/meta/type_traits.h"
-#include "absl/types/internal/conformance_aliases.h"
-#include "absl/types/internal/conformance_profile.h"
-
-namespace {
-
-namespace ti = absl::types_internal;
-
-template <class T>
-using DefaultConstructibleWithNewImpl = decltype(::new (std::nothrow) T);
-
-template <class T>
-using DefaultConstructibleWithNew =
-    absl::type_traits_internal::is_detected<DefaultConstructibleWithNewImpl, T>;
-
-template <class T>
-using MoveConstructibleWithNewImpl =
-    decltype(::new (std::nothrow) T(std::declval<T>()));
-
-template <class T>
-using MoveConstructibleWithNew =
-    absl::type_traits_internal::is_detected<MoveConstructibleWithNewImpl, T>;
-
-template <class T>
-using CopyConstructibleWithNewImpl =
-    decltype(::new (std::nothrow) T(std::declval<const T&>()));
-
-template <class T>
-using CopyConstructibleWithNew =
-    absl::type_traits_internal::is_detected<CopyConstructibleWithNewImpl, T>;
-
-template <class T,
-          class Result =
-              std::integral_constant<bool, noexcept(::new (std::nothrow) T)>>
-using NothrowDefaultConstructibleWithNewImpl =
-    typename std::enable_if<Result::value>::type;
-
-template <class T>
-using NothrowDefaultConstructibleWithNew =
-    absl::type_traits_internal::is_detected<
-        NothrowDefaultConstructibleWithNewImpl, T>;
-
-template <class T,
-          class Result = std::integral_constant<
-              bool, noexcept(::new (std::nothrow) T(std::declval<T>()))>>
-using NothrowMoveConstructibleWithNewImpl =
-    typename std::enable_if<Result::value>::type;
-
-template <class T>
-using NothrowMoveConstructibleWithNew =
-    absl::type_traits_internal::is_detected<NothrowMoveConstructibleWithNewImpl,
-                                            T>;
-
-template <class T,
-          class Result = std::integral_constant<
-              bool, noexcept(::new (std::nothrow) T(std::declval<const T&>()))>>
-using NothrowCopyConstructibleWithNewImpl =
-    typename std::enable_if<Result::value>::type;
-
-template <class T>
-using NothrowCopyConstructibleWithNew =
-    absl::type_traits_internal::is_detected<NothrowCopyConstructibleWithNewImpl,
-                                            T>;
-
-// NOTE: ?: is used to verify contextually-convertible to bool and not simply
-//       implicit or explicit convertibility.
-#define ABSL_INTERNAL_COMPARISON_OP_EXPR(op) \
-  ((std::declval<const T&>() op std::declval<const T&>()) ? true : true)
-
-#define ABSL_INTERNAL_COMPARISON_OP_TRAIT(name, op)                         \
-  template <class T>                                                        \
-  using name##Impl = decltype(ABSL_INTERNAL_COMPARISON_OP_EXPR(op));        \
-                                                                            \
-  template <class T>                                                        \
-  using name = absl::type_traits_internal::is_detected<name##Impl, T>;      \
-                                                                            \
-  template <class T,                                                        \
-            class Result = std::integral_constant<                          \
-                bool, noexcept(ABSL_INTERNAL_COMPARISON_OP_EXPR(op))>>      \
-  using Nothrow##name##Impl = typename std::enable_if<Result::value>::type; \
-                                                                            \
-  template <class T>                                                        \
-  using Nothrow##name =                                                     \
-      absl::type_traits_internal::is_detected<Nothrow##name##Impl, T>
-
-ABSL_INTERNAL_COMPARISON_OP_TRAIT(EqualityComparable, ==);
-ABSL_INTERNAL_COMPARISON_OP_TRAIT(InequalityComparable, !=);
-ABSL_INTERNAL_COMPARISON_OP_TRAIT(LessThanComparable, <);
-ABSL_INTERNAL_COMPARISON_OP_TRAIT(LessEqualComparable, <=);
-ABSL_INTERNAL_COMPARISON_OP_TRAIT(GreaterEqualComparable, >=);
-ABSL_INTERNAL_COMPARISON_OP_TRAIT(GreaterThanComparable, >);
-
-#undef ABSL_INTERNAL_COMPARISON_OP_TRAIT
-
-template <class T>
-class ProfileTest : public ::testing::Test {};
-
-TYPED_TEST_SUITE_P(ProfileTest);
-
-TYPED_TEST_P(ProfileTest, HasAppropriateConstructionProperties) {
-  using profile = typename TypeParam::profile;
-  using arch = typename TypeParam::arch;
-  using expected_profile = typename TypeParam::expected_profile;
-
-  using props = ti::PropertiesOfT<profile>;
-  using arch_props = ti::PropertiesOfArchetypeT<arch>;
-  using expected_props = ti::PropertiesOfT<expected_profile>;
-
-  // Make sure all of the properties are as expected.
-  // There are seemingly redundant tests here to make it easier to diagnose
-  // the specifics of the failure if something were to go wrong.
-  EXPECT_TRUE((std::is_same<props, arch_props>::value));
-  EXPECT_TRUE((std::is_same<props, expected_props>::value));
-  EXPECT_TRUE((std::is_same<arch_props, expected_props>::value));
-
-  EXPECT_EQ(props::default_constructible_support,
-            expected_props::default_constructible_support);
-
-  EXPECT_EQ(props::move_constructible_support,
-            expected_props::move_constructible_support);
-
-  EXPECT_EQ(props::copy_constructible_support,
-            expected_props::copy_constructible_support);
-
-  EXPECT_EQ(props::destructible_support, expected_props::destructible_support);
-
-  // Avoid additional error message noise when profile and archetype match with
-  // each other but were not what was expected.
-  if (!std::is_same<props, arch_props>::value) {
-    EXPECT_EQ(arch_props::default_constructible_support,
-              expected_props::default_constructible_support);
-
-    EXPECT_EQ(arch_props::move_constructible_support,
-              expected_props::move_constructible_support);
-
-    EXPECT_EQ(arch_props::copy_constructible_support,
-              expected_props::copy_constructible_support);
-
-    EXPECT_EQ(arch_props::destructible_support,
-              expected_props::destructible_support);
-  }
-
-  //////////////////////////////////////////////////////////////////////////////
-  //                       Default constructor checks                         //
-  //////////////////////////////////////////////////////////////////////////////
-  EXPECT_EQ(props::default_constructible_support,
-            expected_props::default_constructible_support);
-
-  switch (expected_props::default_constructible_support) {
-    case ti::default_constructible::maybe:
-      EXPECT_FALSE(DefaultConstructibleWithNew<arch>::value);
-      EXPECT_FALSE(NothrowDefaultConstructibleWithNew<arch>::value);
-
-      // Standard constructible traits depend on the destructor.
-      if (std::is_destructible<arch>::value) {
-        EXPECT_FALSE(std::is_default_constructible<arch>::value);
-        EXPECT_FALSE(std::is_nothrow_default_constructible<arch>::value);
-        EXPECT_FALSE(absl::is_trivially_default_constructible<arch>::value);
-      }
-      break;
-    case ti::default_constructible::yes:
-      EXPECT_TRUE(DefaultConstructibleWithNew<arch>::value);
-      EXPECT_FALSE(NothrowDefaultConstructibleWithNew<arch>::value);
-
-      // Standard constructible traits depend on the destructor.
-      if (std::is_destructible<arch>::value) {
-        EXPECT_TRUE(std::is_default_constructible<arch>::value);
-        EXPECT_FALSE(std::is_nothrow_default_constructible<arch>::value);
-        EXPECT_FALSE(absl::is_trivially_default_constructible<arch>::value);
-      }
-      break;
-    case ti::default_constructible::nothrow:
-      EXPECT_TRUE(DefaultConstructibleWithNew<arch>::value);
-      EXPECT_TRUE(NothrowDefaultConstructibleWithNew<arch>::value);
-
-      // Standard constructible traits depend on the destructor.
-      if (std::is_destructible<arch>::value) {
-        EXPECT_TRUE(std::is_default_constructible<arch>::value);
-        EXPECT_TRUE(std::is_nothrow_default_constructible<arch>::value);
-        EXPECT_FALSE(absl::is_trivially_default_constructible<arch>::value);
-
-        // Constructor traits also check the destructor.
-        if (std::is_nothrow_destructible<arch>::value) {
-          EXPECT_TRUE(std::is_nothrow_default_constructible<arch>::value);
-        }
-      }
-      break;
-    case ti::default_constructible::trivial:
-      EXPECT_TRUE(DefaultConstructibleWithNew<arch>::value);
-      EXPECT_TRUE(NothrowDefaultConstructibleWithNew<arch>::value);
-
-      // Standard constructible traits depend on the destructor.
-      if (std::is_destructible<arch>::value) {
-        EXPECT_TRUE(std::is_default_constructible<arch>::value);
-        EXPECT_TRUE(std::is_nothrow_default_constructible<arch>::value);
-
-        // Constructor triviality traits require trivially destructible types.
-        if (absl::is_trivially_destructible<arch>::value) {
-          EXPECT_TRUE(absl::is_trivially_default_constructible<arch>::value);
-        }
-      }
-      break;
-  }
-
-  //////////////////////////////////////////////////////////////////////////////
-  //                         Move constructor checks                          //
-  //////////////////////////////////////////////////////////////////////////////
-  EXPECT_EQ(props::move_constructible_support,
-            expected_props::move_constructible_support);
-
-  switch (expected_props::move_constructible_support) {
-    case ti::move_constructible::maybe:
-      EXPECT_FALSE(MoveConstructibleWithNew<arch>::value);
-      EXPECT_FALSE(NothrowMoveConstructibleWithNew<arch>::value);
-
-      // Standard constructible traits depend on the destructor.
-      if (std::is_destructible<arch>::value) {
-        EXPECT_FALSE(std::is_move_constructible<arch>::value);
-        EXPECT_FALSE(std::is_nothrow_move_constructible<arch>::value);
-        EXPECT_FALSE(absl::is_trivially_move_constructible<arch>::value);
-      }
-      break;
-    case ti::move_constructible::yes:
-      EXPECT_TRUE(MoveConstructibleWithNew<arch>::value);
-      EXPECT_FALSE(NothrowMoveConstructibleWithNew<arch>::value);
-
-      // Standard constructible traits depend on the destructor.
-      if (std::is_destructible<arch>::value) {
-        EXPECT_TRUE(std::is_move_constructible<arch>::value);
-        EXPECT_FALSE(std::is_nothrow_move_constructible<arch>::value);
-        EXPECT_FALSE(absl::is_trivially_move_constructible<arch>::value);
-      }
-      break;
-    case ti::move_constructible::nothrow:
-      EXPECT_TRUE(MoveConstructibleWithNew<arch>::value);
-      EXPECT_TRUE(NothrowMoveConstructibleWithNew<arch>::value);
-
-      // Standard constructible traits depend on the destructor.
-      if (std::is_destructible<arch>::value) {
-        EXPECT_TRUE(std::is_move_constructible<arch>::value);
-        EXPECT_TRUE(std::is_nothrow_move_constructible<arch>::value);
-        EXPECT_FALSE(absl::is_trivially_move_constructible<arch>::value);
-
-        // Constructor traits also check the destructor.
-        if (std::is_nothrow_destructible<arch>::value) {
-          EXPECT_TRUE(std::is_nothrow_move_constructible<arch>::value);
-        }
-      }
-      break;
-    case ti::move_constructible::trivial:
-      EXPECT_TRUE(MoveConstructibleWithNew<arch>::value);
-      EXPECT_TRUE(NothrowMoveConstructibleWithNew<arch>::value);
-
-      // Standard constructible traits depend on the destructor.
-      if (std::is_destructible<arch>::value) {
-        EXPECT_TRUE(std::is_move_constructible<arch>::value);
-        EXPECT_TRUE(std::is_nothrow_move_constructible<arch>::value);
-
-        // Constructor triviality traits require trivially destructible types.
-        if (absl::is_trivially_destructible<arch>::value) {
-          EXPECT_TRUE(absl::is_trivially_move_constructible<arch>::value);
-        }
-      }
-      break;
-  }
-
-  //////////////////////////////////////////////////////////////////////////////
-  //                         Copy constructor checks                          //
-  //////////////////////////////////////////////////////////////////////////////
-  EXPECT_EQ(props::copy_constructible_support,
-            expected_props::copy_constructible_support);
-
-  switch (expected_props::copy_constructible_support) {
-    case ti::copy_constructible::maybe:
-      EXPECT_FALSE(CopyConstructibleWithNew<arch>::value);
-      EXPECT_FALSE(NothrowCopyConstructibleWithNew<arch>::value);
-
-      // Standard constructible traits depend on the destructor.
-      if (std::is_destructible<arch>::value) {
-        EXPECT_FALSE(std::is_copy_constructible<arch>::value);
-        EXPECT_FALSE(std::is_nothrow_copy_constructible<arch>::value);
-        EXPECT_FALSE(absl::is_trivially_copy_constructible<arch>::value);
-      }
-      break;
-    case ti::copy_constructible::yes:
-      EXPECT_TRUE(CopyConstructibleWithNew<arch>::value);
-      EXPECT_FALSE(NothrowCopyConstructibleWithNew<arch>::value);
-
-      // Standard constructible traits depend on the destructor.
-      if (std::is_destructible<arch>::value) {
-        EXPECT_TRUE(std::is_copy_constructible<arch>::value);
-        EXPECT_FALSE(std::is_nothrow_copy_constructible<arch>::value);
-        EXPECT_FALSE(absl::is_trivially_copy_constructible<arch>::value);
-      }
-      break;
-    case ti::copy_constructible::nothrow:
-      EXPECT_TRUE(CopyConstructibleWithNew<arch>::value);
-      EXPECT_TRUE(NothrowCopyConstructibleWithNew<arch>::value);
-
-      // Standard constructible traits depend on the destructor.
-      if (std::is_destructible<arch>::value) {
-        EXPECT_TRUE(std::is_copy_constructible<arch>::value);
-        EXPECT_TRUE(std::is_nothrow_copy_constructible<arch>::value);
-        EXPECT_FALSE(absl::is_trivially_copy_constructible<arch>::value);
-
-        // Constructor traits also check the destructor.
-        if (std::is_nothrow_destructible<arch>::value) {
-          EXPECT_TRUE(std::is_nothrow_copy_constructible<arch>::value);
-        }
-      }
-      break;
-    case ti::copy_constructible::trivial:
-      EXPECT_TRUE(CopyConstructibleWithNew<arch>::value);
-      EXPECT_TRUE(NothrowCopyConstructibleWithNew<arch>::value);
-
-      // Standard constructible traits depend on the destructor.
-      if (std::is_destructible<arch>::value) {
-        EXPECT_TRUE(std::is_copy_constructible<arch>::value);
-        EXPECT_TRUE(std::is_nothrow_copy_constructible<arch>::value);
-
-        // Constructor triviality traits require trivially destructible types.
-        if (absl::is_trivially_destructible<arch>::value) {
-          EXPECT_TRUE(absl::is_trivially_copy_constructible<arch>::value);
-        }
-      }
-      break;
-  }
-
-  //////////////////////////////////////////////////////////////////////////////
-  //                           Destructible checks                            //
-  //////////////////////////////////////////////////////////////////////////////
-  EXPECT_EQ(props::destructible_support, expected_props::destructible_support);
-
-  switch (expected_props::destructible_support) {
-    case ti::destructible::maybe:
-      EXPECT_FALSE(std::is_destructible<arch>::value);
-      EXPECT_FALSE(std::is_nothrow_destructible<arch>::value);
-      EXPECT_FALSE(absl::is_trivially_destructible<arch>::value);
-      break;
-    case ti::destructible::yes:
-      EXPECT_TRUE(std::is_destructible<arch>::value);
-      EXPECT_FALSE(std::is_nothrow_destructible<arch>::value);
-      EXPECT_FALSE(absl::is_trivially_destructible<arch>::value);
-      break;
-    case ti::destructible::nothrow:
-      EXPECT_TRUE(std::is_destructible<arch>::value);
-      EXPECT_TRUE(std::is_nothrow_destructible<arch>::value);
-      EXPECT_FALSE(absl::is_trivially_destructible<arch>::value);
-      break;
-    case ti::destructible::trivial:
-      EXPECT_TRUE(std::is_destructible<arch>::value);
-      EXPECT_TRUE(std::is_nothrow_destructible<arch>::value);
-      EXPECT_TRUE(absl::is_trivially_destructible<arch>::value);
-      break;
-  }
-}
-
-TYPED_TEST_P(ProfileTest, HasAppropriateAssignmentProperties) {
-  using profile = typename TypeParam::profile;
-  using arch = typename TypeParam::arch;
-  using expected_profile = typename TypeParam::expected_profile;
-
-  using props = ti::PropertiesOfT<profile>;
-  using arch_props = ti::PropertiesOfArchetypeT<arch>;
-  using expected_props = ti::PropertiesOfT<expected_profile>;
-
-  // Make sure all of the properties are as expected.
-  // There are seemingly redundant tests here to make it easier to diagnose
-  // the specifics of the failure if something were to go wrong.
-  EXPECT_TRUE((std::is_same<props, arch_props>::value));
-  EXPECT_TRUE((std::is_same<props, expected_props>::value));
-  EXPECT_TRUE((std::is_same<arch_props, expected_props>::value));
-
-  EXPECT_EQ(props::move_assignable_support,
-            expected_props::move_assignable_support);
-
-  EXPECT_EQ(props::copy_assignable_support,
-            expected_props::copy_assignable_support);
-
-  // Avoid additional error message noise when profile and archetype match with
-  // each other but were not what was expected.
-  if (!std::is_same<props, arch_props>::value) {
-    EXPECT_EQ(arch_props::move_assignable_support,
-              expected_props::move_assignable_support);
-
-    EXPECT_EQ(arch_props::copy_assignable_support,
-              expected_props::copy_assignable_support);
-  }
-
-  //////////////////////////////////////////////////////////////////////////////
-  //                          Move assignment checks                          //
-  //////////////////////////////////////////////////////////////////////////////
-  EXPECT_EQ(props::move_assignable_support,
-            expected_props::move_assignable_support);
-
-  switch (expected_props::move_assignable_support) {
-    case ti::move_assignable::maybe:
-      EXPECT_FALSE(std::is_move_assignable<arch>::value);
-      EXPECT_FALSE(std::is_nothrow_move_assignable<arch>::value);
-      EXPECT_FALSE(absl::is_trivially_move_assignable<arch>::value);
-      break;
-    case ti::move_assignable::yes:
-      EXPECT_TRUE(std::is_move_assignable<arch>::value);
-      EXPECT_FALSE(std::is_nothrow_move_assignable<arch>::value);
-      EXPECT_FALSE(absl::is_trivially_move_assignable<arch>::value);
-      break;
-    case ti::move_assignable::nothrow:
-      EXPECT_TRUE(std::is_move_assignable<arch>::value);
-      EXPECT_TRUE(std::is_nothrow_move_assignable<arch>::value);
-      EXPECT_FALSE(absl::is_trivially_move_assignable<arch>::value);
-      break;
-    case ti::move_assignable::trivial:
-      EXPECT_TRUE(std::is_move_assignable<arch>::value);
-      EXPECT_TRUE(std::is_nothrow_move_assignable<arch>::value);
-      EXPECT_TRUE(absl::is_trivially_move_assignable<arch>::value);
-      break;
-  }
-
-  //////////////////////////////////////////////////////////////////////////////
-  //                          Copy assignment checks                          //
-  //////////////////////////////////////////////////////////////////////////////
-  EXPECT_EQ(props::copy_assignable_support,
-            expected_props::copy_assignable_support);
-
-  switch (expected_props::copy_assignable_support) {
-    case ti::copy_assignable::maybe:
-      EXPECT_FALSE(std::is_copy_assignable<arch>::value);
-      EXPECT_FALSE(std::is_nothrow_copy_assignable<arch>::value);
-      EXPECT_FALSE(absl::is_trivially_copy_assignable<arch>::value);
-      break;
-    case ti::copy_assignable::yes:
-      EXPECT_TRUE(std::is_copy_assignable<arch>::value);
-      EXPECT_FALSE(std::is_nothrow_copy_assignable<arch>::value);
-      EXPECT_FALSE(absl::is_trivially_copy_assignable<arch>::value);
-      break;
-    case ti::copy_assignable::nothrow:
-      EXPECT_TRUE(std::is_copy_assignable<arch>::value);
-      EXPECT_TRUE(std::is_nothrow_copy_assignable<arch>::value);
-      EXPECT_FALSE(absl::is_trivially_copy_assignable<arch>::value);
-      break;
-    case ti::copy_assignable::trivial:
-      EXPECT_TRUE(std::is_copy_assignable<arch>::value);
-      EXPECT_TRUE(std::is_nothrow_copy_assignable<arch>::value);
-      EXPECT_TRUE(absl::is_trivially_copy_assignable<arch>::value);
-      break;
-  }
-}
-
-TYPED_TEST_P(ProfileTest, HasAppropriateComparisonProperties) {
-  using profile = typename TypeParam::profile;
-  using arch = typename TypeParam::arch;
-  using expected_profile = typename TypeParam::expected_profile;
-
-  using props = ti::PropertiesOfT<profile>;
-  using arch_props = ti::PropertiesOfArchetypeT<arch>;
-  using expected_props = ti::PropertiesOfT<expected_profile>;
-
-  // Make sure all of the properties are as expected.
-  // There are seemingly redundant tests here to make it easier to diagnose
-  // the specifics of the failure if something were to go wrong.
-  EXPECT_TRUE((std::is_same<props, arch_props>::value));
-  EXPECT_TRUE((std::is_same<props, expected_props>::value));
-  EXPECT_TRUE((std::is_same<arch_props, expected_props>::value));
-
-  EXPECT_EQ(props::equality_comparable_support,
-            expected_props::equality_comparable_support);
-
-  EXPECT_EQ(props::inequality_comparable_support,
-            expected_props::inequality_comparable_support);
-
-  EXPECT_EQ(props::less_than_comparable_support,
-            expected_props::less_than_comparable_support);
-
-  EXPECT_EQ(props::less_equal_comparable_support,
-            expected_props::less_equal_comparable_support);
-
-  EXPECT_EQ(props::greater_equal_comparable_support,
-            expected_props::greater_equal_comparable_support);
-
-  EXPECT_EQ(props::greater_than_comparable_support,
-            expected_props::greater_than_comparable_support);
-
-  // Avoid additional error message noise when profile and archetype match with
-  // each other but were not what was expected.
-  if (!std::is_same<props, arch_props>::value) {
-    EXPECT_EQ(arch_props::equality_comparable_support,
-              expected_props::equality_comparable_support);
-
-    EXPECT_EQ(arch_props::inequality_comparable_support,
-              expected_props::inequality_comparable_support);
-
-    EXPECT_EQ(arch_props::less_than_comparable_support,
-              expected_props::less_than_comparable_support);
-
-    EXPECT_EQ(arch_props::less_equal_comparable_support,
-              expected_props::less_equal_comparable_support);
-
-    EXPECT_EQ(arch_props::greater_equal_comparable_support,
-              expected_props::greater_equal_comparable_support);
-
-    EXPECT_EQ(arch_props::greater_than_comparable_support,
-              expected_props::greater_than_comparable_support);
-  }
-
-  //////////////////////////////////////////////////////////////////////////////
-  //                        Equality comparable checks                        //
-  //////////////////////////////////////////////////////////////////////////////
-  switch (expected_props::equality_comparable_support) {
-    case ti::equality_comparable::maybe:
-      EXPECT_FALSE(EqualityComparable<arch>::value);
-      EXPECT_FALSE(NothrowEqualityComparable<arch>::value);
-      break;
-    case ti::equality_comparable::yes:
-      EXPECT_TRUE(EqualityComparable<arch>::value);
-      EXPECT_FALSE(NothrowEqualityComparable<arch>::value);
-      break;
-    case ti::equality_comparable::nothrow:
-      EXPECT_TRUE(EqualityComparable<arch>::value);
-      EXPECT_TRUE(NothrowEqualityComparable<arch>::value);
-      break;
-  }
-
-  //////////////////////////////////////////////////////////////////////////////
-  //                       Inequality comparable checks                       //
-  //////////////////////////////////////////////////////////////////////////////
-  switch (expected_props::inequality_comparable_support) {
-    case ti::inequality_comparable::maybe:
-      EXPECT_FALSE(InequalityComparable<arch>::value);
-      EXPECT_FALSE(NothrowInequalityComparable<arch>::value);
-      break;
-    case ti::inequality_comparable::yes:
-      EXPECT_TRUE(InequalityComparable<arch>::value);
-      EXPECT_FALSE(NothrowInequalityComparable<arch>::value);
-      break;
-    case ti::inequality_comparable::nothrow:
-      EXPECT_TRUE(InequalityComparable<arch>::value);
-      EXPECT_TRUE(NothrowInequalityComparable<arch>::value);
-      break;
-  }
-
-  //////////////////////////////////////////////////////////////////////////////
-  //                       Less than comparable checks                        //
-  //////////////////////////////////////////////////////////////////////////////
-  switch (expected_props::less_than_comparable_support) {
-    case ti::less_than_comparable::maybe:
-      EXPECT_FALSE(LessThanComparable<arch>::value);
-      EXPECT_FALSE(NothrowLessThanComparable<arch>::value);
-      break;
-    case ti::less_than_comparable::yes:
-      EXPECT_TRUE(LessThanComparable<arch>::value);
-      EXPECT_FALSE(NothrowLessThanComparable<arch>::value);
-      break;
-    case ti::less_than_comparable::nothrow:
-      EXPECT_TRUE(LessThanComparable<arch>::value);
-      EXPECT_TRUE(NothrowLessThanComparable<arch>::value);
-      break;
-  }
-
-  //////////////////////////////////////////////////////////////////////////////
-  //                      Less equal comparable checks                        //
-  //////////////////////////////////////////////////////////////////////////////
-  switch (expected_props::less_equal_comparable_support) {
-    case ti::less_equal_comparable::maybe:
-      EXPECT_FALSE(LessEqualComparable<arch>::value);
-      EXPECT_FALSE(NothrowLessEqualComparable<arch>::value);
-      break;
-    case ti::less_equal_comparable::yes:
-      EXPECT_TRUE(LessEqualComparable<arch>::value);
-      EXPECT_FALSE(NothrowLessEqualComparable<arch>::value);
-      break;
-    case ti::less_equal_comparable::nothrow:
-      EXPECT_TRUE(LessEqualComparable<arch>::value);
-      EXPECT_TRUE(NothrowLessEqualComparable<arch>::value);
-      break;
-  }
-
-  //////////////////////////////////////////////////////////////////////////////
-  //                     Greater equal comparable checks                      //
-  //////////////////////////////////////////////////////////////////////////////
-  switch (expected_props::greater_equal_comparable_support) {
-    case ti::greater_equal_comparable::maybe:
-      EXPECT_FALSE(GreaterEqualComparable<arch>::value);
-      EXPECT_FALSE(NothrowGreaterEqualComparable<arch>::value);
-      break;
-    case ti::greater_equal_comparable::yes:
-      EXPECT_TRUE(GreaterEqualComparable<arch>::value);
-      EXPECT_FALSE(NothrowGreaterEqualComparable<arch>::value);
-      break;
-    case ti::greater_equal_comparable::nothrow:
-      EXPECT_TRUE(GreaterEqualComparable<arch>::value);
-      EXPECT_TRUE(NothrowGreaterEqualComparable<arch>::value);
-      break;
-  }
-
-  //////////////////////////////////////////////////////////////////////////////
-  //                     Greater than comparable checks                       //
-  //////////////////////////////////////////////////////////////////////////////
-  switch (expected_props::greater_than_comparable_support) {
-    case ti::greater_than_comparable::maybe:
-      EXPECT_FALSE(GreaterThanComparable<arch>::value);
-      EXPECT_FALSE(NothrowGreaterThanComparable<arch>::value);
-      break;
-    case ti::greater_than_comparable::yes:
-      EXPECT_TRUE(GreaterThanComparable<arch>::value);
-      EXPECT_FALSE(NothrowGreaterThanComparable<arch>::value);
-      break;
-    case ti::greater_than_comparable::nothrow:
-      EXPECT_TRUE(GreaterThanComparable<arch>::value);
-      EXPECT_TRUE(NothrowGreaterThanComparable<arch>::value);
-      break;
-  }
-}
-
-TYPED_TEST_P(ProfileTest, HasAppropriateAuxilliaryProperties) {
-  using profile = typename TypeParam::profile;
-  using arch = typename TypeParam::arch;
-  using expected_profile = typename TypeParam::expected_profile;
-
-  using props = ti::PropertiesOfT<profile>;
-  using arch_props = ti::PropertiesOfArchetypeT<arch>;
-  using expected_props = ti::PropertiesOfT<expected_profile>;
-
-  // Make sure all of the properties are as expected.
-  // There are seemingly redundant tests here to make it easier to diagnose
-  // the specifics of the failure if something were to go wrong.
-  EXPECT_TRUE((std::is_same<props, arch_props>::value));
-  EXPECT_TRUE((std::is_same<props, expected_props>::value));
-  EXPECT_TRUE((std::is_same<arch_props, expected_props>::value));
-
-  EXPECT_EQ(props::swappable_support, expected_props::swappable_support);
-
-  EXPECT_EQ(props::hashable_support, expected_props::hashable_support);
-
-  // Avoid additional error message noise when profile and archetype match with
-  // each other but were not what was expected.
-  if (!std::is_same<props, arch_props>::value) {
-    EXPECT_EQ(arch_props::swappable_support, expected_props::swappable_support);
-
-    EXPECT_EQ(arch_props::hashable_support, expected_props::hashable_support);
-  }
-
-  //////////////////////////////////////////////////////////////////////////////
-  //                            Swappable checks                              //
-  //////////////////////////////////////////////////////////////////////////////
-  switch (expected_props::swappable_support) {
-    case ti::swappable::maybe:
-      EXPECT_FALSE(absl::type_traits_internal::IsSwappable<arch>::value);
-      EXPECT_FALSE(absl::type_traits_internal::IsNothrowSwappable<arch>::value);
-      break;
-    case ti::swappable::yes:
-      EXPECT_TRUE(absl::type_traits_internal::IsSwappable<arch>::value);
-      EXPECT_FALSE(absl::type_traits_internal::IsNothrowSwappable<arch>::value);
-      break;
-    case ti::swappable::nothrow:
-      EXPECT_TRUE(absl::type_traits_internal::IsSwappable<arch>::value);
-      EXPECT_TRUE(absl::type_traits_internal::IsNothrowSwappable<arch>::value);
-      break;
-  }
-
-  //////////////////////////////////////////////////////////////////////////////
-  //                             Hashable checks                              //
-  //////////////////////////////////////////////////////////////////////////////
-  switch (expected_props::hashable_support) {
-    case ti::hashable::maybe:
-#if ABSL_META_INTERNAL_STD_HASH_SFINAE_FRIENDLY_
-      EXPECT_FALSE(absl::type_traits_internal::IsHashable<arch>::value);
-#endif  // ABSL_META_INTERNAL_STD_HASH_SFINAE_FRIENDLY_
-      break;
-    case ti::hashable::yes:
-      EXPECT_TRUE(absl::type_traits_internal::IsHashable<arch>::value);
-      break;
-  }
-}
-
-REGISTER_TYPED_TEST_SUITE_P(ProfileTest, HasAppropriateConstructionProperties,
-                            HasAppropriateAssignmentProperties,
-                            HasAppropriateComparisonProperties,
-                            HasAppropriateAuxilliaryProperties);
-
-template <class Profile, class Arch, class ExpectedProfile>
-struct ProfileAndExpectation {
-  using profile = Profile;
-  using arch = Arch;
-  using expected_profile = ExpectedProfile;
-};
-
-using CoreProfilesToTest = ::testing::Types<
-    // The terminating case of combine (all properties are "maybe").
-    ProfileAndExpectation<ti::CombineProfiles<>,
-                          ti::Archetype<ti::CombineProfiles<>>,
-                          ti::ConformanceProfile<>>,
-
-    // Core default constructor profiles
-    ProfileAndExpectation<
-        ti::HasDefaultConstructorProfile, ti::HasDefaultConstructorArchetype,
-        ti::ConformanceProfile<ti::default_constructible::yes>>,
-    ProfileAndExpectation<
-        ti::HasNothrowDefaultConstructorProfile,
-        ti::HasNothrowDefaultConstructorArchetype,
-        ti::ConformanceProfile<ti::default_constructible::nothrow>>,
-    ProfileAndExpectation<
-        ti::HasTrivialDefaultConstructorProfile,
-        ti::HasTrivialDefaultConstructorArchetype,
-        ti::ConformanceProfile<ti::default_constructible::trivial>>,
-
-    // Core move constructor profiles
-    ProfileAndExpectation<
-        ti::HasMoveConstructorProfile, ti::HasMoveConstructorArchetype,
-        ti::ConformanceProfile<ti::default_constructible::maybe,
-                               ti::move_constructible::yes>>,
-    ProfileAndExpectation<
-        ti::HasNothrowMoveConstructorProfile,
-        ti::HasNothrowMoveConstructorArchetype,
-        ti::ConformanceProfile<ti::default_constructible::maybe,
-                               ti::move_constructible::nothrow>>,
-    ProfileAndExpectation<
-        ti::HasTrivialMoveConstructorProfile,
-        ti::HasTrivialMoveConstructorArchetype,
-        ti::ConformanceProfile<ti::default_constructible::maybe,
-                               ti::move_constructible::trivial>>,
-
-    // Core copy constructor profiles
-    ProfileAndExpectation<
-        ti::HasCopyConstructorProfile, ti::HasCopyConstructorArchetype,
-        ti::ConformanceProfile<ti::default_constructible::maybe,
-                               ti::move_constructible::maybe,
-                               ti::copy_constructible::yes>>,
-    ProfileAndExpectation<
-        ti::HasNothrowCopyConstructorProfile,
-        ti::HasNothrowCopyConstructorArchetype,
-        ti::ConformanceProfile<ti::default_constructible::maybe,
-                               ti::move_constructible::maybe,
-                               ti::copy_constructible::nothrow>>,
-    ProfileAndExpectation<
-        ti::HasTrivialCopyConstructorProfile,
-        ti::HasTrivialCopyConstructorArchetype,
-        ti::ConformanceProfile<ti::default_constructible::maybe,
-                               ti::move_constructible::maybe,
-                               ti::copy_constructible::trivial>>,
-
-    // Core move assignment profiles
-    ProfileAndExpectation<
-        ti::HasMoveAssignProfile, ti::HasMoveAssignArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::maybe, ti::move_constructible::maybe,
-            ti::copy_constructible::maybe, ti::move_assignable::yes>>,
-    ProfileAndExpectation<
-        ti::HasNothrowMoveAssignProfile, ti::HasNothrowMoveAssignArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::maybe, ti::move_constructible::maybe,
-            ti::copy_constructible::maybe, ti::move_assignable::nothrow>>,
-    ProfileAndExpectation<
-        ti::HasTrivialMoveAssignProfile, ti::HasTrivialMoveAssignArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::maybe, ti::move_constructible::maybe,
-            ti::copy_constructible::maybe, ti::move_assignable::trivial>>,
-
-    // Core copy assignment profiles
-    ProfileAndExpectation<
-        ti::HasCopyAssignProfile, ti::HasCopyAssignArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::maybe, ti::move_constructible::maybe,
-            ti::copy_constructible::maybe, ti::move_assignable::maybe,
-            ti::copy_assignable::yes>>,
-    ProfileAndExpectation<
-        ti::HasNothrowCopyAssignProfile, ti::HasNothrowCopyAssignArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::maybe, ti::move_constructible::maybe,
-            ti::copy_constructible::maybe, ti::move_assignable::maybe,
-            ti::copy_assignable::nothrow>>,
-    ProfileAndExpectation<
-        ti::HasTrivialCopyAssignProfile, ti::HasTrivialCopyAssignArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::maybe, ti::move_constructible::maybe,
-            ti::copy_constructible::maybe, ti::move_assignable::maybe,
-            ti::copy_assignable::trivial>>,
-
-    // Core destructor profiles
-    ProfileAndExpectation<
-        ti::HasDestructorProfile, ti::HasDestructorArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::maybe, ti::move_constructible::maybe,
-            ti::copy_constructible::maybe, ti::move_assignable::maybe,
-            ti::copy_assignable::maybe, ti::destructible::yes>>,
-    ProfileAndExpectation<
-        ti::HasNothrowDestructorProfile, ti::HasNothrowDestructorArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::maybe, ti::move_constructible::maybe,
-            ti::copy_constructible::maybe, ti::move_assignable::maybe,
-            ti::copy_assignable::maybe, ti::destructible::nothrow>>,
-    ProfileAndExpectation<
-        ti::HasTrivialDestructorProfile, ti::HasTrivialDestructorArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::maybe, ti::move_constructible::maybe,
-            ti::copy_constructible::maybe, ti::move_assignable::maybe,
-            ti::copy_assignable::maybe, ti::destructible::trivial>>,
-
-    // Core equality comparable profiles
-    ProfileAndExpectation<
-        ti::HasEqualityProfile, ti::HasEqualityArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::maybe, ti::move_constructible::maybe,
-            ti::copy_constructible::maybe, ti::move_assignable::maybe,
-            ti::copy_assignable::maybe, ti::destructible::maybe,
-            ti::equality_comparable::yes>>,
-    ProfileAndExpectation<
-        ti::HasNothrowEqualityProfile, ti::HasNothrowEqualityArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::maybe, ti::move_constructible::maybe,
-            ti::copy_constructible::maybe, ti::move_assignable::maybe,
-            ti::copy_assignable::maybe, ti::destructible::maybe,
-            ti::equality_comparable::nothrow>>,
-
-    // Core inequality comparable profiles
-    ProfileAndExpectation<
-        ti::HasInequalityProfile, ti::HasInequalityArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::maybe, ti::move_constructible::maybe,
-            ti::copy_constructible::maybe, ti::move_assignable::maybe,
-            ti::copy_assignable::maybe, ti::destructible::maybe,
-            ti::equality_comparable::maybe, ti::inequality_comparable::yes>>,
-    ProfileAndExpectation<
-        ti::HasNothrowInequalityProfile, ti::HasNothrowInequalityArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::maybe, ti::move_constructible::maybe,
-            ti::copy_constructible::maybe, ti::move_assignable::maybe,
-            ti::copy_assignable::maybe, ti::destructible::maybe,
-            ti::equality_comparable::maybe,
-            ti::inequality_comparable::nothrow>>,
-
-    // Core less than comparable profiles
-    ProfileAndExpectation<
-        ti::HasLessThanProfile, ti::HasLessThanArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::maybe, ti::move_constructible::maybe,
-            ti::copy_constructible::maybe, ti::move_assignable::maybe,
-            ti::copy_assignable::maybe, ti::destructible::maybe,
-            ti::equality_comparable::maybe, ti::inequality_comparable::maybe,
-            ti::less_than_comparable::yes>>,
-    ProfileAndExpectation<
-        ti::HasNothrowLessThanProfile, ti::HasNothrowLessThanArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::maybe, ti::move_constructible::maybe,
-            ti::copy_constructible::maybe, ti::move_assignable::maybe,
-            ti::copy_assignable::maybe, ti::destructible::maybe,
-            ti::equality_comparable::maybe, ti::inequality_comparable::maybe,
-            ti::less_than_comparable::nothrow>>,
-
-    // Core less equal comparable profiles
-    ProfileAndExpectation<
-        ti::HasLessEqualProfile, ti::HasLessEqualArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::maybe, ti::move_constructible::maybe,
-            ti::copy_constructible::maybe, ti::move_assignable::maybe,
-            ti::copy_assignable::maybe, ti::destructible::maybe,
-            ti::equality_comparable::maybe, ti::inequality_comparable::maybe,
-            ti::less_than_comparable::maybe, ti::less_equal_comparable::yes>>,
-    ProfileAndExpectation<
-        ti::HasNothrowLessEqualProfile, ti::HasNothrowLessEqualArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::maybe, ti::move_constructible::maybe,
-            ti::copy_constructible::maybe, ti::move_assignable::maybe,
-            ti::copy_assignable::maybe, ti::destructible::maybe,
-            ti::equality_comparable::maybe, ti::inequality_comparable::maybe,
-            ti::less_than_comparable::maybe,
-            ti::less_equal_comparable::nothrow>>,
-
-    // Core greater equal comparable profiles
-    ProfileAndExpectation<
-        ti::HasGreaterEqualProfile, ti::HasGreaterEqualArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::maybe, ti::move_constructible::maybe,
-            ti::copy_constructible::maybe, ti::move_assignable::maybe,
-            ti::copy_assignable::maybe, ti::destructible::maybe,
-            ti::equality_comparable::maybe, ti::inequality_comparable::maybe,
-            ti::less_than_comparable::maybe, ti::less_equal_comparable::maybe,
-            ti::greater_equal_comparable::yes>>,
-    ProfileAndExpectation<
-        ti::HasNothrowGreaterEqualProfile, ti::HasNothrowGreaterEqualArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::maybe, ti::move_constructible::maybe,
-            ti::copy_constructible::maybe, ti::move_assignable::maybe,
-            ti::copy_assignable::maybe, ti::destructible::maybe,
-            ti::equality_comparable::maybe, ti::inequality_comparable::maybe,
-            ti::less_than_comparable::maybe, ti::less_equal_comparable::maybe,
-            ti::greater_equal_comparable::nothrow>>,
-
-    // Core greater than comparable profiles
-    ProfileAndExpectation<
-        ti::HasGreaterThanProfile, ti::HasGreaterThanArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::maybe, ti::move_constructible::maybe,
-            ti::copy_constructible::maybe, ti::move_assignable::maybe,
-            ti::copy_assignable::maybe, ti::destructible::maybe,
-            ti::equality_comparable::maybe, ti::inequality_comparable::maybe,
-            ti::less_than_comparable::maybe, ti::less_equal_comparable::maybe,
-            ti::greater_equal_comparable::maybe,
-            ti::greater_than_comparable::yes>>,
-    ProfileAndExpectation<
-        ti::HasNothrowGreaterThanProfile, ti::HasNothrowGreaterThanArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::maybe, ti::move_constructible::maybe,
-            ti::copy_constructible::maybe, ti::move_assignable::maybe,
-            ti::copy_assignable::maybe, ti::destructible::maybe,
-            ti::equality_comparable::maybe, ti::inequality_comparable::maybe,
-            ti::less_than_comparable::maybe, ti::less_equal_comparable::maybe,
-            ti::greater_equal_comparable::maybe,
-            ti::greater_than_comparable::nothrow>>,
-
-    // Core swappable profiles
-    ProfileAndExpectation<
-        ti::HasSwapProfile, ti::HasSwapArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::maybe, ti::move_constructible::maybe,
-            ti::copy_constructible::maybe, ti::move_assignable::maybe,
-            ti::copy_assignable::maybe, ti::destructible::maybe,
-            ti::equality_comparable::maybe, ti::inequality_comparable::maybe,
-            ti::less_than_comparable::maybe, ti::less_equal_comparable::maybe,
-            ti::greater_equal_comparable::maybe,
-            ti::greater_than_comparable::maybe, ti::swappable::yes>>,
-    ProfileAndExpectation<
-        ti::HasNothrowSwapProfile, ti::HasNothrowSwapArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::maybe, ti::move_constructible::maybe,
-            ti::copy_constructible::maybe, ti::move_assignable::maybe,
-            ti::copy_assignable::maybe, ti::destructible::maybe,
-            ti::equality_comparable::maybe, ti::inequality_comparable::maybe,
-            ti::less_than_comparable::maybe, ti::less_equal_comparable::maybe,
-            ti::greater_equal_comparable::maybe,
-            ti::greater_than_comparable::maybe, ti::swappable::nothrow>>,
-
-    // Core hashable profiles
-    ProfileAndExpectation<
-        ti::HasStdHashSpecializationProfile,
-        ti::HasStdHashSpecializationArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::maybe, ti::move_constructible::maybe,
-            ti::copy_constructible::maybe, ti::move_assignable::maybe,
-            ti::copy_assignable::maybe, ti::destructible::maybe,
-            ti::equality_comparable::maybe, ti::inequality_comparable::maybe,
-            ti::less_than_comparable::maybe, ti::less_equal_comparable::maybe,
-            ti::greater_equal_comparable::maybe,
-            ti::greater_than_comparable::maybe, ti::swappable::maybe,
-            ti::hashable::yes>>>;
-
-using CommonProfilesToTest = ::testing::Types<
-    // NothrowMoveConstructible
-    ProfileAndExpectation<
-        ti::NothrowMoveConstructibleProfile,
-        ti::NothrowMoveConstructibleArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::maybe, ti::move_constructible::nothrow,
-            ti::copy_constructible::maybe, ti::move_assignable::maybe,
-            ti::copy_assignable::maybe, ti::destructible::nothrow>>,
-
-    // CopyConstructible
-    ProfileAndExpectation<
-        ti::CopyConstructibleProfile, ti::CopyConstructibleArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::maybe, ti::move_constructible::nothrow,
-            ti::copy_constructible::yes, ti::move_assignable::maybe,
-            ti::copy_assignable::maybe, ti::destructible::nothrow>>,
-
-    // NothrowMovable
-    ProfileAndExpectation<
-        ti::NothrowMovableProfile, ti::NothrowMovableArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::maybe, ti::move_constructible::nothrow,
-            ti::copy_constructible::maybe, ti::move_assignable::nothrow,
-            ti::copy_assignable::maybe, ti::destructible::nothrow,
-            ti::equality_comparable::maybe, ti::inequality_comparable::maybe,
-            ti::less_than_comparable::maybe, ti::less_equal_comparable::maybe,
-            ti::greater_equal_comparable::maybe,
-            ti::greater_than_comparable::maybe, ti::swappable::nothrow>>,
-
-    // Value
-    ProfileAndExpectation<
-        ti::ValueProfile, ti::ValueArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::maybe, ti::move_constructible::nothrow,
-            ti::copy_constructible::yes, ti::move_assignable::nothrow,
-            ti::copy_assignable::yes, ti::destructible::nothrow,
-            ti::equality_comparable::maybe, ti::inequality_comparable::maybe,
-            ti::less_than_comparable::maybe, ti::less_equal_comparable::maybe,
-            ti::greater_equal_comparable::maybe,
-            ti::greater_than_comparable::maybe, ti::swappable::nothrow>>,
-
-    ////////////////////////////////////////////////////////////////////////////
-    //                  Common but also DefaultConstructible                  //
-    ////////////////////////////////////////////////////////////////////////////
-
-    // DefaultConstructibleNothrowMoveConstructible
-    ProfileAndExpectation<
-        ti::DefaultConstructibleNothrowMoveConstructibleProfile,
-        ti::DefaultConstructibleNothrowMoveConstructibleArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::yes, ti::move_constructible::nothrow,
-            ti::copy_constructible::maybe, ti::move_assignable::maybe,
-            ti::copy_assignable::maybe, ti::destructible::nothrow>>,
-
-    // DefaultConstructibleCopyConstructible
-    ProfileAndExpectation<
-        ti::DefaultConstructibleCopyConstructibleProfile,
-        ti::DefaultConstructibleCopyConstructibleArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::yes, ti::move_constructible::nothrow,
-            ti::copy_constructible::yes, ti::move_assignable::maybe,
-            ti::copy_assignable::maybe, ti::destructible::nothrow>>,
-
-    // DefaultConstructibleNothrowMovable
-    ProfileAndExpectation<
-        ti::DefaultConstructibleNothrowMovableProfile,
-        ti::DefaultConstructibleNothrowMovableArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::yes, ti::move_constructible::nothrow,
-            ti::copy_constructible::maybe, ti::move_assignable::nothrow,
-            ti::copy_assignable::maybe, ti::destructible::nothrow,
-            ti::equality_comparable::maybe, ti::inequality_comparable::maybe,
-            ti::less_than_comparable::maybe, ti::less_equal_comparable::maybe,
-            ti::greater_equal_comparable::maybe,
-            ti::greater_than_comparable::maybe, ti::swappable::nothrow>>,
-
-    // DefaultConstructibleValue
-    ProfileAndExpectation<
-        ti::DefaultConstructibleValueProfile,
-        ti::DefaultConstructibleValueArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::yes, ti::move_constructible::nothrow,
-            ti::copy_constructible::yes, ti::move_assignable::nothrow,
-            ti::copy_assignable::yes, ti::destructible::nothrow,
-            ti::equality_comparable::maybe, ti::inequality_comparable::maybe,
-            ti::less_than_comparable::maybe, ti::less_equal_comparable::maybe,
-            ti::greater_equal_comparable::maybe,
-            ti::greater_than_comparable::maybe, ti::swappable::nothrow>>>;
-
-using ComparableHelpersProfilesToTest = ::testing::Types<
-    // Equatable
-    ProfileAndExpectation<
-        ti::EquatableProfile, ti::EquatableArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::maybe, ti::move_constructible::maybe,
-            ti::copy_constructible::maybe, ti::move_assignable::maybe,
-            ti::copy_assignable::maybe, ti::destructible::maybe,
-            ti::equality_comparable::yes, ti::inequality_comparable::yes>>,
-
-    // Comparable
-    ProfileAndExpectation<
-        ti::ComparableProfile, ti::ComparableArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::maybe, ti::move_constructible::maybe,
-            ti::copy_constructible::maybe, ti::move_assignable::maybe,
-            ti::copy_assignable::maybe, ti::destructible::maybe,
-            ti::equality_comparable::yes, ti::inequality_comparable::yes,
-            ti::less_than_comparable::yes, ti::less_equal_comparable::yes,
-            ti::greater_equal_comparable::yes,
-            ti::greater_than_comparable::yes>>,
-
-    // NothrowEquatable
-    ProfileAndExpectation<
-        ti::NothrowEquatableProfile, ti::NothrowEquatableArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::maybe, ti::move_constructible::maybe,
-            ti::copy_constructible::maybe, ti::move_assignable::maybe,
-            ti::copy_assignable::maybe, ti::destructible::maybe,
-            ti::equality_comparable::nothrow,
-            ti::inequality_comparable::nothrow>>,
-
-    // NothrowComparable
-    ProfileAndExpectation<
-        ti::NothrowComparableProfile, ti::NothrowComparableArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::maybe, ti::move_constructible::maybe,
-            ti::copy_constructible::maybe, ti::move_assignable::maybe,
-            ti::copy_assignable::maybe, ti::destructible::maybe,
-            ti::equality_comparable::nothrow,
-            ti::inequality_comparable::nothrow,
-            ti::less_than_comparable::nothrow,
-            ti::less_equal_comparable::nothrow,
-            ti::greater_equal_comparable::nothrow,
-            ti::greater_than_comparable::nothrow>>>;
-
-using CommonComparableProfilesToTest = ::testing::Types<
-    // ComparableNothrowMoveConstructible
-    ProfileAndExpectation<
-        ti::ComparableNothrowMoveConstructibleProfile,
-        ti::ComparableNothrowMoveConstructibleArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::maybe, ti::move_constructible::nothrow,
-            ti::copy_constructible::maybe, ti::move_assignable::maybe,
-            ti::copy_assignable::maybe, ti::destructible::nothrow,
-            ti::equality_comparable::yes, ti::inequality_comparable::yes,
-            ti::less_than_comparable::yes, ti::less_equal_comparable::yes,
-            ti::greater_equal_comparable::yes,
-            ti::greater_than_comparable::yes>>,
-
-    // ComparableCopyConstructible
-    ProfileAndExpectation<
-        ti::ComparableCopyConstructibleProfile,
-        ti::ComparableCopyConstructibleArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::maybe, ti::move_constructible::nothrow,
-            ti::copy_constructible::yes, ti::move_assignable::maybe,
-            ti::copy_assignable::maybe, ti::destructible::nothrow,
-            ti::equality_comparable::yes, ti::inequality_comparable::yes,
-            ti::less_than_comparable::yes, ti::less_equal_comparable::yes,
-            ti::greater_equal_comparable::yes,
-            ti::greater_than_comparable::yes>>,
-
-    // ComparableNothrowMovable
-    ProfileAndExpectation<
-        ti::ComparableNothrowMovableProfile,
-        ti::ComparableNothrowMovableArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::maybe, ti::move_constructible::nothrow,
-            ti::copy_constructible::maybe, ti::move_assignable::nothrow,
-            ti::copy_assignable::maybe, ti::destructible::nothrow,
-            ti::equality_comparable::yes, ti::inequality_comparable::yes,
-            ti::less_than_comparable::yes, ti::less_equal_comparable::yes,
-            ti::greater_equal_comparable::yes, ti::greater_than_comparable::yes,
-            ti::swappable::nothrow>>,
-
-    // ComparableValue
-    ProfileAndExpectation<
-        ti::ComparableValueProfile, ti::ComparableValueArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::maybe, ti::move_constructible::nothrow,
-            ti::copy_constructible::yes, ti::move_assignable::nothrow,
-            ti::copy_assignable::yes, ti::destructible::nothrow,
-            ti::equality_comparable::yes, ti::inequality_comparable::yes,
-            ti::less_than_comparable::yes, ti::less_equal_comparable::yes,
-            ti::greater_equal_comparable::yes, ti::greater_than_comparable::yes,
-            ti::swappable::nothrow>>>;
-
-using TrivialProfilesToTest = ::testing::Types<
-    ProfileAndExpectation<
-        ti::TrivialSpecialMemberFunctionsProfile,
-        ti::TrivialSpecialMemberFunctionsArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::trivial, ti::move_constructible::trivial,
-            ti::copy_constructible::trivial, ti::move_assignable::trivial,
-            ti::copy_assignable::trivial, ti::destructible::trivial,
-            ti::equality_comparable::maybe, ti::inequality_comparable::maybe,
-            ti::less_than_comparable::maybe, ti::less_equal_comparable::maybe,
-            ti::greater_equal_comparable::maybe,
-            ti::greater_than_comparable::maybe, ti::swappable::nothrow>>,
-
-    ProfileAndExpectation<
-        ti::TriviallyCompleteProfile, ti::TriviallyCompleteArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::trivial, ti::move_constructible::trivial,
-            ti::copy_constructible::trivial, ti::move_assignable::trivial,
-            ti::copy_assignable::trivial, ti::destructible::trivial,
-            ti::equality_comparable::yes, ti::inequality_comparable::yes,
-            ti::less_than_comparable::yes, ti::less_equal_comparable::yes,
-            ti::greater_equal_comparable::yes, ti::greater_than_comparable::yes,
-            ti::swappable::nothrow, ti::hashable::yes>>>;
-
-INSTANTIATE_TYPED_TEST_SUITE_P(Core, ProfileTest, CoreProfilesToTest);
-INSTANTIATE_TYPED_TEST_SUITE_P(Common, ProfileTest, CommonProfilesToTest);
-INSTANTIATE_TYPED_TEST_SUITE_P(ComparableHelpers, ProfileTest,
-                               ComparableHelpersProfilesToTest);
-INSTANTIATE_TYPED_TEST_SUITE_P(CommonComparable, ProfileTest,
-                               CommonComparableProfilesToTest);
-INSTANTIATE_TYPED_TEST_SUITE_P(Trivial, ProfileTest, TrivialProfilesToTest);
-
-TEST(ConformanceTestingTest, Basic) {
-  using profile = ti::CombineProfiles<ti::TriviallyCompleteProfile,
-                                      ti::NothrowComparableProfile>;
-
-  using lim = std::numeric_limits<float>;
-
-  ABSL_INTERNAL_ASSERT_CONFORMANCE_OF(float)
-      .INITIALIZER(-lim::infinity())
-      .INITIALIZER(lim::lowest())
-      .INITIALIZER(-1.f)
-      .INITIALIZER(-lim::min())
-      .EQUIVALENCE_CLASS(INITIALIZER(-0.f), INITIALIZER(0.f))
-      .INITIALIZER(lim::min())
-      .INITIALIZER(1.f)
-      .INITIALIZER(lim::max())
-      .INITIALIZER(lim::infinity())
-      .WITH_STRICT_PROFILE(absl::types_internal::RegularityDomain, profile);
-}
-
-struct BadMoveConstruct {
-  BadMoveConstruct() = default;
-  BadMoveConstruct(BadMoveConstruct&& other) noexcept
-      : value(other.value + 1) {}
-  BadMoveConstruct& operator=(BadMoveConstruct&& other) noexcept = default;
-  int value = 0;
-
-  friend bool operator==(BadMoveConstruct const& lhs,
-                         BadMoveConstruct const& rhs) {
-    return lhs.value == rhs.value;
-  }
-  friend bool operator!=(BadMoveConstruct const& lhs,
-                         BadMoveConstruct const& rhs) {
-    return lhs.value != rhs.value;
-  }
-};
-
-struct BadMoveAssign {
-  BadMoveAssign() = default;
-  BadMoveAssign(BadMoveAssign&& other) noexcept = default;
-  BadMoveAssign& operator=(BadMoveAssign&& other) noexcept {
-    int new_value = other.value + 1;
-    value = new_value;
-    return *this;
-  }
-  int value = 0;
-
-  friend bool operator==(BadMoveAssign const& lhs, BadMoveAssign const& rhs) {
-    return lhs.value == rhs.value;
-  }
-  friend bool operator!=(BadMoveAssign const& lhs, BadMoveAssign const& rhs) {
-    return lhs.value != rhs.value;
-  }
-};
-
-enum class WhichCompIsBad { eq, ne, lt, le, ge, gt };
-
-template <WhichCompIsBad Which>
-struct BadCompare {
-  int value;
-
-  friend bool operator==(BadCompare const& lhs, BadCompare const& rhs) {
-    return Which == WhichCompIsBad::eq ? lhs.value != rhs.value
-                                       : lhs.value == rhs.value;
-  }
-
-  friend bool operator!=(BadCompare const& lhs, BadCompare const& rhs) {
-    return Which == WhichCompIsBad::ne ? lhs.value == rhs.value
-                                       : lhs.value != rhs.value;
-  }
-
-  friend bool operator<(BadCompare const& lhs, BadCompare const& rhs) {
-    return Which == WhichCompIsBad::lt ? lhs.value >= rhs.value
-                                       : lhs.value < rhs.value;
-  }
-
-  friend bool operator<=(BadCompare const& lhs, BadCompare const& rhs) {
-    return Which == WhichCompIsBad::le ? lhs.value > rhs.value
-                                       : lhs.value <= rhs.value;
-  }
-
-  friend bool operator>=(BadCompare const& lhs, BadCompare const& rhs) {
-    return Which == WhichCompIsBad::ge ? lhs.value < rhs.value
-                                       : lhs.value >= rhs.value;
-  }
-
-  friend bool operator>(BadCompare const& lhs, BadCompare const& rhs) {
-    return Which == WhichCompIsBad::gt ? lhs.value <= rhs.value
-                                       : lhs.value > rhs.value;
-  }
-};
-
-TEST(ConformanceTestingDeathTest, Failures) {
-  {
-    using profile = ti::CombineProfiles<ti::TriviallyCompleteProfile,
-                                        ti::NothrowComparableProfile>;
-
-    // Note: The initializers are intentionally in the wrong order.
-    ABSL_INTERNAL_ASSERT_NONCONFORMANCE_OF(float)
-        .INITIALIZER(1.f)
-        .INITIALIZER(0.f)
-        .WITH_LOOSE_PROFILE(profile);
-  }
-
-  {
-    using profile =
-        ti::CombineProfiles<ti::NothrowMovableProfile, ti::EquatableProfile>;
-
-    ABSL_INTERNAL_ASSERT_NONCONFORMANCE_OF(BadMoveConstruct)
-        .DUE_TO("Move construction")
-        .INITIALIZER(BadMoveConstruct())
-        .WITH_LOOSE_PROFILE(profile);
-  }
-
-  {
-    using profile =
-        ti::CombineProfiles<ti::NothrowMovableProfile, ti::EquatableProfile>;
-
-    ABSL_INTERNAL_ASSERT_NONCONFORMANCE_OF(BadMoveAssign)
-        .DUE_TO("Move assignment")
-        .INITIALIZER(BadMoveAssign())
-        .WITH_LOOSE_PROFILE(profile);
-  }
-}
-
-TEST(ConformanceTestingDeathTest, CompFailures) {
-  using profile = ti::ComparableProfile;
-
-  {
-    using BadComp = BadCompare<WhichCompIsBad::eq>;
-
-    ABSL_INTERNAL_ASSERT_NONCONFORMANCE_OF(BadComp)
-        .DUE_TO("Comparison")
-        .INITIALIZER(BadComp{0})
-        .INITIALIZER(BadComp{1})
-        .WITH_LOOSE_PROFILE(profile);
-  }
-
-  {
-    using BadComp = BadCompare<WhichCompIsBad::ne>;
-
-    ABSL_INTERNAL_ASSERT_NONCONFORMANCE_OF(BadComp)
-        .DUE_TO("Comparison")
-        .INITIALIZER(BadComp{0})
-        .INITIALIZER(BadComp{1})
-        .WITH_LOOSE_PROFILE(profile);
-  }
-
-  {
-    using BadComp = BadCompare<WhichCompIsBad::lt>;
-
-    ABSL_INTERNAL_ASSERT_NONCONFORMANCE_OF(BadComp)
-        .DUE_TO("Comparison")
-        .INITIALIZER(BadComp{0})
-        .INITIALIZER(BadComp{1})
-        .WITH_LOOSE_PROFILE(profile);
-  }
-
-  {
-    using BadComp = BadCompare<WhichCompIsBad::le>;
-
-    ABSL_INTERNAL_ASSERT_NONCONFORMANCE_OF(BadComp)
-        .DUE_TO("Comparison")
-        .INITIALIZER(BadComp{0})
-        .INITIALIZER(BadComp{1})
-        .WITH_LOOSE_PROFILE(profile);
-  }
-
-  {
-    using BadComp = BadCompare<WhichCompIsBad::ge>;
-
-    ABSL_INTERNAL_ASSERT_NONCONFORMANCE_OF(BadComp)
-        .DUE_TO("Comparison")
-        .INITIALIZER(BadComp{0})
-        .INITIALIZER(BadComp{1})
-        .WITH_LOOSE_PROFILE(profile);
-  }
-
-  {
-    using BadComp = BadCompare<WhichCompIsBad::gt>;
-
-    ABSL_INTERNAL_ASSERT_NONCONFORMANCE_OF(BadComp)
-        .DUE_TO("Comparison")
-        .INITIALIZER(BadComp{0})
-        .INITIALIZER(BadComp{1})
-        .WITH_LOOSE_PROFILE(profile);
-  }
-}
-
-struct BadSelfMove {
-  BadSelfMove() = default;
-  BadSelfMove(BadSelfMove&&) = default;
-  BadSelfMove& operator=(BadSelfMove&& other) noexcept {
-    if (this == &other) {
-      broken_state = true;
-    }
-    return *this;
-  }
-
-  friend bool operator==(const BadSelfMove& lhs, const BadSelfMove& rhs) {
-    return !(lhs.broken_state || rhs.broken_state);
-  }
-
-  friend bool operator!=(const BadSelfMove& lhs, const BadSelfMove& rhs) {
-    return lhs.broken_state || rhs.broken_state;
-  }
-
-  bool broken_state = false;
-};
-
-TEST(ConformanceTestingDeathTest, SelfMoveFailure) {
-  using profile = ti::EquatableNothrowMovableProfile;
-
-  {
-    ABSL_INTERNAL_ASSERT_NONCONFORMANCE_OF(BadSelfMove)
-        .DUE_TO("Move assignment")
-        .INITIALIZER(BadSelfMove())
-        .WITH_LOOSE_PROFILE(profile);
-  }
-}
-
-struct BadSelfCopy {
-  BadSelfCopy() = default;
-  BadSelfCopy(BadSelfCopy&&) = default;
-  BadSelfCopy(const BadSelfCopy&) = default;
-  BadSelfCopy& operator=(BadSelfCopy&&) = default;
-  BadSelfCopy& operator=(BadSelfCopy const& other) {
-    if (this == &other) {
-      broken_state = true;
-    }
-    return *this;
-  }
-
-  friend bool operator==(const BadSelfCopy& lhs, const BadSelfCopy& rhs) {
-    return !(lhs.broken_state || rhs.broken_state);
-  }
-
-  friend bool operator!=(const BadSelfCopy& lhs, const BadSelfCopy& rhs) {
-    return lhs.broken_state || rhs.broken_state;
-  }
-
-  bool broken_state = false;
-};
-
-TEST(ConformanceTestingDeathTest, SelfCopyFailure) {
-  using profile = ti::EquatableValueProfile;
-
-  {
-    ABSL_INTERNAL_ASSERT_NONCONFORMANCE_OF(BadSelfCopy)
-        .DUE_TO("Copy assignment")
-        .INITIALIZER(BadSelfCopy())
-        .WITH_LOOSE_PROFILE(profile);
-  }
-}
-
-struct BadSelfSwap {
-  friend void swap(BadSelfSwap& lhs, BadSelfSwap& rhs) noexcept {
-    if (&lhs == &rhs) lhs.broken_state = true;
-  }
-
-  friend bool operator==(const BadSelfSwap& lhs, const BadSelfSwap& rhs) {
-    return !(lhs.broken_state || rhs.broken_state);
-  }
-
-  friend bool operator!=(const BadSelfSwap& lhs, const BadSelfSwap& rhs) {
-    return lhs.broken_state || rhs.broken_state;
-  }
-
-  bool broken_state = false;
-};
-
-TEST(ConformanceTestingDeathTest, SelfSwapFailure) {
-  using profile = ti::EquatableNothrowMovableProfile;
-
-  {
-    ABSL_INTERNAL_ASSERT_NONCONFORMANCE_OF(BadSelfSwap)
-        .DUE_TO("Swap")
-        .INITIALIZER(BadSelfSwap())
-        .WITH_LOOSE_PROFILE(profile);
-  }
-}
-
-struct BadDefaultInitializedMoveAssign {
-  BadDefaultInitializedMoveAssign() : default_initialized(true) {}
-  explicit BadDefaultInitializedMoveAssign(int v) : value(v) {}
-  BadDefaultInitializedMoveAssign(
-      BadDefaultInitializedMoveAssign&& other) noexcept
-      : value(other.value) {}
-  BadDefaultInitializedMoveAssign& operator=(
-      BadDefaultInitializedMoveAssign&& other) noexcept {
-    value = other.value;
-    if (default_initialized) ++value;  // Bad move if lhs is default initialized
-    return *this;
-  }
-
-  friend bool operator==(const BadDefaultInitializedMoveAssign& lhs,
-                         const BadDefaultInitializedMoveAssign& rhs) {
-    return lhs.value == rhs.value;
-  }
-
-  friend bool operator!=(const BadDefaultInitializedMoveAssign& lhs,
-                         const BadDefaultInitializedMoveAssign& rhs) {
-    return lhs.value != rhs.value;
-  }
-
-  bool default_initialized = false;
-  int value = 0;
-};
-
-TEST(ConformanceTestingDeathTest, DefaultInitializedMoveAssignFailure) {
-  using profile =
-      ti::CombineProfiles<ti::DefaultConstructibleNothrowMovableProfile,
-                          ti::EquatableProfile>;
-
-  {
-    ABSL_INTERNAL_ASSERT_NONCONFORMANCE_OF(BadDefaultInitializedMoveAssign)
-        .DUE_TO("move assignment")
-        .INITIALIZER(BadDefaultInitializedMoveAssign(0))
-        .WITH_LOOSE_PROFILE(profile);
-  }
-}
-
-struct BadDefaultInitializedCopyAssign {
-  BadDefaultInitializedCopyAssign() : default_initialized(true) {}
-  explicit BadDefaultInitializedCopyAssign(int v) : value(v) {}
-  BadDefaultInitializedCopyAssign(
-      BadDefaultInitializedCopyAssign&& other) noexcept
-      : value(other.value) {}
-  BadDefaultInitializedCopyAssign(const BadDefaultInitializedCopyAssign& other)
-      : value(other.value) {}
-
-  BadDefaultInitializedCopyAssign& operator=(
-      BadDefaultInitializedCopyAssign&& other) noexcept {
-    value = other.value;
-    return *this;
-  }
-
-  BadDefaultInitializedCopyAssign& operator=(
-      const BadDefaultInitializedCopyAssign& other) {
-    value = other.value;
-    if (default_initialized) ++value;  // Bad move if lhs is default initialized
-    return *this;
-  }
-
-  friend bool operator==(const BadDefaultInitializedCopyAssign& lhs,
-                         const BadDefaultInitializedCopyAssign& rhs) {
-    return lhs.value == rhs.value;
-  }
-
-  friend bool operator!=(const BadDefaultInitializedCopyAssign& lhs,
-                         const BadDefaultInitializedCopyAssign& rhs) {
-    return lhs.value != rhs.value;
-  }
-
-  bool default_initialized = false;
-  int value = 0;
-};
-
-TEST(ConformanceTestingDeathTest, DefaultInitializedAssignFailure) {
-  using profile = ti::CombineProfiles<ti::DefaultConstructibleValueProfile,
-                                      ti::EquatableProfile>;
-
-  {
-    ABSL_INTERNAL_ASSERT_NONCONFORMANCE_OF(BadDefaultInitializedCopyAssign)
-        .DUE_TO("copy assignment")
-        .INITIALIZER(BadDefaultInitializedCopyAssign(0))
-        .WITH_LOOSE_PROFILE(profile);
-  }
-}
-
-}  // namespace
diff --git a/absl/types/internal/parentheses.h b/absl/types/internal/parentheses.h
deleted file mode 100644
index 5aebee8..0000000
--- a/absl/types/internal/parentheses.h
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright 2019 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.
-//
-// -----------------------------------------------------------------------------
-// parentheses.h
-// -----------------------------------------------------------------------------
-//
-// This file contains macros that expand to a left parenthesis and a right
-// parenthesis. These are in their own file and are generated from macros
-// because otherwise clang-format gets confused and clang-format off directives
-// do not help.
-//
-// The parentheses macros are used when wanting to require a rescan before
-// expansion of parenthesized text appearing after a function-style macro name.
-
-#ifndef ABSL_TYPES_INTERNAL_PARENTHESES_H_
-#define ABSL_TYPES_INTERNAL_PARENTHESES_H_
-
-#define ABSL_INTERNAL_LPAREN (
-
-#define ABSL_INTERNAL_RPAREN )
-
-#endif  // ABSL_TYPES_INTERNAL_PARENTHESES_H_
diff --git a/absl/types/internal/transform_args.h b/absl/types/internal/transform_args.h
deleted file mode 100644
index 4a0ab42..0000000
--- a/absl/types/internal/transform_args.h
+++ /dev/null
@@ -1,246 +0,0 @@
-// Copyright 2019 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.
-//
-// -----------------------------------------------------------------------------
-// transform_args.h
-// -----------------------------------------------------------------------------
-//
-// This file contains a higher-order macro that "transforms" each element of a
-// a variadic argument by a provided secondary macro.
-
-#ifndef ABSL_TYPES_INTERNAL_TRANSFORM_ARGS_H_
-#define ABSL_TYPES_INTERNAL_TRANSFORM_ARGS_H_
-
-//
-// ABSL_INTERNAL_CAT(a, b)
-//
-// This macro takes two arguments and concatenates them together via ## after
-// expansion.
-//
-// Example:
-//
-//   ABSL_INTERNAL_CAT(foo_, bar)
-//
-// Results in:
-//
-//   foo_bar
-#define ABSL_INTERNAL_CAT(a, b) ABSL_INTERNAL_CAT_IMPL(a, b)
-#define ABSL_INTERNAL_CAT_IMPL(a, b) a##b
-
-//
-// ABSL_INTERNAL_TRANSFORM_ARGS(m, ...)
-//
-// This macro takes another macro as an argument followed by a trailing series
-// of additional parameters (up to 32 additional arguments). It invokes the
-// passed-in macro once for each of the additional arguments, with the
-// expansions separated by commas.
-//
-// Example:
-//
-//   ABSL_INTERNAL_TRANSFORM_ARGS(MY_MACRO, a, b, c)
-//
-// Results in:
-//
-//   MY_MACRO(a), MY_MACRO(b), MY_MACRO(c)
-//
-// TODO(calabrese) Handle no arguments as a special case.
-#define ABSL_INTERNAL_TRANSFORM_ARGS(m, ...)             \
-  ABSL_INTERNAL_CAT(ABSL_INTERNAL_TRANSFORM_ARGS,        \
-                    ABSL_INTERNAL_NUM_ARGS(__VA_ARGS__)) \
-  (m, __VA_ARGS__)
-
-#define ABSL_INTERNAL_TRANSFORM_ARGS1(m, a0) m(a0)
-
-#define ABSL_INTERNAL_TRANSFORM_ARGS2(m, a0, a1) m(a0), m(a1)
-
-#define ABSL_INTERNAL_TRANSFORM_ARGS3(m, a0, a1, a2) m(a0), m(a1), m(a2)
-
-#define ABSL_INTERNAL_TRANSFORM_ARGS4(m, a0, a1, a2, a3) \
-  m(a0), m(a1), m(a2), m(a3)
-
-#define ABSL_INTERNAL_TRANSFORM_ARGS5(m, a0, a1, a2, a3, a4) \
-  m(a0), m(a1), m(a2), m(a3), m(a4)
-
-#define ABSL_INTERNAL_TRANSFORM_ARGS6(m, a0, a1, a2, a3, a4, a5) \
-  m(a0), m(a1), m(a2), m(a3), m(a4), m(a5)
-
-#define ABSL_INTERNAL_TRANSFORM_ARGS7(m, a0, a1, a2, a3, a4, a5, a6) \
-  m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6)
-
-#define ABSL_INTERNAL_TRANSFORM_ARGS8(m, a0, a1, a2, a3, a4, a5, a6, a7) \
-  m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7)
-
-#define ABSL_INTERNAL_TRANSFORM_ARGS9(m, a0, a1, a2, a3, a4, a5, a6, a7, a8) \
-  m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7), m(a8)
-
-#define ABSL_INTERNAL_TRANSFORM_ARGS10(m, a0, a1, a2, a3, a4, a5, a6, a7, a8, \
-                                       a9)                                    \
-  m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7), m(a8), m(a9)
-
-#define ABSL_INTERNAL_TRANSFORM_ARGS11(m, a0, a1, a2, a3, a4, a5, a6, a7, a8, \
-                                       a9, a10)                               \
-  m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7), m(a8), m(a9), m(a10)
-
-#define ABSL_INTERNAL_TRANSFORM_ARGS12(m, a0, a1, a2, a3, a4, a5, a6, a7, a8, \
-                                       a9, a10, a11)                          \
-  m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7), m(a8), m(a9),       \
-      m(a10), m(a11)
-
-#define ABSL_INTERNAL_TRANSFORM_ARGS13(m, a0, a1, a2, a3, a4, a5, a6, a7, a8, \
-                                       a9, a10, a11, a12)                     \
-  m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7), m(a8), m(a9),       \
-      m(a10), m(a11), m(a12)
-
-#define ABSL_INTERNAL_TRANSFORM_ARGS14(m, a0, a1, a2, a3, a4, a5, a6, a7, a8, \
-                                       a9, a10, a11, a12, a13)                \
-  m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7), m(a8), m(a9),       \
-      m(a10), m(a11), m(a12), m(a13)
-
-#define ABSL_INTERNAL_TRANSFORM_ARGS15(m, a0, a1, a2, a3, a4, a5, a6, a7, a8, \
-                                       a9, a10, a11, a12, a13, a14)           \
-  m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7), m(a8), m(a9),       \
-      m(a10), m(a11), m(a12), m(a13), m(a14)
-
-#define ABSL_INTERNAL_TRANSFORM_ARGS16(m, a0, a1, a2, a3, a4, a5, a6, a7, a8, \
-                                       a9, a10, a11, a12, a13, a14, a15)      \
-  m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7), m(a8), m(a9),       \
-      m(a10), m(a11), m(a12), m(a13), m(a14), m(a15)
-
-#define ABSL_INTERNAL_TRANSFORM_ARGS17(m, a0, a1, a2, a3, a4, a5, a6, a7, a8, \
-                                       a9, a10, a11, a12, a13, a14, a15, a16) \
-  m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7), m(a8), m(a9),       \
-      m(a10), m(a11), m(a12), m(a13), m(a14), m(a15), m(a16)
-
-#define ABSL_INTERNAL_TRANSFORM_ARGS18(m, a0, a1, a2, a3, a4, a5, a6, a7, a8, \
-                                       a9, a10, a11, a12, a13, a14, a15, a16, \
-                                       a17)                                   \
-  m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7), m(a8), m(a9),       \
-      m(a10), m(a11), m(a12), m(a13), m(a14), m(a15), m(a16), m(a17)
-
-#define ABSL_INTERNAL_TRANSFORM_ARGS19(m, a0, a1, a2, a3, a4, a5, a6, a7, a8, \
-                                       a9, a10, a11, a12, a13, a14, a15, a16, \
-                                       a17, a18)                              \
-  m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7), m(a8), m(a9),       \
-      m(a10), m(a11), m(a12), m(a13), m(a14), m(a15), m(a16), m(a17), m(a18)
-
-#define ABSL_INTERNAL_TRANSFORM_ARGS20(m, a0, a1, a2, a3, a4, a5, a6, a7, a8, \
-                                       a9, a10, a11, a12, a13, a14, a15, a16, \
-                                       a17, a18, a19)                         \
-  m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7), m(a8), m(a9),       \
-      m(a10), m(a11), m(a12), m(a13), m(a14), m(a15), m(a16), m(a17), m(a18), \
-      m(a19)
-
-#define ABSL_INTERNAL_TRANSFORM_ARGS21(m, a0, a1, a2, a3, a4, a5, a6, a7, a8, \
-                                       a9, a10, a11, a12, a13, a14, a15, a16, \
-                                       a17, a18, a19, a20)                    \
-  m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7), m(a8), m(a9),       \
-      m(a10), m(a11), m(a12), m(a13), m(a14), m(a15), m(a16), m(a17), m(a18), \
-      m(a19), m(a20)
-
-#define ABSL_INTERNAL_TRANSFORM_ARGS22(m, a0, a1, a2, a3, a4, a5, a6, a7, a8, \
-                                       a9, a10, a11, a12, a13, a14, a15, a16, \
-                                       a17, a18, a19, a20, a21)               \
-  m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7), m(a8), m(a9),       \
-      m(a10), m(a11), m(a12), m(a13), m(a14), m(a15), m(a16), m(a17), m(a18), \
-      m(a19), m(a20), m(a21)
-
-#define ABSL_INTERNAL_TRANSFORM_ARGS23(m, a0, a1, a2, a3, a4, a5, a6, a7, a8, \
-                                       a9, a10, a11, a12, a13, a14, a15, a16, \
-                                       a17, a18, a19, a20, a21, a22)          \
-  m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7), m(a8), m(a9),       \
-      m(a10), m(a11), m(a12), m(a13), m(a14), m(a15), m(a16), m(a17), m(a18), \
-      m(a19), m(a20), m(a21), m(a22)
-
-#define ABSL_INTERNAL_TRANSFORM_ARGS24(m, a0, a1, a2, a3, a4, a5, a6, a7, a8, \
-                                       a9, a10, a11, a12, a13, a14, a15, a16, \
-                                       a17, a18, a19, a20, a21, a22, a23)     \
-  m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7), m(a8), m(a9),       \
-      m(a10), m(a11), m(a12), m(a13), m(a14), m(a15), m(a16), m(a17), m(a18), \
-      m(a19), m(a20), m(a21), m(a22), m(a23)
-
-#define ABSL_INTERNAL_TRANSFORM_ARGS25(m, a0, a1, a2, a3, a4, a5, a6, a7, a8,  \
-                                       a9, a10, a11, a12, a13, a14, a15, a16,  \
-                                       a17, a18, a19, a20, a21, a22, a23, a24) \
-  m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7), m(a8), m(a9),        \
-      m(a10), m(a11), m(a12), m(a13), m(a14), m(a15), m(a16), m(a17), m(a18),  \
-      m(a19), m(a20), m(a21), m(a22), m(a23), m(a24)
-
-#define ABSL_INTERNAL_TRANSFORM_ARGS26(                                       \
-    m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15,  \
-    a16, a17, a18, a19, a20, a21, a22, a23, a24, a25)                         \
-  m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7), m(a8), m(a9),       \
-      m(a10), m(a11), m(a12), m(a13), m(a14), m(a15), m(a16), m(a17), m(a18), \
-      m(a19), m(a20), m(a21), m(a22), m(a23), m(a24), m(a25)
-
-#define ABSL_INTERNAL_TRANSFORM_ARGS27(                                       \
-    m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15,  \
-    a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26)                    \
-  m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7), m(a8), m(a9),       \
-      m(a10), m(a11), m(a12), m(a13), m(a14), m(a15), m(a16), m(a17), m(a18), \
-      m(a19), m(a20), m(a21), m(a22), m(a23), m(a24), m(a25), m(a26)
-
-#define ABSL_INTERNAL_TRANSFORM_ARGS28(                                       \
-    m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15,  \
-    a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27)               \
-  m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7), m(a8), m(a9),       \
-      m(a10), m(a11), m(a12), m(a13), m(a14), m(a15), m(a16), m(a17), m(a18), \
-      m(a19), m(a20), m(a21), m(a22), m(a23), m(a24), m(a25), m(a26), m(a27)
-
-#define ABSL_INTERNAL_TRANSFORM_ARGS29(                                       \
-    m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15,  \
-    a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28)          \
-  m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7), m(a8), m(a9),       \
-      m(a10), m(a11), m(a12), m(a13), m(a14), m(a15), m(a16), m(a17), m(a18), \
-      m(a19), m(a20), m(a21), m(a22), m(a23), m(a24), m(a25), m(a26), m(a27), \
-      m(a28)
-
-#define ABSL_INTERNAL_TRANSFORM_ARGS30(                                       \
-    m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15,  \
-    a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29)     \
-  m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7), m(a8), m(a9),       \
-      m(a10), m(a11), m(a12), m(a13), m(a14), m(a15), m(a16), m(a17), m(a18), \
-      m(a19), m(a20), m(a21), m(a22), m(a23), m(a24), m(a25), m(a26), m(a27), \
-      m(a28), m(a29)
-
-#define ABSL_INTERNAL_TRANSFORM_ARGS31(                                        \
-    m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15,   \
-    a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30) \
-  m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7), m(a8), m(a9),        \
-      m(a10), m(a11), m(a12), m(a13), m(a14), m(a15), m(a16), m(a17), m(a18),  \
-      m(a19), m(a20), m(a21), m(a22), m(a23), m(a24), m(a25), m(a26), m(a27),  \
-      m(a28), m(a29), m(a30)
-
-#define ABSL_INTERNAL_TRANSFORM_ARGS32(m, a0, a1, a2, a3, a4, a5, a6, a7, a8,  \
-                                       a9, a10, a11, a12, a13, a14, a15, a16,  \
-                                       a17, a18, a19, a20, a21, a22, a23, a24, \
-                                       a25, a26, a27, a28, a29, a30, a31)      \
-  m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7), m(a8), m(a9),        \
-      m(a10), m(a11), m(a12), m(a13), m(a14), m(a15), m(a16), m(a17), m(a18),  \
-      m(a19), m(a20), m(a21), m(a22), m(a23), m(a24), m(a25), m(a26), m(a27),  \
-      m(a28), m(a29), m(a30), m(a31)
-
-#define ABSL_INTERNAL_NUM_ARGS_IMPL(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9,    \
-                                    a10, a11, a12, a13, a14, a15, a16, a17,    \
-                                    a18, a19, a20, a21, a22, a23, a24, a25,    \
-                                    a26, a27, a28, a29, a30, a31, result, ...) \
-  result
-
-#define ABSL_INTERNAL_FORCE_EXPANSION(...) __VA_ARGS__
-
-#define ABSL_INTERNAL_NUM_ARGS(...)                                            \
-  ABSL_INTERNAL_FORCE_EXPANSION(ABSL_INTERNAL_NUM_ARGS_IMPL(                   \
-      __VA_ARGS__, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, \
-      17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, ))
-
-#endif  // ABSL_TYPES_INTERNAL_TRANSFORM_ARGS_H_
diff --git a/absl/types/internal/variant.h b/absl/types/internal/variant.h
index fc8829e..263d7b0 100644
--- a/absl/types/internal/variant.h
+++ b/absl/types/internal/variant.h
@@ -1047,11 +1047,11 @@
   std::size_t index_;
 };
 
-using absl::internal::identity;
+using absl::internal::type_identity;
 
 // OverloadSet::Overload() is a unary function which is overloaded to
 // take any of the element types of the variant, by reference-to-const.
-// The return type of the overload on T is identity<T>, so that you
+// The return type of the overload on T is type_identity<T>, so that you
 // can statically determine which overload was called.
 //
 // Overload() is not defined, so it can only be called in unevaluated
@@ -1062,7 +1062,7 @@
 template <typename T, typename... Ts>
 struct OverloadSet<T, Ts...> : OverloadSet<Ts...> {
   using Base = OverloadSet<Ts...>;
-  static identity<T> Overload(const T&);
+  static type_identity<T> Overload(const T&);
   using Base::Overload;
 };
 
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..88cd759 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,6 +173,8 @@
  public:
   using element_type = T;
   using value_type = absl::remove_cv_t<T>;
+  // TODO(b/316099902) - pointer should be Nullable<T*>, but this makes it hard
+  // to recognize foreach loops as safe.
   using pointer = T*;
   using const_pointer = const T*;
   using reference = T&;
@@ -679,12 +682,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 +728,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/BUILD.bazel b/absl/utility/BUILD.bazel
index 061f4c5..1c01fc1 100644
--- a/absl/utility/BUILD.bazel
+++ b/absl/utility/BUILD.bazel
@@ -21,7 +21,14 @@
     "ABSL_TEST_COPTS",
 )
 
-package(default_visibility = ["//visibility:public"])
+package(
+    default_visibility = ["//visibility:public"],
+    features = [
+        "header_modules",
+        "layering_check",
+        "parse_headers",
+    ],
+)
 
 licenses(["notice"])
 
@@ -49,6 +56,7 @@
         "//absl/base:core_headers",
         "//absl/memory",
         "//absl/strings",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -72,6 +80,7 @@
     linkopts = ABSL_DEFAULT_LINKOPTS,
     deps = [
         ":if_constexpr",
+        "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
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
diff --git a/ci/cmake_common.sh b/ci/cmake_common.sh
index 051b70d..784b381 100644
--- a/ci/cmake_common.sh
+++ b/ci/cmake_common.sh
@@ -14,7 +14,11 @@
 
 # The commit of GoogleTest to be used in the CMake tests in this directory.
 # Keep this in sync with the commit in the WORKSPACE file.
-readonly ABSL_GOOGLETEST_COMMIT="f8d7d77c06936315286eb55f8de22cd23c188571"  # v1.14.0
+# TODO(dmauro): After the next GoogleTest release, use the stable file required
+# by Bzlmod.  This means downloading a copy of the file and reuploading it to
+# avoid changing checksums if the compression is changed by GitHub.  It also
+# means stop referring to it as a commit and instead use the uploaded filename.
+readonly ABSL_GOOGLETEST_COMMIT="f8d7d77c06936315286eb55f8de22cd23c188571"
 
 # Avoid depending on GitHub by looking for a cached copy of the commit first.
 if [[ -r "${KOKORO_GFILE_DIR:-}/distdir/${ABSL_GOOGLETEST_COMMIT}.zip" ]]; then
diff --git a/ci/linux_arm_clang-latest_libcxx_bazel.sh b/ci/linux_arm_clang-latest_libcxx_bazel.sh
new file mode 100755
index 0000000..13f4ad1
--- /dev/null
+++ b/ci/linux_arm_clang-latest_libcxx_bazel.sh
@@ -0,0 +1,100 @@
+#!/bin/bash
+#
+# Copyright 2019 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.
+
+# This script that can be invoked to test abseil-cpp in a hermetic environment
+# using a Docker image on Linux. You must have Docker installed to use this
+# script.
+
+set -euox pipefail
+
+if [[ -z ${ABSEIL_ROOT:-} ]]; then
+  ABSEIL_ROOT="$(realpath $(dirname ${0})/..)"
+fi
+
+if [[ -z ${STD:-} ]]; then
+  STD="c++14 c++17 c++20"
+fi
+
+if [[ -z ${COMPILATION_MODE:-} ]]; then
+  COMPILATION_MODE="fastbuild opt"
+fi
+
+if [[ -z ${EXCEPTIONS_MODE:-} ]]; then
+  EXCEPTIONS_MODE="-fno-exceptions -fexceptions"
+fi
+
+source "${ABSEIL_ROOT}/ci/linux_docker_containers.sh"
+readonly DOCKER_CONTAINER=${LINUX_ARM_CLANG_LATEST_CONTAINER}
+
+# USE_BAZEL_CACHE=1 only works on Kokoro.
+# Without access to the credentials this won't work.
+if [[ ${USE_BAZEL_CACHE:-0} -ne 0 ]]; then
+  DOCKER_EXTRA_ARGS="--mount type=bind,source=${KOKORO_KEYSTORE_DIR},target=/keystore,readonly ${DOCKER_EXTRA_ARGS:-}"
+  # Bazel doesn't track changes to tools outside of the workspace
+  # (e.g. /usr/bin/gcc), so by appending the docker container to the
+  # remote_http_cache url, we make changes to the container part of
+  # the cache key. Hashing the key is to make it shorter and url-safe.
+  container_key=$(echo ${DOCKER_CONTAINER} | sha256sum | head -c 16)
+  BAZEL_EXTRA_ARGS="--remote_cache=https://storage.googleapis.com/absl-bazel-remote-cache/${container_key} --google_credentials=/keystore/73103_absl-bazel-remote-cache ${BAZEL_EXTRA_ARGS:-}"
+fi
+
+# Avoid depending on external sites like GitHub by checking --distdir for
+# external dependencies first.
+# https://docs.bazel.build/versions/master/guide.html#distdir
+if [[ ${KOKORO_GFILE_DIR:-} ]] && [[ -d "${KOKORO_GFILE_DIR}/distdir" ]]; then
+  DOCKER_EXTRA_ARGS="--mount type=bind,source=${KOKORO_GFILE_DIR}/distdir,target=/distdir,readonly ${DOCKER_EXTRA_ARGS:-}"
+  BAZEL_EXTRA_ARGS="--distdir=/distdir ${BAZEL_EXTRA_ARGS:-}"
+fi
+
+for std in ${STD}; do
+  for compilation_mode in ${COMPILATION_MODE}; do
+    for exceptions_mode in ${EXCEPTIONS_MODE}; do
+      echo "--------------------------------------------------------------------"
+      time docker run \
+        --mount type=bind,source="${ABSEIL_ROOT}",target=/abseil-cpp-ro,readonly \
+        --tmpfs=/abseil-cpp \
+        --workdir=/abseil-cpp \
+        --cap-add=SYS_PTRACE \
+        --rm \
+        -e CC="/opt/llvm/clang/bin/clang" \
+        -e BAZEL_CXXOPTS="-std=${std}:-nostdinc++" \
+        -e BAZEL_LINKOPTS="-L/opt/llvm/clang/lib/aarch64-unknown-linux-gnu:-lc++:-lc++abi:-lm:-Wl,-rpath=/opt/llvm/clang/lib/aarch64-unknown-linux-gnu" \
+        -e CPLUS_INCLUDE_PATH="/opt/llvm/clang/include/aarch64-unknown-linux-gnu/c++/v1:/opt/llvm/clang/include/c++/v1" \
+        ${DOCKER_EXTRA_ARGS:-} \
+        ${DOCKER_CONTAINER} \
+        /bin/sh -c "
+          cp -r /abseil-cpp-ro/* /abseil-cpp/
+          if [ -n \"${ALTERNATE_OPTIONS:-}\" ]; then
+            cp ${ALTERNATE_OPTIONS:-} absl/base/options.h || exit 1
+          fi
+          /usr/local/bin/bazel test ... \
+            --compilation_mode=\"${compilation_mode}\" \
+            --copt=\"${exceptions_mode}\" \
+            --copt=\"-DGTEST_REMOVE_LEGACY_TEST_CASEAPI_=1\" \
+            --copt=-Werror \
+            --define=\"absl=1\" \
+            --enable_bzlmod=true \
+            --features=external_include_paths \
+            --keep_going \
+            --show_timestamps \
+            --test_env=\"GTEST_INSTALL_FAILURE_SIGNAL_HANDLER=1\" \
+            --test_env=\"TZDIR=/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo\" \
+            --test_output=errors \
+            --test_tag_filters=-benchmark \
+            ${BAZEL_EXTRA_ARGS:-}"
+    done
+  done
+done
diff --git a/ci/linux_clang-latest_libcxx_asan_bazel.sh b/ci/linux_clang-latest_libcxx_asan_bazel.sh
index f9c146b..3153fae 100755
--- a/ci/linux_clang-latest_libcxx_asan_bazel.sh
+++ b/ci/linux_clang-latest_libcxx_asan_bazel.sh
@@ -48,7 +48,7 @@
   # remote_http_cache url, we make changes to the container part of
   # the cache key. Hashing the key is to make it shorter and url-safe.
   container_key=$(echo ${DOCKER_CONTAINER} | sha256sum | head -c 16)
-  BAZEL_EXTRA_ARGS="--remote_http_cache=https://storage.googleapis.com/absl-bazel-remote-cache/${container_key} --google_credentials=/keystore/73103_absl-bazel-remote-cache ${BAZEL_EXTRA_ARGS:-}"
+  BAZEL_EXTRA_ARGS="--remote_cache=https://storage.googleapis.com/absl-bazel-remote-cache/${container_key} --google_credentials=/keystore/73103_absl-bazel-remote-cache ${BAZEL_EXTRA_ARGS:-}"
 fi
 
 # Avoid depending on external sites like GitHub by checking --distdir for
@@ -70,8 +70,8 @@
         --rm \
         -e CC="/opt/llvm/clang/bin/clang" \
         -e BAZEL_CXXOPTS="-std=${std}:-nostdinc++" \
-        -e BAZEL_LINKOPTS="-L/opt/llvm/libcxx/lib/x86_64-unknown-linux-gnu:-lc++:-lc++abi:-lm:-Wl,-rpath=/opt/llvm/libcxx/lib/x86_64-unknown-linux-gnu" \
-        -e CPLUS_INCLUDE_PATH="/opt/llvm/libcxx/include/x86_64-unknown-linux-gnu/c++/v1:/opt/llvm/libcxx/include/c++/v1" \
+        -e BAZEL_LINKOPTS="-L/opt/llvm/libcxx/lib:-lc++:-lc++abi:-lm:-Wl,-rpath=/opt/llvm/libcxx/lib" \
+        -e CPLUS_INCLUDE_PATH="/opt/llvm/libcxx/include/c++/v1" \
         ${DOCKER_EXTRA_ARGS:-} \
         ${DOCKER_CONTAINER} \
         /usr/local/bin/bazel test ... \
@@ -84,7 +84,7 @@
           --copt="-fsanitize=undefined" \
           --copt="-fno-sanitize-blacklist" \
           --copt=-Werror \
-          --distdir="/bazel-distdir" \
+          --enable_bzlmod=true \
           --features=external_include_paths \
           --keep_going \
           --linkopt="-fsanitize=address" \
diff --git a/ci/linux_clang-latest_libcxx_bazel.sh b/ci/linux_clang-latest_libcxx_bazel.sh
index 38b2d74..4f3eba4 100755
--- a/ci/linux_clang-latest_libcxx_bazel.sh
+++ b/ci/linux_clang-latest_libcxx_bazel.sh
@@ -48,7 +48,7 @@
   # remote_http_cache url, we make changes to the container part of
   # the cache key. Hashing the key is to make it shorter and url-safe.
   container_key=$(echo ${DOCKER_CONTAINER} | sha256sum | head -c 16)
-  BAZEL_EXTRA_ARGS="--remote_http_cache=https://storage.googleapis.com/absl-bazel-remote-cache/${container_key} --google_credentials=/keystore/73103_absl-bazel-remote-cache ${BAZEL_EXTRA_ARGS:-}"
+  BAZEL_EXTRA_ARGS="--remote_cache=https://storage.googleapis.com/absl-bazel-remote-cache/${container_key} --google_credentials=/keystore/73103_absl-bazel-remote-cache ${BAZEL_EXTRA_ARGS:-}"
 fi
 
 # Avoid depending on external sites like GitHub by checking --distdir for
@@ -71,8 +71,8 @@
         --rm \
         -e CC="/opt/llvm/clang/bin/clang" \
         -e BAZEL_CXXOPTS="-std=${std}:-nostdinc++" \
-        -e BAZEL_LINKOPTS="-L/opt/llvm/libcxx/lib/x86_64-unknown-linux-gnu:-lc++:-lc++abi:-lm:-Wl,-rpath=/opt/llvm/libcxx/lib/x86_64-unknown-linux-gnu" \
-        -e CPLUS_INCLUDE_PATH="/opt/llvm/libcxx/include/x86_64-unknown-linux-gnu/c++/v1:/opt/llvm/libcxx/include/c++/v1" \
+        -e BAZEL_LINKOPTS="-L/opt/llvm/libcxx/lib:-lc++:-lc++abi:-lm:-Wl,-rpath=/opt/llvm/libcxx/lib" \
+        -e CPLUS_INCLUDE_PATH="/opt/llvm/libcxx/include/c++/v1" \
         ${DOCKER_EXTRA_ARGS:-} \
         ${DOCKER_CONTAINER} \
         /bin/sh -c "
@@ -86,7 +86,7 @@
             --copt=\"-DGTEST_REMOVE_LEGACY_TEST_CASEAPI_=1\" \
             --copt=-Werror \
             --define=\"absl=1\" \
-            --distdir=\"/bazel-distdir\" \
+            --enable_bzlmod=true \
             --features=external_include_paths \
             --keep_going \
             --show_timestamps \
diff --git a/ci/linux_clang-latest_libcxx_tsan_bazel.sh b/ci/linux_clang-latest_libcxx_tsan_bazel.sh
index 34d7940..06f4c2e 100755
--- a/ci/linux_clang-latest_libcxx_tsan_bazel.sh
+++ b/ci/linux_clang-latest_libcxx_tsan_bazel.sh
@@ -48,7 +48,7 @@
   # remote_http_cache url, we make changes to the container part of
   # the cache key. Hashing the key is to make it shorter and url-safe.
   container_key=$(echo ${DOCKER_CONTAINER} | sha256sum | head -c 16)
-  BAZEL_EXTRA_ARGS="--remote_http_cache=https://storage.googleapis.com/absl-bazel-remote-cache/${container_key} --google_credentials=/keystore/73103_absl-bazel-remote-cache ${BAZEL_EXTRA_ARGS:-}"
+  BAZEL_EXTRA_ARGS="--remote_cache=https://storage.googleapis.com/absl-bazel-remote-cache/${container_key} --google_credentials=/keystore/73103_absl-bazel-remote-cache ${BAZEL_EXTRA_ARGS:-}"
 fi
 
 # Avoid depending on external sites like GitHub by checking --distdir for
@@ -70,8 +70,8 @@
         --rm \
         -e CC="/opt/llvm/clang/bin/clang" \
         -e BAZEL_CXXOPTS="-std=${std}:-nostdinc++" \
-        -e BAZEL_LINKOPTS="-L/opt/llvm/libcxx-tsan/lib/x86_64-unknown-linux-gnu:-lc++:-lc++abi:-lm:-Wl,-rpath=/opt/llvm/libcxx-tsan/lib/x86_64-unknown-linux-gnu" \
-        -e CPLUS_INCLUDE_PATH="/opt/llvm/libcxx-tsan/include/x86_64-unknown-linux-gnu/c++/v1:/opt/llvm/libcxx-tsan/include/c++/v1" \
+        -e BAZEL_LINKOPTS="-L/opt/llvm/libcxx-tsan/lib:-lc++:-lc++abi:-lm:-Wl,-rpath=/opt/llvm/libcxx-tsan/lib" \
+        -e CPLUS_INCLUDE_PATH="/opt/llvm/libcxx-tsan/include/c++/v1" \
         ${DOCKER_EXTRA_ARGS:-} \
         ${DOCKER_CONTAINER} \
         /usr/local/bin/bazel test ... \
@@ -82,7 +82,7 @@
           --copt="-fsanitize=thread" \
           --copt="-fno-sanitize-blacklist" \
           --copt=-Werror \
-          --distdir="/bazel-distdir" \
+          --enable_bzlmod=true \
           --features=external_include_paths \
           --keep_going \
           --linkopt="-fsanitize=thread" \
diff --git a/ci/linux_clang-latest_libstdcxx_bazel.sh b/ci/linux_clang-latest_libstdcxx_bazel.sh
index 13d56fc..d499e13 100755
--- a/ci/linux_clang-latest_libstdcxx_bazel.sh
+++ b/ci/linux_clang-latest_libstdcxx_bazel.sh
@@ -48,7 +48,7 @@
   # remote_http_cache url, we make changes to the container part of
   # the cache key. Hashing the key is to make it shorter and url-safe.
   container_key=$(echo ${DOCKER_CONTAINER} | sha256sum | head -c 16)
-  BAZEL_EXTRA_ARGS="--remote_http_cache=https://storage.googleapis.com/absl-bazel-remote-cache/${container_key} --google_credentials=/keystore/73103_absl-bazel-remote-cache ${BAZEL_EXTRA_ARGS:-}"
+  BAZEL_EXTRA_ARGS="--remote_cache=https://storage.googleapis.com/absl-bazel-remote-cache/${container_key} --google_credentials=/keystore/73103_absl-bazel-remote-cache ${BAZEL_EXTRA_ARGS:-}"
 fi
 
 # Avoid depending on external sites like GitHub by checking --distdir for
@@ -80,7 +80,7 @@
           --copt="-march=haswell" \
           --copt=-Werror \
           --define="absl=1" \
-          --distdir="/bazel-distdir" \
+          --enable_bzlmod=true \
           --features=external_include_paths \
           --keep_going \
           --linkopt="--gcc-toolchain=/usr/local" \
diff --git a/ci/linux_docker_containers.sh b/ci/linux_docker_containers.sh
index a07c64c..232233d 100644
--- a/ci/linux_docker_containers.sh
+++ b/ci/linux_docker_containers.sh
@@ -16,6 +16,7 @@
 # Test scripts should source this file to get the identifiers.
 
 readonly LINUX_ALPINE_CONTAINER="gcr.io/google.com/absl-177019/alpine:20230612"
-readonly LINUX_CLANG_LATEST_CONTAINER="gcr.io/google.com/absl-177019/linux_hybrid-latest:20230217"
-readonly LINUX_GCC_LATEST_CONTAINER="gcr.io/google.com/absl-177019/linux_hybrid-latest:20230517"
+readonly LINUX_CLANG_LATEST_CONTAINER="gcr.io/google.com/absl-177019/linux_hybrid-latest:20231218"
+readonly LINUX_ARM_CLANG_LATEST_CONTAINER="gcr.io/google.com/absl-177019/linux_arm_hybrid-latest:20231219"
+readonly LINUX_GCC_LATEST_CONTAINER="gcr.io/google.com/absl-177019/linux_hybrid-latest:20231218"
 readonly LINUX_GCC_FLOOR_CONTAINER="gcr.io/google.com/absl-177019/linux_gcc-floor:20230120"
diff --git a/ci/linux_gcc-floor_libstdcxx_bazel.sh b/ci/linux_gcc-floor_libstdcxx_bazel.sh
index 68b3999..5bd1dbf 100755
--- a/ci/linux_gcc-floor_libstdcxx_bazel.sh
+++ b/ci/linux_gcc-floor_libstdcxx_bazel.sh
@@ -59,6 +59,9 @@
   BAZEL_EXTRA_ARGS="--distdir=/distdir ${BAZEL_EXTRA_ARGS:-}"
 fi
 
+# TODO(absl-team): This currently uses Bazel 5. When upgrading to a version
+# of Bazel that supports Bzlmod, add --enable_bzlmod=false to keep test
+# coverage for the old WORKSPACE dependency management.
 for std in ${STD}; do
   for compilation_mode in ${COMPILATION_MODE}; do
     for exceptions_mode in ${EXCEPTIONS_MODE}; do
diff --git a/ci/linux_gcc-latest_libstdcxx_bazel.sh b/ci/linux_gcc-latest_libstdcxx_bazel.sh
index 091acb3..8f77346 100755
--- a/ci/linux_gcc-latest_libstdcxx_bazel.sh
+++ b/ci/linux_gcc-latest_libstdcxx_bazel.sh
@@ -48,7 +48,7 @@
   # remote_http_cache url, we make changes to the container part of
   # the cache key. Hashing the key is to make it shorter and url-safe.
   container_key=$(echo ${DOCKER_CONTAINER} | sha256sum | head -c 16)
-  BAZEL_EXTRA_ARGS="--remote_http_cache=https://storage.googleapis.com/absl-bazel-remote-cache/${container_key} --google_credentials=/keystore/73103_absl-bazel-remote-cache ${BAZEL_EXTRA_ARGS:-}"
+  BAZEL_EXTRA_ARGS="--remote_cache=https://storage.googleapis.com/absl-bazel-remote-cache/${container_key} --google_credentials=/keystore/73103_absl-bazel-remote-cache ${BAZEL_EXTRA_ARGS:-}"
 fi
 
 # Avoid depending on external sites like GitHub by checking --distdir for
@@ -84,7 +84,7 @@
             --copt=\"-DGTEST_REMOVE_LEGACY_TEST_CASEAPI_=1\" \
             --copt=-Werror \
             --define=\"absl=1\" \
-            --distdir=\"/bazel-distdir\" \
+            --enable_bzlmod=true \
             --features=external_include_paths \
             --keep_going \
             --show_timestamps \
diff --git a/ci/macos_xcode_bazel.sh b/ci/macos_xcode_bazel.sh
index 04c9a1a..bb8fb4b 100755
--- a/ci/macos_xcode_bazel.sh
+++ b/ci/macos_xcode_bazel.sh
@@ -24,7 +24,7 @@
 fi
 
 # If we are running on Kokoro, check for a versioned Bazel binary.
-KOKORO_GFILE_BAZEL_BIN="bazel-5.1.1-darwin-x86_64"
+KOKORO_GFILE_BAZEL_BIN="bazel-7.0.0-darwin-x86_64"
 if [[ ${KOKORO_GFILE_DIR:-} ]] && [[ -f ${KOKORO_GFILE_DIR}/${KOKORO_GFILE_BAZEL_BIN} ]]; then
   BAZEL_BIN="${KOKORO_GFILE_DIR}/${KOKORO_GFILE_BAZEL_BIN}"
   chmod +x ${BAZEL_BIN}
@@ -56,6 +56,7 @@
   --copt="-DGTEST_REMOVE_LEGACY_TEST_CASEAPI_=1" \
   --copt="-Werror" \
   --cxxopt="-std=c++14" \
+  --enable_bzlmod=true \
   --features=external_include_paths \
   --keep_going \
   --show_timestamps \
diff --git a/ci/windows_clangcl_bazel.bat b/ci/windows_clangcl_bazel.bat
index 21230e1..5162628 100755
--- a/ci/windows_clangcl_bazel.bat
+++ b/ci/windows_clangcl_bazel.bat
@@ -39,7 +39,7 @@
 :: /google/data/rw/teams/absl/kokoro/windows.
 ::
 :: TODO(absl-team): Remove -Wno-microsoft-cast
-%KOKORO_GFILE_DIR%\bazel-5.1.1-windows-x86_64.exe ^
+%KOKORO_GFILE_DIR%\bazel-7.0.0-windows-x86_64.exe ^
   test ... ^
   --compilation_mode=%COMPILATION_MODE% ^
   --compiler=clang-cl ^
@@ -48,7 +48,9 @@
   --copt=-Wno-microsoft-cast ^
   --define=absl=1 ^
   --distdir=%KOKORO_GFILE_DIR%\distdir ^
-  --features=external_include_paths ^
+  --enable_bzlmod=true ^
+  --extra_execution_platforms=//absl:x64_windows-clang-cl ^
+  --extra_toolchains=@local_config_cc//:cc-toolchain-x64_windows-clang-cl ^
   --keep_going ^
   --test_env="GTEST_INSTALL_FAILURE_SIGNAL_HANDLER=1" ^
   --test_env=TZDIR="%CD%\absl\time\internal\cctz\testdata\zoneinfo" ^
diff --git a/ci/windows_msvc_bazel.bat b/ci/windows_msvc_bazel.bat
index 11d9f35..e2acf5f 100755
--- a/ci/windows_msvc_bazel.bat
+++ b/ci/windows_msvc_bazel.bat
@@ -34,14 +34,14 @@
 :: To upgrade Bazel, first download a new binary from
 :: https://github.com/bazelbuild/bazel/releases and copy it to
 :: /google/data/rw/teams/absl/kokoro/windows.
-%KOKORO_GFILE_DIR%\bazel-5.1.1-windows-x86_64.exe ^
+%KOKORO_GFILE_DIR%\bazel-7.0.0-windows-x86_64.exe ^
   test ... ^
   --compilation_mode=%COMPILATION_MODE% ^
   --copt=/WX ^
   --copt=/std:%STD% ^
   --define=absl=1 ^
   --distdir=%KOKORO_GFILE_DIR%\distdir ^
-  --features=external_include_paths ^
+  --enable_bzlmod=true ^
   --keep_going ^
   --test_env="GTEST_INSTALL_FAILURE_SIGNAL_HANDLER=1" ^
   --test_env=TZDIR="%CD%\absl\time\internal\cctz\testdata\zoneinfo" ^
diff --git a/ci/windows_msvc_cmake.bat b/ci/windows_msvc_cmake.bat
index 743b6f7..c0f1ac9 100755
--- a/ci/windows_msvc_cmake.bat
+++ b/ci/windows_msvc_cmake.bat
@@ -14,7 +14,12 @@
 
 SETLOCAL ENABLEDELAYEDEXPANSION
 
-:: Use GoogleTest v1.14.0
+:: The commit of GoogleTest to be used in the CMake tests in this directory.
+:: Keep this in sync with the commit in the WORKSPACE file.
+:: TODO(dmauro): After the next GoogleTest release, use the stable file required
+:: by Bzlmod.  This means downloading a copy of the file and reuploading it to
+:: avoid changing checksums if the compression is changed by GitHub.  It also
+:: means stop referring to it as a commit and instead use the uploaded filename.
 SET ABSL_GOOGLETEST_COMMIT=f8d7d77c06936315286eb55f8de22cd23c188571
 
 IF EXIST %KOKORO_GFILE_DIR%\distdir\%ABSL_GOOGLETEST_COMMIT%.zip (
diff --git a/create_lts.py b/create_lts.py
index 642b884..7e5368e 100755
--- a/create_lts.py
+++ b/create_lts.py
@@ -96,6 +96,11 @@
 
   # Replacement directives go here.
   ReplaceStringsInFile(
+      'MODULE.bazel', {
+          'version = "head"':
+              'version = "{}.0"'.format(datestamp)
+      })
+  ReplaceStringsInFile(
       'absl/base/config.h', {
           '#undef ABSL_LTS_RELEASE_VERSION':
               '#define ABSL_LTS_RELEASE_VERSION {}'.format(datestamp),