From ff9e42f3769f2031fdcf71997e5f1e38155dde4a Mon Sep 17 00:00:00 2001 From: Aslam Ansari Date: Fri, 26 Jun 2026 13:02:03 +0530 Subject: [PATCH 1/3] added script for automation --- scripts/sync_abseil.sh | 61 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 scripts/sync_abseil.sh diff --git a/scripts/sync_abseil.sh b/scripts/sync_abseil.sh new file mode 100644 index 000000000..8b4ba7019 --- /dev/null +++ b/scripts/sync_abseil.sh @@ -0,0 +1,61 @@ +#!/bin/bash + +# Usage: ./sync_abseil.sh +# Example: ./sync_abseil.sh 20260107.1 + +if [ -z "$1" ]; then + echo "Error: No release tag provided." + exit 1 +fi + +RELEASE_TAG=$1 +UPSTREAM_URL="https://github.com/abseil/abseil-cpp.git" + +echo "--- Syncing Abseil $RELEASE_TAG from Upstream ---" + +# 1. Add upstream remote if it doesn't exist +if ! git remote get-url upstream &> /dev/null; then + echo "Adding upstream remote: $UPSTREAM_URL" + git remote add upstream "$UPSTREAM_URL" +fi + +# 2. Fetch tags from upstream +echo "Fetching tags from upstream..." +git fetch upstream --tags + +# 3. Create/Checkout your branch +BRANCH_NAME="sync-$RELEASE_TAG" +git checkout -b "$BRANCH_NAME" + +# 4. Checkout the specific tag into your working directory +# Note: We use a forced checkout to pull files from the upstream tag +echo "Syncing files from upstream tag $RELEASE_TAG..." +git checkout "$RELEASE_TAG" -- . + +# 5. Cleanup (The SwiftPM-specific pruning) +echo "Applying SwiftPM cleanup..." + +find absl/ -name "*_test.cc" -delete +find absl/ -name "*_test.c" -delete +find absl/ -name "*_benchmark.cc" -delete + +rm -f absl/status/internal/status_matchers.* +rm -rf absl/time/internal/cctz/testdata +find absl/ -name "BUILD.bazel" -delete +find absl/ -name "CMakeLists.txt" -delete +rm -rf absl/copts + +grep -rl '#include "gtest/gtest.h"' absl/ | xargs rm -f +grep -rl '#include "gmock/gmock.h"' absl/ | xargs rm -f +grep -rEl "^int main\(" absl/ | xargs rm -f + +echo "Cleanup complete." + +# 6. Verify +echo "--- Running Build Verification ---" +if swift build; then + echo "SUCCESS: Sync and build verified." +else + echo "ERROR: Build failed. Please check the logs." + exit 1 +fi \ No newline at end of file From 71896749d7be5fb7f249ec46a929b0fcd6be13f1 Mon Sep 17 00:00:00 2001 From: Aslam Ansari Date: Fri, 26 Jun 2026 13:58:31 +0530 Subject: [PATCH 2/3] Sync from abseil/abseil-cpp@20260526.0 --- .github/ISSUE_TEMPLATE/00-bug_report.yml | 53 + .github/ISSUE_TEMPLATE/config.yml | 5 + .github/PULL_REQUEST_TEMPLATE.md | 8 + .gitignore | 10 +- CMake/AbseilDll.cmake | 194 +- CMake/AbseilHelpers.cmake | 7 +- CMakeLists.txt | 6 +- FAQ.md | 209 +- MODULE.bazel | 13 +- Package.swift | 4 + absl/abseil.podspec.gen.py | 1 + absl/algorithm/algorithm.h | 36 +- absl/algorithm/container.h | 492 +++-- absl/base/attributes.h | 111 +- absl/base/call_once.h | 1 + absl/base/casts.cc | 61 + absl/base/casts.h | 137 +- absl/base/config.h | 104 +- absl/base/fast_type_id.h | 32 +- absl/base/internal/dynamic_annotations.h | 2 +- absl/base/internal/hardening.h | 136 ++ absl/base/internal/iterator_traits.h | 8 +- absl/base/internal/low_level_alloc.cc | 43 +- absl/base/internal/low_level_alloc.h | 6 + absl/base/internal/low_level_scheduling.h | 92 +- absl/base/internal/nullability_traits.h | 71 + absl/base/internal/poison.cc | 13 +- absl/base/internal/raw_logging.cc | 7 +- absl/base/internal/spinlock.cc | 50 +- absl/base/internal/spinlock.h | 103 +- absl/base/internal/sysinfo.cc | 12 +- absl/base/internal/thread_identity.h | 52 + absl/base/internal/unscaledcycleclock.cc | 6 +- absl/base/internal/unscaledcycleclock.h | 5 + .../base/internal/unscaledcycleclock_config.h | 2 +- absl/base/macros.h | 113 +- absl/base/nullability.h | 10 +- absl/base/optimization.h | 9 +- absl/base/options.h | 64 +- absl/base/policy_checks.h | 10 +- absl/base/throw_delegate.cc | 205 ++ absl/base/throw_delegate.h | 65 + absl/cleanup/cleanup.h | 9 +- absl/cleanup/internal/cleanup.h | 5 +- absl/container/btree_map.h | 70 +- absl/container/btree_set.h | 76 +- absl/container/chunked_queue.h | 757 +++++++ absl/container/fixed_array.h | 40 +- absl/container/flat_hash_map.h | 76 +- absl/container/flat_hash_set.h | 76 +- absl/container/inlined_vector.h | 78 +- absl/container/internal/btree.h | 56 +- absl/container/internal/btree_container.h | 52 +- absl/container/internal/chunked_queue.h | 173 ++ absl/container/internal/common.h | 62 +- .../container/internal/common_policy_traits.h | 2 +- absl/container/internal/compressed_tuple.h | 76 +- absl/container/internal/container_memory.h | 158 +- .../internal/hash_function_defaults.h | 16 +- .../internal/hash_generator_testing.cc | 2 - .../internal/hash_generator_testing.h | 11 +- absl/container/internal/hash_policy_testing.h | 14 - absl/container/internal/hash_policy_traits.h | 36 +- .../internal/hashtable_control_bytes.h | 104 +- absl/container/internal/hashtablez_sampler.cc | 14 +- absl/container/internal/hashtablez_sampler.h | 14 +- absl/container/internal/inlined_vector.h | 72 +- absl/container/internal/layout.h | 26 +- absl/container/internal/raw_hash_map.h | 167 +- absl/container/internal/raw_hash_set.cc | 998 ++++++---- absl/container/internal/raw_hash_set.h | 1765 ++++++++++------- .../internal/raw_hash_set_resize_impl.h | 1 - absl/container/linked_hash_map.h | 666 +++++++ absl/container/linked_hash_set.h | 527 +++++ absl/container/node_hash_map.h | 93 +- absl/container/node_hash_set.h | 83 +- absl/crc/internal/cpu_detect.cc | 29 +- absl/crc/internal/cpu_detect.h | 8 +- absl/crc/internal/crc.cc | 10 +- .../internal/crc32_x86_arm_combined_simd.h | 41 + absl/crc/internal/crc_internal.h | 16 - .../internal/crc_memcpy_x86_arm_combined.cc | 5 + absl/crc/internal/crc_x86_arm_combined.cc | 492 +++-- absl/crc/internal/gen_crc32c_consts.py | 90 + .../internal/non_temporal_arm_intrinsics.h | 6 +- absl/debugging/failure_signal_handler.cc | 4 +- absl/debugging/internal/demangle.cc | 4 +- absl/debugging/internal/demangle_rust.h | 8 + absl/debugging/internal/elf_mem_image.h | 8 +- absl/debugging/internal/examine_stack.cc | 16 +- absl/debugging/internal/examine_stack.h | 8 +- .../internal/stacktrace_aarch64-inl.inc | 13 +- absl/debugging/internal/stacktrace_config.h | 11 +- .../internal/stacktrace_emscripten-inl.inc | 24 +- .../internal/stacktrace_generic-inl.inc | 21 +- .../internal/stacktrace_powerpc-inl.inc | 5 +- .../internal/stacktrace_riscv-inl.inc | 7 +- .../debugging/internal/stacktrace_x86-inl.inc | 22 +- absl/debugging/internal/symbolize.h | 86 +- absl/debugging/internal/vdso_support.cc | 4 + absl/debugging/stacktrace.cc | 181 +- absl/debugging/stacktrace.h | 54 +- absl/debugging/symbolize.cc | 22 +- absl/debugging/symbolize_elf.inc | 232 +-- absl/debugging/symbolize_emscripten.inc | 21 +- absl/debugging/symbolize_unimplemented.inc | 8 +- absl/debugging/symbolize_win32.inc | 6 +- absl/flags/commandlineflag.h | 14 +- absl/flags/declare.h | 9 + absl/flags/flag.h | 3 +- absl/flags/internal/commandlineflag.h | 2 +- absl/flags/internal/flag.cc | 37 +- absl/flags/internal/flag.h | 67 +- absl/flags/internal/program_name.cc | 4 +- absl/flags/internal/usage.cc | 4 +- absl/flags/marshalling.cc | 17 +- absl/flags/marshalling.h | 30 +- absl/flags/parse.cc | 14 +- absl/flags/reflection.cc | 18 +- absl/flags/usage.cc | 4 +- absl/flags/usage_config.cc | 4 +- absl/functional/any_invocable.h | 41 +- absl/functional/bind_back.h | 79 + absl/functional/bind_front.h | 8 +- absl/functional/function_ref.h | 145 +- absl/functional/internal/any_invocable.h | 74 +- absl/functional/internal/back_binder.h | 95 + absl/functional/internal/front_binder.h | 8 +- absl/functional/internal/function_ref.h | 50 +- absl/hash/hash.h | 2 +- absl/hash/internal/city.cc | 90 +- absl/hash/internal/hash.cc | 393 +++- absl/hash/internal/hash.h | 810 ++++---- absl/hash/internal/spy_hash_state.h | 22 +- absl/log/absl_vlog_is_on.h | 2 - absl/log/die_if_null.cc | 4 +- absl/log/die_if_null.h | 27 +- absl/log/internal/check_impl.h | 3 +- absl/log/internal/check_op.cc | 4 + absl/log/internal/check_op.h | 178 +- absl/log/internal/conditions.h | 73 +- absl/log/internal/container.h | 312 +++ absl/log/internal/log_impl.h | 218 +- absl/log/internal/log_message.cc | 16 +- absl/log/internal/log_message.h | 20 +- absl/log/internal/log_sink_set.cc | 8 +- absl/log/internal/nullstream.h | 2 +- absl/log/internal/proto.cc | 13 + absl/log/internal/strip.h | 61 +- absl/log/internal/structured_proto.cc | 10 +- absl/log/internal/structured_proto.h | 11 +- absl/log/internal/vlog_config.cc | 60 +- absl/log/internal/vlog_config.h | 1 - absl/log/log.h | 4 +- absl/log/log_entry.cc | 260 ++- absl/log/log_entry.h | 2 + absl/log/log_streamer.h | 31 +- absl/log/vlog_is_on.h | 2 - absl/memory/memory.h | 60 +- absl/meta/internal/constexpr_testing.h | 73 + absl/meta/internal/requires.h | 67 + absl/meta/type_traits.h | 217 +- absl/numeric/int128.h | 62 +- absl/numeric/int128_have_intrinsic.inc | 38 +- absl/numeric/int128_no_intrinsic.inc | 12 +- absl/numeric/internal/bits.h | 36 +- absl/profiling/hashtable.cc | 131 ++ absl/profiling/hashtable.h | 40 + absl/profiling/internal/profile_builder.cc | 462 +++++ absl/profiling/internal/profile_builder.h | 161 ++ absl/profiling/internal/sample_recorder.h | 18 +- absl/random/benchmarks.cc | 2 +- absl/random/beta_distribution.h | 4 +- absl/random/bit_gen_ref.h | 79 +- absl/random/discrete_distribution.h | 2 +- absl/random/distributions.h | 34 +- absl/random/exponential_distribution.h | 2 +- absl/random/internal/distribution_caller.h | 29 +- absl/random/internal/entropy_pool.cc | 4 +- absl/random/internal/fast_uniform_bits.h | 2 +- absl/random/internal/generate_real.h | 2 +- absl/random/internal/iostream_state_saver.h | 4 +- absl/random/internal/mock_helpers.h | 54 +- absl/random/internal/nonsecure_base.h | 4 +- absl/random/internal/pcg_engine.h | 12 +- absl/random/internal/platform.h | 13 - absl/random/internal/randen_detect.cc | 12 +- absl/random/internal/randen_engine.h | 4 +- absl/random/internal/salted_seed_seq.h | 11 +- absl/random/internal/seed_material.cc | 36 +- absl/random/internal/seed_material.h | 3 +- absl/random/internal/traits.h | 21 + absl/random/internal/uniform_helper.h | 46 +- absl/random/mocking_access.h | 74 + absl/random/uniform_real_distribution.h | 2 +- absl/status/internal/status_internal.cc | 81 +- absl/status/internal/status_internal.h | 34 +- absl/status/internal/statusor_internal.h | 288 ++- absl/status/status.cc | 136 +- absl/status/status.h | 277 ++- absl/status/status_builder.cc | 196 ++ absl/status/status_builder.h | 978 +++++++++ absl/status/status_macros.h | 484 +++++ absl/status/status_payload_printer.h | 5 +- absl/status/statusor.h | 254 ++- absl/strings/ascii.h | 16 +- absl/strings/cord.cc | 58 +- absl/strings/cord.h | 43 +- absl/strings/cord_buffer.h | 6 +- absl/strings/escaping.cc | 430 ++-- absl/strings/escaping.h | 14 +- absl/strings/internal/append_and_overwrite.h | 93 + absl/strings/internal/cord_internal.h | 4 +- absl/strings/internal/cordz_handle.cc | 12 +- absl/strings/internal/cordz_info.cc | 48 +- absl/strings/internal/cordz_info.h | 30 +- .../internal/damerau_levenshtein_distance.cc | 6 + .../internal/damerau_levenshtein_distance.h | 1 + absl/strings/internal/escaping.cc | 141 -- absl/strings/internal/escaping.h | 26 +- absl/strings/internal/generic_printer.cc | 107 + absl/strings/internal/generic_printer.h | 115 ++ .../internal/generic_printer_internal.h | 444 +++++ absl/strings/internal/resize_uninitialized.h | 67 +- absl/strings/internal/stl_type_traits.h | 78 +- absl/strings/internal/str_format/arg.h | 36 +- absl/strings/internal/str_format/bind.h | 4 +- absl/strings/internal/str_format/checker.h | 6 +- .../internal/str_format/float_conversion.cc | 1152 ++++++++--- absl/strings/internal/str_join_internal.h | 34 +- absl/strings/internal/str_split_internal.h | 28 +- absl/strings/internal/stringify_sink.h | 12 + absl/strings/internal/stringify_stream.h | 119 ++ absl/strings/numbers.cc | 485 ++++- absl/strings/numbers.h | 41 +- absl/strings/resize_and_overwrite.h | 198 ++ absl/strings/str_cat.cc | 187 +- absl/strings/str_cat.h | 66 +- absl/strings/str_split.h | 23 +- absl/strings/string_view.h | 726 +------ absl/strings/substitute.cc | 48 +- absl/strings/substitute.h | 4 +- absl/synchronization/barrier.cc | 2 +- absl/synchronization/blocking_counter.cc | 4 +- .../internal/create_thread_identity.cc | 27 +- absl/synchronization/internal/graphcycles.cc | 63 +- .../synchronization/internal/kernel_timeout.h | 4 +- .../synchronization/internal/per_thread_sem.h | 5 + absl/synchronization/internal/thread_pool.h | 6 +- absl/synchronization/mutex.cc | 70 +- absl/synchronization/mutex.h | 293 ++- absl/synchronization/notification.cc | 10 +- absl/synchronization/notification.h | 2 +- absl/time/clock.cc | 59 +- absl/time/clock.h | 3 + absl/time/clock_interface.cc | 71 + absl/time/clock_interface.h | 93 + absl/time/duration.cc | 4 +- absl/time/format.cc | 13 +- .../cctz/include/cctz/civil_time_detail.h | 12 + .../internal/cctz/src/test_time_zone_names.cc | 515 +++++ .../internal/cctz/src/test_time_zone_names.h | 33 + .../internal/cctz/src/time_zone_format.cc | 222 ++- .../internal/cctz/src/time_zone_lookup.cc | 109 +- .../internal/cctz/src/time_zone_name_win.cc | 185 ++ .../internal/cctz/src/time_zone_name_win.h | 37 + absl/time/internal/cctz/src/tzfile.h | 25 +- absl/time/simulated_clock.cc | 225 +++ absl/time/simulated_clock.h | 109 + absl/time/time.cc | 8 +- absl/time/time.h | 27 +- absl/types/any.h | 30 +- absl/types/any_span.h | 1067 ++++++++++ absl/types/compare.h | 127 +- absl/types/internal/any_span.h | 477 +++++ absl/types/internal/span.h | 12 +- absl/types/optional.h | 35 +- absl/types/optional_ref.h | 295 +++ absl/types/source_location.cc | 18 + absl/types/source_location.h | 172 ++ absl/types/span.h | 105 +- absl/types/variant.h | 93 +- absl/utility/utility.h | 128 +- ci/absl_alternate_options.h | 8 +- ci/cmake_common.sh | 2 +- ci/cmake_install_test.sh | 6 + ci/linux_arm_clang-latest_libcxx_bazel.sh | 15 +- ci/linux_clang-latest_libcxx_asan_bazel.sh | 21 +- ci/linux_clang-latest_libcxx_bazel.sh | 15 +- ci/linux_clang-latest_libcxx_tsan_bazel.sh | 18 +- ci/linux_clang-latest_libstdcxx_bazel.sh | 12 +- ci/linux_docker_containers.sh | 8 +- ci/linux_gcc-floor_libstdcxx_bazel.sh | 14 +- ci/linux_gcc-latest_libstdcxx_bazel.sh | 11 +- ci/linux_gcc-latest_libstdcxx_cmake.sh | 6 + ci/linux_gcc_alpine_cmake.sh | 6 + ci/macos_xcode_bazel.sh | 15 +- ci/macos_xcode_cmake.sh | 11 +- ci/windows_clangcl_bazel.bat | 3 +- ci/windows_msvc_bazel.bat | 3 +- ci/windows_msvc_cmake.bat | 7 +- scripts/sync_abseil.sh | 2 +- 302 files changed, 21449 insertions(+), 6774 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/00-bug_report.yml create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 absl/base/casts.cc create mode 100644 absl/base/internal/hardening.h create mode 100644 absl/base/internal/nullability_traits.h create mode 100644 absl/base/throw_delegate.cc create mode 100644 absl/base/throw_delegate.h create mode 100644 absl/container/chunked_queue.h create mode 100644 absl/container/internal/chunked_queue.h create mode 100644 absl/container/linked_hash_map.h create mode 100644 absl/container/linked_hash_set.h create mode 100755 absl/crc/internal/gen_crc32c_consts.py create mode 100644 absl/functional/bind_back.h create mode 100644 absl/functional/internal/back_binder.h create mode 100644 absl/log/internal/container.h create mode 100644 absl/meta/internal/constexpr_testing.h create mode 100644 absl/meta/internal/requires.h create mode 100644 absl/profiling/hashtable.cc create mode 100644 absl/profiling/hashtable.h create mode 100644 absl/profiling/internal/profile_builder.cc create mode 100644 absl/profiling/internal/profile_builder.h create mode 100644 absl/random/mocking_access.h create mode 100644 absl/status/status_builder.cc create mode 100644 absl/status/status_builder.h create mode 100644 absl/status/status_macros.h create mode 100644 absl/strings/internal/append_and_overwrite.h create mode 100644 absl/strings/internal/generic_printer.cc create mode 100644 absl/strings/internal/generic_printer.h create mode 100644 absl/strings/internal/generic_printer_internal.h create mode 100644 absl/strings/internal/stringify_stream.h create mode 100644 absl/strings/resize_and_overwrite.h create mode 100644 absl/time/clock_interface.cc create mode 100644 absl/time/clock_interface.h create mode 100644 absl/time/internal/cctz/src/test_time_zone_names.cc create mode 100644 absl/time/internal/cctz/src/test_time_zone_names.h create mode 100644 absl/time/internal/cctz/src/time_zone_name_win.cc create mode 100644 absl/time/internal/cctz/src/time_zone_name_win.h create mode 100644 absl/time/simulated_clock.cc create mode 100644 absl/time/simulated_clock.h create mode 100644 absl/types/any_span.h create mode 100644 absl/types/internal/any_span.h create mode 100644 absl/types/optional_ref.h create mode 100644 absl/types/source_location.cc create mode 100644 absl/types/source_location.h mode change 100644 => 100755 scripts/sync_abseil.sh diff --git a/.github/ISSUE_TEMPLATE/00-bug_report.yml b/.github/ISSUE_TEMPLATE/00-bug_report.yml new file mode 100644 index 000000000..32cebe15a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/00-bug_report.yml @@ -0,0 +1,53 @@ +name: Bug Report +description: Let us know that something does not work as expected. +title: "[Bug]: Please title this bug report" +body: + - type: textarea + id: what-happened + attributes: + label: Describe the issue + description: What happened, and what did you expect to happen? + validations: + required: true + - type: textarea + id: steps + attributes: + label: Steps to reproduce the problem + description: It is important that we are able to reproduce the problem that you are experiencing. Please provide all code and relevant steps to reproduce the problem, including your `BUILD`/`CMakeLists.txt` file and build commands. Links to a GitHub branch or [godbolt.org](https://godbolt.org/) that demonstrate the problem are also helpful. + validations: + required: true + - type: textarea + id: version + attributes: + label: What version of Abseil are you using? + description: Please include the output of `git rev-parse HEAD` or the name of the LTS release that you are using. + validations: + required: true + - type: textarea + id: os + attributes: + label: What operating system and version are you using? + description: If you are using a Linux distribution please include the name and version of the distribution as well. + validations: + required: true + - type: textarea + id: compiler + attributes: + label: What compiler and version are you using? + description: Please include the output of `gcc -v` or `clang -v`, or the equivalent for your compiler. + validations: + required: true + - type: textarea + id: buildsystem + attributes: + label: What build system are you using? + description: Please include the output of `bazel --version` or `cmake --version`, or the equivalent for your build system. + validations: + required: true + - type: textarea + id: additional + attributes: + label: Additional context + description: Add any other context about the problem here. + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 000000000..c690fd9a5 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: false +contact_links: + - name: Question + url: https://github.com/abseil/abseil-cpp/discussions + about: Have a question? Ask us anything! :-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 000000000..ff55e3529 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,8 @@ +Thank you for your contribution to Abseil! + +Before submitting this PR, please be sure to read our [contributing +guidelines](https://github.com/abseil/abseil-cpp/blob/master/CONTRIBUTING.md). + +If you are a Googler, please also note that it is required that you send us a +Piper CL instead of using the GitHub pull-request process. The code propagation +process will deliver the change to GitHub. diff --git a/.gitignore b/.gitignore index af581aa42..7588e99b8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +# Bzlmod lockfile +MODULE.bazel.lock # Ignore all bazel-* symlinks. /bazel-* # Ignore Bazel verbose explanations @@ -13,5 +15,11 @@ CMakeLists.txt.user # Ignore generated python artifacts *.pyc copts/__pycache__/ -# SwiftPM build directory + +# Ignore build files. +.build/ .swiftpm/ + +# Ignore target under any directory. +target/ +.DS_Store diff --git a/CMake/AbseilDll.cmake b/CMake/AbseilDll.cmake index f01021bb0..66a1747bb 100644 --- a/CMake/AbseilDll.cmake +++ b/CMake/AbseilDll.cmake @@ -6,6 +6,7 @@ set(ABSL_INTERNAL_DLL_FILES "algorithm/container.h" "base/attributes.h" "base/call_once.h" + "base/casts.cc" "base/casts.h" "base/config.h" "base/const_init.h" @@ -19,35 +20,32 @@ set(ABSL_INTERNAL_DLL_FILES "base/internal/dynamic_annotations.h" "base/internal/endian.h" "base/internal/errno_saver.h" + "base/internal/hardening.h" "base/internal/hide_ptr.h" - "base/internal/identity.h" "base/internal/iterator_traits.h" "base/internal/low_level_alloc.cc" "base/internal/low_level_alloc.h" "base/internal/low_level_scheduling.h" - "base/internal/nullability_deprecated.h" + "base/internal/nullability_traits.h" "base/internal/per_thread_tls.h" "base/internal/poison.cc" "base/internal/poison.h" - "base/prefetch.h" "base/internal/pretty_function.h" "base/internal/raw_logging.cc" "base/internal/raw_logging.h" "base/internal/scheduling_mode.h" "base/internal/scoped_set_env.cc" "base/internal/scoped_set_env.h" - "base/internal/strerror.h" - "base/internal/strerror.cc" "base/internal/spinlock.cc" "base/internal/spinlock.h" "base/internal/spinlock_wait.cc" "base/internal/spinlock_wait.h" + "base/internal/strerror.cc" + "base/internal/strerror.h" "base/internal/sysinfo.cc" "base/internal/sysinfo.h" "base/internal/thread_identity.cc" "base/internal/thread_identity.h" - "base/internal/throw_delegate.cc" - "base/internal/throw_delegate.h" "base/internal/tracing.cc" "base/internal/tracing.h" "base/internal/tsan_mutex_interface.h" @@ -64,18 +62,23 @@ set(ABSL_INTERNAL_DLL_FILES "base/options.h" "base/policy_checks.h" "base/port.h" + "base/prefetch.h" "base/thread_annotations.h" + "base/throw_delegate.cc" + "base/throw_delegate.h" "cleanup/cleanup.h" "cleanup/internal/cleanup.h" "container/btree_map.h" "container/btree_set.h" - "container/hash_container_defaults.h" + "container/chunked_queue.h" "container/fixed_array.h" "container/flat_hash_map.h" "container/flat_hash_set.h" + "container/hash_container_defaults.h" "container/inlined_vector.h" "container/internal/btree.h" "container/internal/btree_container.h" + "container/internal/chunked_queue.h" "container/internal/common.h" "container/internal/common_policy_traits.h" "container/internal/compressed_tuple.h" @@ -96,23 +99,24 @@ set(ABSL_INTERNAL_DLL_FILES "container/internal/raw_hash_set.h" "container/internal/raw_hash_set_resize_impl.h" "container/internal/tracked.h" + "container/linked_hash_map.h" + "container/linked_hash_set.h" "container/node_hash_map.h" "container/node_hash_set.h" "crc/crc32c.cc" "crc/crc32c.h" "crc/internal/cpu_detect.cc" "crc/internal/cpu_detect.h" - "crc/internal/crc32c.h" - "crc/internal/crc32c_inline.h" - "crc/internal/crc32_x86_arm_combined_simd.h" "crc/internal/crc.cc" "crc/internal/crc.h" + "crc/internal/crc32_x86_arm_combined_simd.h" + "crc/internal/crc32c.h" + "crc/internal/crc32c_inline.h" "crc/internal/crc_cord_state.cc" "crc/internal/crc_cord_state.h" "crc/internal/crc_internal.h" - "crc/internal/crc_x86_arm_combined.cc" - "crc/internal/crc_memcpy_fallback.cc" "crc/internal/crc_memcpy.h" + "crc/internal/crc_memcpy_fallback.cc" "crc/internal/crc_memcpy_x86_arm_combined.cc" "crc/internal/crc_non_temporal_memcpy.cc" "crc/internal/crc_x86_arm_combined.cc" @@ -120,11 +124,6 @@ set(ABSL_INTERNAL_DLL_FILES "crc/internal/non_temporal_memcpy.h" "debugging/failure_signal_handler.cc" "debugging/failure_signal_handler.h" - "debugging/leak_check.h" - "debugging/stacktrace.cc" - "debugging/stacktrace.h" - "debugging/symbolize.cc" - "debugging/symbolize.h" "debugging/internal/address_is_readable.cc" "debugging/internal/address_is_readable.h" "debugging/internal/addresses.h" @@ -147,21 +146,26 @@ set(ABSL_INTERNAL_DLL_FILES "debugging/internal/utf8_for_code_point.h" "debugging/internal/vdso_support.cc" "debugging/internal/vdso_support.h" + "debugging/leak_check.h" + "debugging/stacktrace.cc" + "debugging/stacktrace.h" + "debugging/symbolize.cc" + "debugging/symbolize.h" "functional/any_invocable.h" - "functional/internal/front_binder.h" + "functional/bind_back.h" "functional/bind_front.h" "functional/function_ref.h" "functional/internal/any_invocable.h" + "functional/internal/back_binder.h" + "functional/internal/front_binder.h" "functional/internal/function_ref.h" "functional/overload.h" "hash/hash.h" - "hash/internal/city.h" "hash/internal/city.cc" - "hash/internal/hash.h" + "hash/internal/city.h" "hash/internal/hash.cc" + "hash/internal/hash.h" "hash/internal/spy_hash_state.h" - "hash/internal/low_level_hash.h" - "hash/internal/low_level_hash.cc" "hash/internal/weakly_mixed_integer.h" "log/absl_check.h" "log/absl_log.h" @@ -171,6 +175,8 @@ set(ABSL_INTERNAL_DLL_FILES "log/die_if_null.h" "log/globals.cc" "log/globals.h" + "log/initialize.cc" + "log/initialize.h" "log/internal/append_truncated.h" "log/internal/check_impl.h" "log/internal/check_op.cc" @@ -178,8 +184,9 @@ set(ABSL_INTERNAL_DLL_FILES "log/internal/conditions.cc" "log/internal/conditions.h" "log/internal/config.h" - "log/internal/fnmatch.h" + "log/internal/container.h" "log/internal/fnmatch.cc" + "log/internal/fnmatch.h" "log/internal/globals.cc" "log/internal/globals.h" "log/internal/log_format.cc" @@ -192,8 +199,8 @@ set(ABSL_INTERNAL_DLL_FILES "log/internal/nullguard.cc" "log/internal/nullguard.h" "log/internal/nullstream.h" - "log/internal/proto.h" "log/internal/proto.cc" + "log/internal/proto.h" "log/internal/strip.h" "log/internal/structured.h" "log/internal/structured_proto.cc" @@ -201,9 +208,8 @@ set(ABSL_INTERNAL_DLL_FILES "log/internal/vlog_config.cc" "log/internal/vlog_config.h" "log/internal/voidify.h" - "log/initialize.cc" - "log/initialize.h" "log/log.h" + "log/log_entry.cc" "log/log_entry.h" "log/log_sink.cc" "log/log_sink.h" @@ -212,16 +218,21 @@ set(ABSL_INTERNAL_DLL_FILES "log/structured.h" "log/vlog_is_on.h" "memory/memory.h" + "meta/internal/requires.h" "meta/type_traits.h" "numeric/bits.h" "numeric/int128.cc" "numeric/int128.h" "numeric/internal/bits.h" "numeric/internal/representation.h" + "profiling/hashtable.cc" + "profiling/hashtable.h" "profiling/internal/exponential_biased.cc" "profiling/internal/exponential_biased.h" "profiling/internal/periodic_sampler.cc" "profiling/internal/periodic_sampler.h" + "profiling/internal/profile_builder.cc" + "profiling/internal/profile_builder.h" "profiling/internal/sample_recorder.h" "random/bernoulli_distribution.h" "random/beta_distribution.h" @@ -233,15 +244,15 @@ set(ABSL_INTERNAL_DLL_FILES "random/gaussian_distribution.cc" "random/gaussian_distribution.h" "random/internal/distribution_caller.h" - "random/internal/fastmath.h" + "random/internal/entropy_pool.cc" + "random/internal/entropy_pool.h" "random/internal/fast_uniform_bits.h" + "random/internal/fastmath.h" "random/internal/generate_real.h" "random/internal/iostream_state_saver.h" "random/internal/nonsecure_base.h" "random/internal/pcg_engine.h" "random/internal/platform.h" - "random/internal/entropy_pool.cc" - "random/internal/entropy_pool.h" "random/internal/randen.cc" "random/internal/randen.h" "random/internal/randen_detect.cc" @@ -270,15 +281,18 @@ set(ABSL_INTERNAL_DLL_FILES "random/uniform_int_distribution.h" "random/uniform_real_distribution.h" "random/zipf_distribution.h" - "status/internal/status_internal.h" "status/internal/status_internal.cc" + "status/internal/status_internal.h" "status/internal/statusor_internal.h" - "status/status.h" "status/status.cc" - "status/statusor.h" - "status/statusor.cc" - "status/status_payload_printer.h" + "status/status.h" + "status/status_builder.cc" + "status/status_builder.h" + "status/status_macros.h" "status/status_payload_printer.cc" + "status/status_payload_printer.h" + "status/statusor.cc" + "status/statusor.h" "strings/ascii.cc" "strings/ascii.h" "strings/charconv.cc" @@ -291,6 +305,9 @@ set(ABSL_INTERNAL_DLL_FILES "strings/cord_buffer.h" "strings/escaping.cc" "strings/escaping.h" + "strings/has_absl_stringify.h" + "strings/has_ostream_operator.h" + "strings/internal/append_and_overwrite.h" "strings/internal/charconv_bigint.cc" "strings/internal/charconv_bigint.h" "strings/internal/charconv_parse.cc" @@ -304,10 +321,10 @@ set(ABSL_INTERNAL_DLL_FILES "strings/internal/cord_rep_btree_navigator.h" "strings/internal/cord_rep_btree_reader.cc" "strings/internal/cord_rep_btree_reader.h" + "strings/internal/cord_rep_consume.cc" + "strings/internal/cord_rep_consume.h" "strings/internal/cord_rep_crc.cc" "strings/internal/cord_rep_crc.h" - "strings/internal/cord_rep_consume.h" - "strings/internal/cord_rep_consume.cc" "strings/internal/cord_rep_flat.h" "strings/internal/cordz_functions.cc" "strings/internal/cordz_functions.h" @@ -320,33 +337,13 @@ set(ABSL_INTERNAL_DLL_FILES "strings/internal/cordz_statistics.h" "strings/internal/cordz_update_scope.h" "strings/internal/cordz_update_tracker.h" - "strings/internal/damerau_levenshtein_distance.h" "strings/internal/damerau_levenshtein_distance.cc" - "strings/internal/stl_type_traits.h" - "strings/internal/string_constant.h" - "strings/internal/stringify_sink.h" - "strings/internal/stringify_sink.cc" - "strings/has_absl_stringify.h" - "strings/has_ostream_operator.h" - "strings/match.cc" - "strings/match.h" - "strings/numbers.cc" - "strings/numbers.h" - "strings/str_format.h" - "strings/str_cat.cc" - "strings/str_cat.h" - "strings/str_join.h" - "strings/str_replace.cc" - "strings/str_replace.h" - "strings/str_split.cc" - "strings/str_split.h" - "strings/string_view.cc" - "strings/string_view.h" - "strings/strip.h" - "strings/substitute.cc" - "strings/substitute.h" - "strings/internal/escaping.h" + "strings/internal/damerau_levenshtein_distance.h" "strings/internal/escaping.cc" + "strings/internal/escaping.h" + "strings/internal/generic_printer.cc" + "strings/internal/generic_printer.h" + "strings/internal/generic_printer_internal.h" "strings/internal/memutil.cc" "strings/internal/memutil.h" "strings/internal/ostringstream.cc" @@ -354,6 +351,7 @@ set(ABSL_INTERNAL_DLL_FILES "strings/internal/pow10_helper.cc" "strings/internal/pow10_helper.h" "strings/internal/resize_uninitialized.h" + "strings/internal/stl_type_traits.h" "strings/internal/str_format/arg.cc" "strings/internal/str_format/arg.h" "strings/internal/str_format/bind.cc" @@ -370,47 +368,67 @@ set(ABSL_INTERNAL_DLL_FILES "strings/internal/str_format/parser.h" "strings/internal/str_join_internal.h" "strings/internal/str_split_internal.h" + "strings/internal/string_constant.h" + "strings/internal/stringify_sink.cc" + "strings/internal/stringify_sink.h" + "strings/internal/stringify_stream.h" "strings/internal/utf8.cc" "strings/internal/utf8.h" + "strings/match.cc" + "strings/match.h" + "strings/numbers.cc" + "strings/numbers.h" + "strings/resize_and_overwrite.h" + "strings/str_cat.cc" + "strings/str_cat.h" + "strings/str_format.h" + "strings/str_join.h" + "strings/str_replace.cc" + "strings/str_replace.h" + "strings/str_split.cc" + "strings/str_split.h" + "strings/strip.h" + "strings/substitute.cc" + "strings/substitute.h" "synchronization/barrier.cc" "synchronization/barrier.h" "synchronization/blocking_counter.cc" "synchronization/blocking_counter.h" - "synchronization/mutex.cc" - "synchronization/mutex.h" - "synchronization/notification.cc" - "synchronization/notification.h" "synchronization/internal/create_thread_identity.cc" "synchronization/internal/create_thread_identity.h" "synchronization/internal/futex.h" - "synchronization/internal/futex_waiter.h" "synchronization/internal/futex_waiter.cc" + "synchronization/internal/futex_waiter.h" "synchronization/internal/graphcycles.cc" "synchronization/internal/graphcycles.h" - "synchronization/internal/kernel_timeout.h" "synchronization/internal/kernel_timeout.cc" + "synchronization/internal/kernel_timeout.h" "synchronization/internal/per_thread_sem.cc" "synchronization/internal/per_thread_sem.h" - "synchronization/internal/pthread_waiter.h" "synchronization/internal/pthread_waiter.cc" - "synchronization/internal/sem_waiter.h" + "synchronization/internal/pthread_waiter.h" "synchronization/internal/sem_waiter.cc" - "synchronization/internal/stdcpp_waiter.h" + "synchronization/internal/sem_waiter.h" "synchronization/internal/stdcpp_waiter.cc" + "synchronization/internal/stdcpp_waiter.h" "synchronization/internal/thread_pool.h" "synchronization/internal/waiter.h" - "synchronization/internal/waiter_base.h" "synchronization/internal/waiter_base.cc" - "synchronization/internal/win32_waiter.h" + "synchronization/internal/waiter_base.h" "synchronization/internal/win32_waiter.cc" + "synchronization/internal/win32_waiter.h" + "synchronization/mutex.cc" + "synchronization/mutex.h" + "synchronization/notification.cc" + "synchronization/notification.h" "time/civil_time.cc" "time/civil_time.h" "time/clock.cc" "time/clock.h" + "time/clock_interface.cc" + "time/clock_interface.h" "time/duration.cc" "time/format.cc" - "time/time.cc" - "time/time.h" "time/internal/cctz/include/cctz/civil_time.h" "time/internal/cctz/include/cctz/civil_time_detail.h" "time/internal/cctz/include/cctz/time_zone.h" @@ -432,17 +450,32 @@ set(ABSL_INTERNAL_DLL_FILES "time/internal/cctz/src/time_zone_posix.h" "time/internal/cctz/src/tzfile.h" "time/internal/cctz/src/zone_info_source.cc" + "time/simulated_clock.cc" + "time/simulated_clock.h" + "time/time.cc" + "time/time.h" "types/any.h" + "types/any_span.h" "types/compare.h" + "types/internal/any_span.h" + "types/internal/span.h" "types/optional.h" + "types/optional_ref.h" + "types/source_location.cc" + "types/source_location.h" "types/span.h" - "types/internal/span.h" "types/variant.h" "utility/utility.h" "debugging/leak_check.cc" + "strings/string_view.h" ) -if(NOT MSVC) +if(MSVC) + list(APPEND ABSL_INTERNAL_DLL_FILES + "time/internal/cctz/src/time_zone_name_win.cc" + "time/internal/cctz/src/time_zone_name_win.h" + ) +else() list(APPEND ABSL_INTERNAL_DLL_FILES "flags/commandlineflag.cc" "flags/commandlineflag.h" @@ -719,8 +752,10 @@ int main() { return 0; } if(ABSL_INTERNAL_AT_LEAST_CXX20) set(ABSL_INTERNAL_CXX_STD_FEATURE cxx_std_20) -else() +elseif(ABSL_INTERNAL_AT_LEAST_CXX17) set(ABSL_INTERNAL_CXX_STD_FEATURE cxx_std_17) +else() + message(FATAL_ERROR "The compiler defaults to or is configured for C++ < 17. C++ >= 17 is required and Abseil and all libraries that use Abseil must use the same C++ language standard") endif() function(absl_internal_dll_contains) @@ -825,6 +860,9 @@ function(absl_make_dll) ${_dll_libs} ${ABSL_DEFAULT_LINKOPTS} $<$:-llog> + $<$:-ladvapi32> + $<$:-ldbghelp> + $<$:-lbcrypt> ) set_target_properties(${_dll} PROPERTIES LINKER_LANGUAGE "CXX" diff --git a/CMake/AbseilHelpers.cmake b/CMake/AbseilHelpers.cmake index 624a3c7e9..61e1ae421 100644 --- a/CMake/AbseilHelpers.cmake +++ b/CMake/AbseilHelpers.cmake @@ -326,7 +326,12 @@ Cflags: -I\${includedir}${PC_CFLAGS}\n") ) if (_build_type STREQUAL "dll") - set(ABSL_CC_LIB_DEPS abseil_dll) + if(${_in_dll}) + set(ABSL_CC_LIB_DEPS abseil_dll) + endif() + if(${_in_test_dll}) + set(ABSL_CC_LIB_DEPS abseil_test_dll) + endif() endif() target_link_libraries(${_NAME} diff --git a/CMakeLists.txt b/CMakeLists.txt index 8d3059d5c..2b6bbfed6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,8 +23,8 @@ if (POLICY CMP0141) cmake_policy(SET CMP0141 NEW) endif (POLICY CMP0141) -project(absl LANGUAGES CXX VERSION 20250512) -set(ABSL_SOVERSION "2505.0.0") +project(absl LANGUAGES CXX VERSION 20260526) +set(ABSL_SOVERSION "2605.0.0") include(CTest) # Output directory is correct by default for most build setups. However, when @@ -217,7 +217,7 @@ if(ABSL_ENABLE_INSTALL) # Handle features that require at least C++20. if (ABSL_INTERNAL_AT_LEAST_CXX20) - foreach(FEATURE "ORDERING") + foreach(FEATURE "ORDERING" "SOURCE_LOCATION") string(REPLACE "#define ABSL_OPTION_USE_STD_${FEATURE} 2" "#define ABSL_OPTION_USE_STD_${FEATURE} 1" diff --git a/FAQ.md b/FAQ.md index fbd92ce97..30291fe4f 100644 --- a/FAQ.md +++ b/FAQ.md @@ -2,9 +2,9 @@ ## Is Abseil the right home for my utility library? -Most often the answer to the question is "no." As both the [About -Abseil](https://abseil.io/about/) page and our [contributing -guidelines](https://github.com/abseil/abseil-cpp/blob/master/CONTRIBUTING.md#contribution-guidelines) +Most often the answer to the question is "no." As both the +[About Abseil](https://abseil.io/about/) page and our +[contributing guidelines](https://github.com/abseil/abseil-cpp/blob/master/CONTRIBUTING.md#contribution-guidelines) explain, Abseil contains a variety of core C++ library code that is widely used at [Google](https://www.google.com/). As such, Abseil's primary purpose is to be used as a dependency by Google's open source C++ projects. While we do hope that @@ -12,27 +12,24 @@ Abseil is also useful to the C++ community at large, this added constraint also means that we are unlikely to accept a contribution of utility code that isn't already widely used by Google. -## How to I set the C++ dialect used to build Abseil? +## How do I set the C++ dialect used to build Abseil? The short answer is that whatever mechanism you choose, you need to make sure that you set this option consistently at the global level for your entire project. If, for example, you want to set the C++ dialect to C++17, with -[Bazel](https://bazel/build/) as the build system and `gcc` or `clang` as the -compiler, there several ways to do this: -* Pass `--cxxopt=-std=c++17` on the command line (for example, `bazel build - --cxxopt=-std=c++17 ...`) -* Set the environment variable `BAZEL_CXXOPTS` (for example, - `BAZEL_CXXOPTS=-std=c++17`) -* Add `build --cxxopt=-std=c++17` to your [`.bazelrc` - file](https://docs.bazel.build/versions/master/guide.html#bazelrc) +[Bazel](https://bazel.build/) as the build system and `gcc` or `clang` as the +compiler, there are several ways to do this: * Pass `--cxxopt=-std=c++17` on the +command line (for example, `bazel build --cxxopt=-std=c++17 ...`) * Set the +environment variable `BAZEL_CXXOPTS` (for example, `BAZEL_CXXOPTS=-std=c++17`) * +Add `build --cxxopt=-std=c++17` to your +[`.bazelrc` file](https://docs.bazel.build/versions/master/guide.html#bazelrc) If you are using CMake as the build system, you'll need to add a line like -`set(CMAKE_CXX_STANDARD 17)` to your top level `CMakeLists.txt` file. If you -are developing a library designed to be used by other clients, you should -instead leave `CMAKE_CXX_STANDARD` unset and configure the minimum C++ standard -required by each of your library targets via `target_compile_features`. See the -[CMake build -instructions](https://github.com/abseil/abseil-cpp/blob/master/CMake/README.md) +`set(CMAKE_CXX_STANDARD 17)` to your top level `CMakeLists.txt` file. If you are +developing a library designed to be used by other clients, you should instead +leave `CMAKE_CXX_STANDARD` unset and configure the minimum C++ standard required +by each of your library targets via `target_compile_features`. See the +[CMake build instructions](https://github.com/abseil/abseil-cpp/blob/master/CMake/README.md) for more information. For a longer answer to this question and to understand why some other approaches @@ -42,12 +39,12 @@ Abseil?"](#what-is-abi-and-why-dont-you-recommend-using-a-pre-compiled-version-o ## What is ABI and why don't you recommend using a pre-compiled version of Abseil? -For the purposes of this discussion, you can think of -[ABI](https://en.wikipedia.org/wiki/Application_binary_interface) as the -compiled representation of the interfaces in code. This is in contrast to +For the purposes of this discussion, +[ABI](https://en.wikipedia.org/wiki/Application_binary_interface) refers to the +compiled representation of code interfaces. This contrasts with [API](https://en.wikipedia.org/wiki/Application_programming_interface), which -you can think of as the interfaces as defined by the code itself. [Abseil has a -strong promise of API compatibility, but does not make any promise of ABI +refers to the interfaces defined in the source code. [Abseil has a strong +promise of API compatibility, but does not make any promise of ABI compatibility](https://abseil.io/about/compatibility). Let's take a look at what this means in practice. @@ -72,19 +69,19 @@ program may contain conflicting definitions of the same class/function/variable/enum, etc. As a rule, all compile options that affect the ABI of a program need to be applied to the entire build on a global basis. -C++ has something called the [One Definition -Rule](https://en.wikipedia.org/wiki/One_Definition_Rule) (ODR). C++ doesn't -allow multiple definitions of the same class/function/variable/enum, etc. ODR -violations sometimes result in linker errors, but linkers do not always catch -violations. Uncaught ODR violations can result in strange runtime behaviors or -crashes that can be hard to debug. +C++ has something called the +[One Definition Rule](https://en.wikipedia.org/wiki/One_Definition_Rule) (ODR). +C++ doesn't allow multiple definitions of the same class/function/variable/enum, +etc. ODR violations sometimes result in linker errors, but linkers do not always +catch violations. Uncaught ODR violations can result in strange runtime +behaviors or crashes that can be hard to debug. If you build the Abseil library and your code using different compile options that affect ABI, there is a good chance you will run afoul of the One Definition Rule. Examples of GCC compile options that affect ABI include (but aren't limited to) language dialect (e.g. `-std=`), optimization level (e.g. `-O2`), -code generation flags (e.g. `-fexceptions`), and preprocessor defines -(e.g. `-DNDEBUG`). +code generation flags (e.g. `-fexceptions`), and preprocessor defines (e.g. +`-DNDEBUG`). If you use a pre-compiled version of Abseil, (for example, from your Linux distribution package manager or from something like @@ -98,16 +95,16 @@ all packages have been built with consistent compile options. This is one of the reasons we warn against - though do not outright reject - using Abseil as a pre-compiled library. -Another possible way that you might afoul of ABI issues is if you accidentally -include two versions of Abseil in your program. Multiple versions of Abseil can -end up within the same binary if your program uses the Abseil library and -another library also transitively depends on Abseil (resulting in what is -sometimes called the diamond dependency problem). In cases such as this you must -structure your build so that all libraries use the same version of Abseil. -[Abseil's strong promise of API compatibility between -releases](https://abseil.io/about/compatibility) means the latest "HEAD" release -of Abseil is almost certainly the right choice if you are doing as we recommend -and building all of your code from source. +Another possible way that you might run afoul of ABI issues is if you +accidentally include two versions of Abseil in your program. Multiple versions +of Abseil can end up within the same binary if your program uses the Abseil +library and another library also transitively depends on Abseil (resulting in +what is sometimes called the diamond dependency problem). In cases such as this +you must structure your build so that all libraries use the same version of +Abseil. +[Abseil's strong promise of API compatibility between releases](https://abseil.io/about/compatibility) +means the latest "HEAD" release of Abseil is almost certainly the right choice +if you are doing as we recommend and building all of your code from source. For these reasons we recommend you avoid pre-compiled code and build the Abseil library yourself in a consistent manner with the rest of your code. @@ -121,47 +118,101 @@ to make it compatible. In practice, the need to use an automated tool is extremely rare. This means that upgrading from one source release to another should be a routine practice that can and should be performed often. -We recommend you update to the [latest commit in the `master` branch of -Abseil](https://github.com/abseil/abseil-cpp/commits/master) as often as -possible. Not only will you pick up bug fixes more quickly, but if you have good -automated testing, you will catch and be able to fix any [Hyrum's -Law](https://www.hyrumslaw.com/) dependency problems on an incremental basis -instead of being overwhelmed by them and having difficulty isolating them if you -wait longer between updates. - -If you are using the [Bazel](https://bazel.build/) build system and its -[external dependencies](https://docs.bazel.build/versions/master/external.html) -feature, updating the -[`http_archive`](https://docs.bazel.build/versions/master/repo/http.html#http_archive) -rule in your -[`WORKSPACE`](https://docs.bazel.build/versions/master/be/workspace.html) for -`com_google_abseil` to point to the [latest commit in the `master` branch of -Abseil](https://github.com/abseil/abseil-cpp/commits/master) is all you need to -do. For example, on February 11, 2020, the latest commit to the master branch -was `98eb410c93ad059f9bba1bf43f5bb916fc92a5ea`. To update to this commit, you -would add the following snippet to your `WORKSPACE` file: - -``` -http_archive( - name = "com_google_absl", - urls = ["https://github.com/abseil/abseil-cpp/archive/98eb410c93ad059f9bba1bf43f5bb916fc92a5ea.zip"], # 2020-02-11T18:50:53Z - strip_prefix = "abseil-cpp-98eb410c93ad059f9bba1bf43f5bb916fc92a5ea", - sha256 = "aabf6c57e3834f8dc3873a927f37eaf69975d4b28117fc7427dfb1c661542a87", +We recommend you update to the +[latest commit in the `master` branch of Abseil](https://github.com/abseil/abseil-cpp/commits/master) +as often as possible. Not only will you pick up bug fixes more quickly, but if +you have good automated testing, you will catch and be able to fix any +[Hyrum's Law](https://www.hyrumslaw.com/) dependency problems on an incremental +basis instead of being overwhelmed by them and having difficulty isolating them +if you wait longer between updates. + +If you are using the [Bazel](https://bazel.build/) build system with +[Bzlmod](https://bazel.build/external/overview#bzlmod), you can use a +`git_override` in your `MODULE.bazel` file to track the latest commit. + +For example, to update to the latest commit, you would add (or update) the +following snippet in your `MODULE.bazel` file: + +```starlark +bazel_dep(name = "abseil-cpp", version = "20260107.1") + +git_override( + module_name = "abseil-cpp", + remote = "https://github.com/abseil/abseil-cpp.git", + # Replace the following line with the latest commit. + commit = "6ec9964c325db0610a376b3cb81de073ea6ada90", ) ``` -To get the `sha256` of this URL, run `curl -sL --output - -https://github.com/abseil/abseil-cpp/archive/98eb410c93ad059f9bba1bf43f5bb916fc92a5ea.zip -| sha256sum -`. - -You can commit the updated `WORKSPACE` file to your source control every time +You can commit the updated `MODULE.bazel` file to your source control every time you update, and if you have good automated testing, you might even consider automating this. -One thing we don't recommend is using GitHub's `master.zip` files (for example -[https://github.com/abseil/abseil-cpp/archive/master.zip](https://github.com/abseil/abseil-cpp/archive/master.zip)), -which are always the latest commit in the `master` branch, to implement live at -head. Since these `master.zip` URLs are not versioned, you will lose build -reproducibility. In addition, some build systems, including Bazel, will simply -cache this file, which means you won't actually be updating to the latest -release until your cache is cleared or invalidated. +## Why do I see strange behaviors when I use Abseil hash tables? + +Abseil's hash function uses a random seed. + +Many programmers believe incorrectly that this is a defense against +[hash flooding](https://en.wikipedia.org/wiki/Collision_attack#Hash_flooding). +While it does make a hash flooding attack more difficult, Abseil's hash function +prioritizes speed over thorough mixing (and thus is not cryptographically +secure), and the current seed implementation is also not cryptographically +secure. If you are storing a large amount of attacker-controlled data, the most +reliable defense against hash-flooding is to use a container that does not have +`O(n)` worst-case behavior. + +The real reason for hash randomization is to prevent +[Hyrum's Law](https://www.hyrumslaw.com/) dependencies on iteration order. This +has allowed us to roll out steady improvements to the implementation without +breaking users who may have otherwise written code that was dependent on +ordering or other characteristics. + +The current implementation uses a global seed, which, if linked incorrectly +(e.g., static Abseil in multiple DSOs), can cause the ODR violations. If more +than one seed is linked, different calls to the hash function may return +different values, rendering hash elements inaccessible, causing crashes, or +other arbitrarily bad behaviors. + +We are often asked for a knob to disable hash randomization. The answer is a +hard "no", even under test or under a flag, because people will find a way to +force it and allow their code or tests to depend on it. At Google-scale, the +compute costs that are saved by preserving the ability to improve the +implementation far outweigh the inconvenience of learning how to write code +resilient to change. + +## How do I use the LLVM Sanitizers with Abseil? + +LLVM Sanitizers are a suite of powerful, dynamic analysis tools that +automatically detect various critical bugs during program execution. They work +by instrumenting the compiled binary and linking a runtime library to intercept +operations and report issues. + +We receive many incorrect bug reports from users trying to use the sanitizers. +The most common cause of these issues is ODR violations in the form of an +instrumentation mismatch. This happens when users try to link libraries compiled +with sanitizer instrumentation with uninstrumented libraries. It is important +that all code in the application is built with the same sanitizer configuration. + +The easiest way to do this is to use [Bazel](https://bazel.build) and pass the +sanitizer options on the commandline, but it is important not to overlook the +importance of avoiding precompiled system libraries, including the C++ standard +library. For instance, +[MemorySanitizer requires an instrumented `libc++`](https://github.com/google/sanitizers/wiki/MemorySanitizerLibcxxHowTo). + +Since most users are not going to build `libc++` with Bazel, here is a +MemorySanitizer recipe that currently works (and could easily be tweaked for +ThreadSanitizer and friends): + +```shell +# From the root of the LLVM source tree, configure libc++ to be instrumented with MSAN: +cmake -G Ninja -S runtimes -B build_msan -DLLVM_ENABLE_RUNTIMES="libcxx;libcxxabi;libunwind" -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX="${HOME}/llvm-msan" -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DLLVM_USE_SANITIZER=MemoryWithOrigins -DLLVM_TARGETS_TO_BUILD="host" + +# Build and install it into ${HOME}/llvm-msan +ninja -C build_msan install-cxx install-cxxabi install-unwind + +# Then build (or test) your code like this: +bazel test --repo_env=CC=clang --repo_env=BAZEL_CXXOPTS=nostdinc++ --repo_env=BAZEL_LINKOPTS=-L${HOME}/llvm-msan/lib:-lc++:-lc++abi:-lgcc_s:-lm:-Wl,-rpath=${HOME}/llvm-msan/lib --repo_env=CPLUS_INCLUDE_PATH=${HOME}/llvm-msan/include/c++/v1 --copt=-fsanitize=memory --linkopt=-fsanitize=memory --linkopt=-fsanitize-link-c++-runtime ... +``` + +You should consider adding these options to a +[`.bazelrc`](https://bazel.build/run/bazelrc) file to avoid retyping them. diff --git a/MODULE.bazel b/MODULE.bazel index 48a65c795..7ed0b9220 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -16,8 +16,7 @@ module( name = "abseil-cpp", - version = "20250512.1", - compatibility_level = 1, + version = "20260526.0", ) cc_configure = use_extension("@rules_cc//cc:extensions.bzl", @@ -25,13 +24,13 @@ cc_configure = use_extension("@rules_cc//cc:extensions.bzl", dev_dependency = True) use_repo(cc_configure, "local_config_cc") -bazel_dep(name = "rules_cc", version = "0.1.1") -bazel_dep(name = "bazel_skylib", version = "1.7.1") -bazel_dep(name = "platforms", version = "0.0.11") +bazel_dep(name = "rules_cc", version = "0.2.18") +bazel_dep(name = "bazel_skylib", version = "1.9.0") +bazel_dep(name = "platforms", version = "1.1.0") bazel_dep( name = "google_benchmark", - version = "1.9.2", + version = "1.9.5", dev_dependency = True, ) @@ -39,5 +38,5 @@ bazel_dep( # intended to be used by Abseil users depend on GoogleTest. bazel_dep( name = "googletest", - version = "1.17.0", + version = "1.17.0.bcr.2", ) diff --git a/Package.swift b/Package.swift index fe934d3cc..bd67b6a3b 100644 --- a/Package.swift +++ b/Package.swift @@ -48,6 +48,10 @@ let package = Package( // other files "absl/flags/flag_benchmark.lds", "absl/abseil.podspec.gen.py", + "absl/time/internal/cctz/src/time_zone_name_win.cc", + "absl/crc/internal/gen_crc32c_consts.py", + "absl/abseil.podspec.gen.py", + "absl/flags/flag_benchmark.lds" ], sources: [ "absl/" diff --git a/absl/abseil.podspec.gen.py b/absl/abseil.podspec.gen.py index e1afa210b..e19f95119 100755 --- a/absl/abseil.podspec.gen.py +++ b/absl/abseil.podspec.gen.py @@ -42,6 +42,7 @@ 'USER_HEADER_SEARCH_PATHS' => '$(inherited) "$(PODS_TARGET_SRCROOT)"', 'USE_HEADERMAP' => 'NO', 'ALWAYS_SEARCH_USER_PATHS' => 'NO', + 'CLANG_CXX_LANGUAGE_STANDARD' => 'c++17', } s.ios.deployment_target = '12.0' s.osx.deployment_target = '10.13' diff --git a/absl/algorithm/algorithm.h b/absl/algorithm/algorithm.h index 48f59504d..4e2ebf4ea 100644 --- a/absl/algorithm/algorithm.h +++ b/absl/algorithm/algorithm.h @@ -27,6 +27,7 @@ #include #include "absl/base/config.h" +#include "absl/base/macros.h" namespace absl { ABSL_NAMESPACE_BEGIN @@ -40,8 +41,39 @@ ABSL_NAMESPACE_BEGIN // // See the documentation for the STL header for more information: // https://en.cppreference.com/w/cpp/header/algorithm -using std::equal; -using std::rotate; + +template +ABSL_DEPRECATE_AND_INLINE() +constexpr bool equal(InputIt1 first1, InputIt1 last1, InputIt2 first2) { + return std::equal(first1, last1, first2); +} + +template +ABSL_DEPRECATE_AND_INLINE() +constexpr bool equal(InputIt1 first1, InputIt1 last1, InputIt2 first2, + BinaryPredicate p) { + return std::equal(first1, last1, first2, p); +} + +template +ABSL_DEPRECATE_AND_INLINE() +constexpr bool equal(InputIt1 first1, InputIt1 last1, InputIt2 first2, + InputIt2 last2) { + return std::equal(first1, last1, first2, last2); +} + +template +ABSL_DEPRECATE_AND_INLINE() +constexpr bool equal(InputIt1 first1, InputIt1 last1, InputIt2 first2, + InputIt2 last2, BinaryPredicate p) { + return std::equal(first1, last1, first2, last2, p); +} + +template +ABSL_DEPRECATE_AND_INLINE() +constexpr ForwardIt rotate(ForwardIt first, ForwardIt n_first, ForwardIt last) { + return std::rotate(first, n_first, last); +} // linear_search() // diff --git a/absl/algorithm/container.h b/absl/algorithm/container.h index 6f9c1938f..c0934f7a9 100644 --- a/absl/algorithm/container.h +++ b/absl/algorithm/container.h @@ -52,8 +52,9 @@ #include "absl/algorithm/algorithm.h" #include "absl/base/config.h" +#include "absl/base/internal/hardening.h" +#include "absl/base/internal/iterator_traits.h" #include "absl/base/macros.h" -#include "absl/base/nullability.h" #include "absl/meta/type_traits.h" namespace absl { @@ -107,6 +108,42 @@ ABSL_INTERNAL_CONSTEXPR_SINCE_CXX17 ContainerIter c_end(C& c) { return end(c); } +// Helper to check that the `OutputRange` has enough space. +// Only performs the check if the iterators are ForwardIterators or better. +template +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX17 void AssertCopyNSize(InputSequence& input, + Size n, + OutputRange& output) { + using InputIter = ContainerIter; + using OutputIter = ContainerIter; + + if constexpr (base_internal::IsAtLeastForwardIterator::value) { + base_internal::HardeningAssert( + n <= std::distance(container_algorithm_internal::c_begin(input), + container_algorithm_internal::c_end(input))); + } + if constexpr (base_internal::IsAtLeastForwardIterator::value) { + base_internal::HardeningAssert( + n <= std::distance(container_algorithm_internal::c_begin(output), + container_algorithm_internal::c_end(output))); + } +} + +template +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX17 void AssertCopySize(InputSequence& input, + OutputRange& output) { + using InputIter = ContainerIter; + using OutputIter = ContainerIter; + if constexpr (base_internal::IsAtLeastForwardIterator::value && + base_internal::IsAtLeastForwardIterator::value) { + base_internal::HardeningAssert( + std::distance(container_algorithm_internal::c_begin(input), + container_algorithm_internal::c_end(input)) <= + std::distance(container_algorithm_internal::c_begin(output), + container_algorithm_internal::c_end(output))); + } +} + template struct IsUnorderedContainer : std::false_type {}; @@ -118,6 +155,27 @@ template struct IsUnorderedContainer> : std::true_type {}; +template +struct HasBeginEnd : std::false_type {}; + +template +struct HasBeginEnd()())), + decltype(container_algorithm_internal::end( + std::declval()()))>> + : std::true_type {}; + +// We don't support multidimensional arrays yet +template +using IsMultidimensionalArray = std::is_array>; + +template +struct IsIterator : std::false_type {}; + +template +struct IsIterator< + Iter, std::void_t::iterator_category>> + : std::true_type {}; } // namespace container_algorithm_internal // PUBLIC API @@ -198,8 +256,8 @@ ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 bool c_none_of(const C& c, Pred&& pred) { // Container-based version of the `std::for_each()` function to // apply a function to a container's elements. template -ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 decay_t c_for_each(C&& c, - Function&& f) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 std::decay_t c_for_each( + C&& c, Function&& f) { return std::for_each(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), std::forward(f)); @@ -522,9 +580,41 @@ ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 // Container-based version of the `std::copy()` function to copy a // container's elements into an iterator. template -OutputIterator c_copy(const InputSequence& input, OutputIterator output) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 + std::enable_if_t>::value && + !container_algorithm_internal::IsMultidimensionalArray< + InputSequence>::value, + std::decay_t> + c_copy(const InputSequence& input, OutputIterator&& output) { return std::copy(container_algorithm_internal::c_begin(input), - container_algorithm_internal::c_end(input), output); + container_algorithm_internal::c_end(input), + std::forward(output)); +} + +// Copies elements from `input` to `output`. `absl::c_copy(input, output)` is +// equivalent to `std::copy(std::begin(input), std::end(input), +// std::begin(output))`. +// +// The `output` container must be large enough to hold all elements of `input`; +// this function does not resize `output`. + +// If `std::size(input) > std::size(output)`, behavior is undefined. +// If `std::size(output) > std::size(input)`, only `std::size(input)` elements +// are copied, and `output` is not truncated. +template +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 + std::enable_if_t>::value && + !container_algorithm_internal::IsMultidimensionalArray< + std::remove_reference_t>::value && + !container_algorithm_internal::IsMultidimensionalArray< + InputSequence>::value, + void> + c_copy(const InputSequence& input, OutputRange&& output) { + container_algorithm_internal::AssertCopySize(input, output); + absl::c_copy(input, container_algorithm_internal::c_begin( + std::forward(output))); } // c_copy_n() @@ -532,8 +622,40 @@ OutputIterator c_copy(const InputSequence& input, OutputIterator output) { // Container-based version of the `std::copy_n()` function to copy a // container's first N elements into an iterator. template -OutputIterator c_copy_n(const C& input, Size n, OutputIterator output) { - return std::copy_n(container_algorithm_internal::c_begin(input), n, output); +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 std::enable_if_t< + container_algorithm_internal::IsIterator< + absl::remove_cvref_t>::value && + !container_algorithm_internal::IsMultidimensionalArray::value, + std::decay_t> +c_copy_n(const C& input, Size n, OutputIterator&& output) { + return std::copy_n(container_algorithm_internal::c_begin(input), n, + std::forward(output)); +} + +// Copies the first `n` elements from `input` to `output`. +// `absl::c_copy_n(input, n, output)` is equivalent to +// `std::copy_n(std::begin(input), n, std::begin(output))`. +// +// The `output` container must be large enough to hold N elements; this function +// does not resize `output`. +// +// If `n > std::size(output)` or `n > std::size(input)`, behavior is +// undefined. +// If `std::size(output) > n`, only `n` elements are copied, and `output` is not +// truncated. +template +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 std::enable_if_t< + container_algorithm_internal::HasBeginEnd< + std::add_lvalue_reference_t>::value && + !container_algorithm_internal::IsMultidimensionalArray< + std::remove_reference_t>::value && + !container_algorithm_internal::IsMultidimensionalArray::value, + void> +c_copy_n(const C& input, Size n, OutputRange&& output) { + container_algorithm_internal::AssertCopyNSize(input, n, output); + absl::c_copy_n( + input, n, + container_algorithm_internal::c_begin(std::forward(output))); } // c_copy_if() @@ -541,8 +663,8 @@ OutputIterator c_copy_n(const C& input, Size n, OutputIterator output) { // Container-based version of the `std::copy_if()` function to copy // a container's elements satisfying some condition into an iterator. template -OutputIterator c_copy_if(const InputSequence& input, OutputIterator output, - Pred&& pred) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 OutputIterator +c_copy_if(const InputSequence& input, OutputIterator output, Pred&& pred) { return std::copy_if(container_algorithm_internal::c_begin(input), container_algorithm_internal::c_end(input), output, std::forward(pred)); @@ -553,8 +675,8 @@ OutputIterator c_copy_if(const InputSequence& input, OutputIterator output, // Container-based version of the `std::copy_backward()` function to // copy a container's elements in reverse order into an iterator. template -BidirectionalIterator c_copy_backward(const C& src, - BidirectionalIterator dest) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 BidirectionalIterator +c_copy_backward(const C& src, BidirectionalIterator dest) { return std::copy_backward(container_algorithm_internal::c_begin(src), container_algorithm_internal::c_end(src), dest); } @@ -564,9 +686,36 @@ BidirectionalIterator c_copy_backward(const C& src, // Container-based version of the `std::move()` function to move // a container's elements into an iterator. template -OutputIterator c_move(C&& src, OutputIterator dest) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 + std::enable_if_t>::value && + !container_algorithm_internal::IsMultidimensionalArray< + std::remove_reference_t>::value, + std::decay_t> + c_move(C&& src, OutputIterator&& dest) { return std::move(container_algorithm_internal::c_begin(src), - container_algorithm_internal::c_end(src), dest); + container_algorithm_internal::c_end(src), + std::forward(dest)); +} + +// Moves elements from `src` to `dest`. `absl::c_move(src, dest)` is +// equivalent to `std::move(std::begin(src), std::end(src), std::begin(dest))`. +// +// The `dest` container must be large enough to hold all elements of `src`; +// this function does not resize `dest`. +template +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 + std::enable_if_t>::value && + !container_algorithm_internal::IsMultidimensionalArray< + std::remove_reference_t>::value && + !container_algorithm_internal::IsMultidimensionalArray< + std::remove_reference_t>::value, + void> + c_move(C&& src, OutputRange&& dest) { + container_algorithm_internal::AssertCopySize(src, dest); + absl::c_move(std::forward(src), container_algorithm_internal::c_begin( + std::forward(dest))); } // c_move_backward() @@ -574,7 +723,8 @@ OutputIterator c_move(C&& src, OutputIterator dest) { // Container-based version of the `std::move_backward()` function to // move a container's elements into an iterator in reverse order. template -BidirectionalIterator c_move_backward(C&& src, BidirectionalIterator dest) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 BidirectionalIterator +c_move_backward(C&& src, BidirectionalIterator dest) { return std::move_backward(container_algorithm_internal::c_begin(src), container_algorithm_internal::c_end(src), dest); } @@ -585,7 +735,9 @@ BidirectionalIterator c_move_backward(C&& src, BidirectionalIterator dest) { // swap a container's elements with another container's elements. Swaps the // first N elements of `c1` and `c2`, where N = min(size(c1), size(c2)). template -container_algorithm_internal::ContainerIter c_swap_ranges(C1& c1, C2& c2) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 + container_algorithm_internal::ContainerIter + c_swap_ranges(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); @@ -605,8 +757,8 @@ container_algorithm_internal::ContainerIter c_swap_ranges(C1& c1, C2& c2) { // result in an iterator pointing to the last transformed element in the output // range. template -OutputIterator c_transform(const InputSequence& input, OutputIterator output, - UnaryOp&& unary_op) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 OutputIterator c_transform( + const InputSequence& input, OutputIterator output, UnaryOp&& unary_op) { return std::transform(container_algorithm_internal::c_begin(input), container_algorithm_internal::c_end(input), output, std::forward(unary_op)); @@ -617,9 +769,9 @@ OutputIterator c_transform(const InputSequence& input, OutputIterator output, // where N = min(size(c1), size(c2)). template -OutputIterator c_transform(const InputSequence1& input1, - const InputSequence2& input2, OutputIterator output, - BinaryOp&& binary_op) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 OutputIterator +c_transform(const InputSequence1& input1, const InputSequence2& input2, + OutputIterator output, BinaryOp&& binary_op) { auto first1 = container_algorithm_internal::c_begin(input1); auto last1 = container_algorithm_internal::c_end(input1); auto first2 = container_algorithm_internal::c_begin(input2); @@ -638,7 +790,9 @@ OutputIterator c_transform(const InputSequence1& input1, // replace a container's elements of some value with a new value. The container // is modified in place. template -void c_replace(Sequence& sequence, const T& old_value, const T& new_value) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 void c_replace(Sequence& sequence, + const T& old_value, + const T& new_value) { std::replace(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), old_value, new_value); @@ -650,7 +804,8 @@ void c_replace(Sequence& sequence, const T& old_value, const T& new_value) { // replace a container's elements of some value with a new value based on some // condition. The container is modified in place. template -void c_replace_if(C& c, Pred&& pred, T&& new_value) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 void c_replace_if(C& c, Pred&& pred, + T&& new_value) { std::replace_if(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), std::forward(pred), std::forward(new_value)); @@ -662,8 +817,8 @@ void c_replace_if(C& c, Pred&& pred, T&& new_value) { // replace a container's elements of some value with a new value and return the // results within an iterator. template -OutputIterator c_replace_copy(const C& c, OutputIterator result, T&& old_value, - T&& new_value) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 OutputIterator c_replace_copy( + const C& c, OutputIterator result, T&& old_value, T&& new_value) { return std::replace_copy(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), result, std::forward(old_value), @@ -676,8 +831,8 @@ OutputIterator c_replace_copy(const C& c, OutputIterator result, T&& old_value, // to replace a container's elements of some value with a new value based on // some condition, and return the results within an iterator. template -OutputIterator c_replace_copy_if(const C& c, OutputIterator result, Pred&& pred, - const T& new_value) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 OutputIterator c_replace_copy_if( + const C& c, OutputIterator result, Pred&& pred, const T& new_value) { return std::replace_copy_if(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), result, std::forward(pred), new_value); @@ -688,7 +843,7 @@ OutputIterator c_replace_copy_if(const C& c, OutputIterator result, Pred&& pred, // Container-based version of the `std::fill()` function to fill a // container with some value. template -void c_fill(C& c, const T& value) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 void c_fill(C& c, const T& value) { std::fill(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), value); } @@ -698,7 +853,8 @@ void c_fill(C& c, const T& value) { // Container-based version of the `std::fill_n()` function to fill // the first N elements in a container with some value. template -void c_fill_n(C& c, Size n, const T& value) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 void c_fill_n(C& c, Size n, + const T& value) { std::fill_n(container_algorithm_internal::c_begin(c), n, value); } @@ -707,7 +863,7 @@ void c_fill_n(C& c, Size n, const T& value) { // Container-based version of the `std::generate()` function to // assign a container's elements to the values provided by the given generator. template -void c_generate(C& c, Generator&& gen) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 void c_generate(C& c, Generator&& gen) { std::generate(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), std::forward(gen)); @@ -719,8 +875,9 @@ void c_generate(C& c, Generator&& gen) { // assign a container's first N elements to the values provided by the given // generator. template -container_algorithm_internal::ContainerIter c_generate_n(C& c, Size n, - Generator&& gen) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 + container_algorithm_internal::ContainerIter + c_generate_n(C& c, Size n, Generator&& gen) { return std::generate_n(container_algorithm_internal::c_begin(c), n, std::forward(gen)); } @@ -736,8 +893,8 @@ container_algorithm_internal::ContainerIter c_generate_n(C& c, Size n, // copy a container's elements while removing any elements matching the given // `value`. template -OutputIterator c_remove_copy(const C& c, OutputIterator result, - const T& value) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 OutputIterator +c_remove_copy(const C& c, OutputIterator result, const T& value) { return std::remove_copy(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), result, value); @@ -749,8 +906,8 @@ OutputIterator c_remove_copy(const C& c, OutputIterator result, // to copy a container's elements while removing any elements matching the given // condition. template -OutputIterator c_remove_copy_if(const C& c, OutputIterator result, - Pred&& pred) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 OutputIterator +c_remove_copy_if(const C& c, OutputIterator result, Pred&& pred) { return std::remove_copy_if(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), result, std::forward(pred)); @@ -762,7 +919,8 @@ OutputIterator c_remove_copy_if(const C& c, OutputIterator result, // copy a container's elements while removing any elements containing duplicate // values. template -OutputIterator c_unique_copy(const C& c, OutputIterator result) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 OutputIterator +c_unique_copy(const C& c, OutputIterator result) { return std::unique_copy(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), result); } @@ -770,8 +928,8 @@ OutputIterator c_unique_copy(const C& c, OutputIterator result) { // Overload of c_unique_copy() for using a predicate evaluation other than // `==` for comparing uniqueness of the element values. template -OutputIterator c_unique_copy(const C& c, OutputIterator result, - BinaryPredicate&& pred) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 OutputIterator +c_unique_copy(const C& c, OutputIterator result, BinaryPredicate&& pred) { return std::unique_copy(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), result, std::forward(pred)); @@ -782,7 +940,7 @@ OutputIterator c_unique_copy(const C& c, OutputIterator result, // Container-based version of the `std::reverse()` function to // reverse a container's elements. template -void c_reverse(Sequence& sequence) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 void c_reverse(Sequence& sequence) { std::reverse(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence)); } @@ -792,7 +950,8 @@ void c_reverse(Sequence& sequence) { // Container-based version of the `std::reverse()` function to // reverse a container's elements and write them to an iterator range. template -OutputIterator c_reverse_copy(const C& sequence, OutputIterator result) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 OutputIterator +c_reverse_copy(const C& sequence, OutputIterator result) { return std::reverse_copy(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), result); @@ -805,7 +964,8 @@ OutputIterator c_reverse_copy(const C& sequence, OutputIterator result) { // the first element in the container. template > -Iterator c_rotate(C& sequence, Iterator middle) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 Iterator c_rotate(C& sequence, + Iterator middle) { return absl::rotate(container_algorithm_internal::c_begin(sequence), middle, container_algorithm_internal::c_end(sequence)); } @@ -816,10 +976,10 @@ Iterator c_rotate(C& sequence, Iterator middle) { // shift a container's elements leftward such that the `middle` element becomes // the first element in a new iterator range. template -OutputIterator c_rotate_copy( - const C& sequence, - container_algorithm_internal::ContainerIter middle, - OutputIterator result) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 OutputIterator +c_rotate_copy(const C& sequence, + container_algorithm_internal::ContainerIter middle, + OutputIterator result) { return std::rotate_copy(container_algorithm_internal::c_begin(sequence), middle, container_algorithm_internal::c_end(sequence), result); @@ -861,7 +1021,8 @@ OutputIterator c_sample(const C& c, OutputIterator result, Distance n, // to test whether all elements in the container for which `pred` returns `true` // precede those for which `pred` is `false`. template -bool c_is_partitioned(const C& c, Pred&& pred) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 bool c_is_partitioned(const C& c, + Pred&& pred) { return std::is_partitioned(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), std::forward(pred)); @@ -874,7 +1035,9 @@ bool c_is_partitioned(const C& c, Pred&& pred) { // which `pred` returns `true` precede all those for which it returns `false`, // returning an iterator to the first element of the second group. template -container_algorithm_internal::ContainerIter c_partition(C& c, Pred&& pred) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 + container_algorithm_internal::ContainerIter + c_partition(C& c, Pred&& pred) { return std::partition(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), std::forward(pred)); @@ -903,9 +1066,9 @@ container_algorithm_internal::ContainerIter c_stable_partition(C& c, template -std::pair c_partition_copy( - const C& c, OutputIterator1 out_true, OutputIterator2 out_false, - Pred&& pred) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 std::pair +c_partition_copy(const C& c, OutputIterator1 out_true, + OutputIterator2 out_false, Pred&& pred) { return std::partition_copy(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), out_true, out_false, std::forward(pred)); @@ -917,8 +1080,9 @@ std::pair c_partition_copy( // to return the first element of an already partitioned container for which // the given `pred` is not `true`. template -container_algorithm_internal::ContainerIter c_partition_point(C& c, - Pred&& pred) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 + container_algorithm_internal::ContainerIter + c_partition_point(C& c, Pred&& pred) { return std::partition_point(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), std::forward(pred)); @@ -933,7 +1097,7 @@ container_algorithm_internal::ContainerIter c_partition_point(C& c, // Container-based version of the `std::sort()` function // to sort elements in ascending order of their values. template -void c_sort(C& c) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 void c_sort(C& c) { std::sort(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c)); } @@ -941,7 +1105,7 @@ void c_sort(C& c) { // Overload of c_sort() for performing a `comp` comparison other than the // default `operator<`. template -void c_sort(C& c, LessThan&& comp) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 void c_sort(C& c, LessThan&& comp) { std::sort(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), std::forward(comp)); @@ -972,7 +1136,7 @@ void c_stable_sort(C& c, LessThan&& comp) { // Container-based version of the `std::is_sorted()` function // to evaluate whether the given container is sorted in ascending order. template -bool c_is_sorted(const C& c) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 bool c_is_sorted(const C& c) { return std::is_sorted(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c)); } @@ -980,7 +1144,8 @@ bool c_is_sorted(const C& c) { // c_is_sorted() overload for performing a `comp` comparison other than the // default `operator<`. template -bool c_is_sorted(const C& c, LessThan&& comp) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 bool c_is_sorted(const C& c, + LessThan&& comp) { return std::is_sorted(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), std::forward(comp)); @@ -992,7 +1157,7 @@ bool c_is_sorted(const C& c, LessThan&& comp) { // to rearrange elements within a container such that elements before `middle` // are sorted in ascending order. template -void c_partial_sort( +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 void c_partial_sort( RandomAccessContainer& sequence, container_algorithm_internal::ContainerIter middle) { std::partial_sort(container_algorithm_internal::c_begin(sequence), middle, @@ -1002,7 +1167,7 @@ void c_partial_sort( // Overload of c_partial_sort() for performing a `comp` comparison other than // the default `operator<`. template -void c_partial_sort( +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 void c_partial_sort( RandomAccessContainer& sequence, container_algorithm_internal::ContainerIter middle, LessThan&& comp) { @@ -1019,8 +1184,9 @@ void c_partial_sort( // At most min(result.last - result.first, sequence.last - sequence.first) // elements from the sequence will be stored in the result. template -container_algorithm_internal::ContainerIter -c_partial_sort_copy(const C& sequence, RandomAccessContainer& result) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 + container_algorithm_internal::ContainerIter + c_partial_sort_copy(const C& sequence, RandomAccessContainer& result) { return std::partial_sort_copy(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), container_algorithm_internal::c_begin(result), @@ -1030,9 +1196,10 @@ c_partial_sort_copy(const C& sequence, RandomAccessContainer& result) { // Overload of c_partial_sort_copy() for performing a `comp` comparison other // than the default `operator<`. template -container_algorithm_internal::ContainerIter -c_partial_sort_copy(const C& sequence, RandomAccessContainer& result, - LessThan&& comp) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 + container_algorithm_internal::ContainerIter + c_partial_sort_copy(const C& sequence, RandomAccessContainer& result, + LessThan&& comp) { return std::partial_sort_copy(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), container_algorithm_internal::c_begin(result), @@ -1046,7 +1213,9 @@ c_partial_sort_copy(const C& sequence, RandomAccessContainer& result, // to return the first element within a container that is not sorted in // ascending order as an iterator. template -container_algorithm_internal::ContainerIter c_is_sorted_until(C& c) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 + container_algorithm_internal::ContainerIter + c_is_sorted_until(C& c) { return std::is_sorted_until(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c)); } @@ -1054,8 +1223,9 @@ container_algorithm_internal::ContainerIter c_is_sorted_until(C& c) { // Overload of c_is_sorted_until() for performing a `comp` comparison other than // the default `operator<`. template -container_algorithm_internal::ContainerIter c_is_sorted_until( - C& c, LessThan&& comp) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 + container_algorithm_internal::ContainerIter + c_is_sorted_until(C& c, LessThan&& comp) { return std::is_sorted_until(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), std::forward(comp)); @@ -1069,7 +1239,7 @@ container_algorithm_internal::ContainerIter c_is_sorted_until( // any order, except that all preceding `nth` will be less than that element, // and all following `nth` will be greater than that element. template -void c_nth_element( +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 void c_nth_element( RandomAccessContainer& sequence, container_algorithm_internal::ContainerIter nth) { std::nth_element(container_algorithm_internal::c_begin(sequence), nth, @@ -1079,7 +1249,7 @@ void c_nth_element( // Overload of c_nth_element() for performing a `comp` comparison other than // the default `operator<`. template -void c_nth_element( +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 void c_nth_element( RandomAccessContainer& sequence, container_algorithm_internal::ContainerIter nth, LessThan&& comp) { @@ -1098,8 +1268,9 @@ void c_nth_element( // to return an iterator pointing to the first element in a sorted container // which does not compare less than `value`. template -container_algorithm_internal::ContainerIter c_lower_bound( - Sequence& sequence, const T& value) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 + container_algorithm_internal::ContainerIter + c_lower_bound(Sequence& sequence, const T& value) { return std::lower_bound(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), value); } @@ -1107,8 +1278,9 @@ container_algorithm_internal::ContainerIter c_lower_bound( // Overload of c_lower_bound() for performing a `comp` comparison other than // the default `operator<`. template -container_algorithm_internal::ContainerIter c_lower_bound( - Sequence& sequence, const T& value, LessThan&& comp) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 + container_algorithm_internal::ContainerIter + c_lower_bound(Sequence& sequence, const T& value, LessThan&& comp) { return std::lower_bound(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), value, std::forward(comp)); @@ -1120,8 +1292,9 @@ container_algorithm_internal::ContainerIter c_lower_bound( // to return an iterator pointing to the first element in a sorted container // which is greater than `value`. template -container_algorithm_internal::ContainerIter c_upper_bound( - Sequence& sequence, const T& value) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 + container_algorithm_internal::ContainerIter + c_upper_bound(Sequence& sequence, const T& value) { return std::upper_bound(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), value); } @@ -1129,8 +1302,9 @@ container_algorithm_internal::ContainerIter c_upper_bound( // Overload of c_upper_bound() for performing a `comp` comparison other than // the default `operator<`. template -container_algorithm_internal::ContainerIter c_upper_bound( - Sequence& sequence, const T& value, LessThan&& comp) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 + container_algorithm_internal::ContainerIter + c_upper_bound(Sequence& sequence, const T& value, LessThan&& comp) { return std::upper_bound(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), value, std::forward(comp)); @@ -1142,8 +1316,9 @@ container_algorithm_internal::ContainerIter c_upper_bound( // to return an iterator pair pointing to the first and last elements in a // sorted container which compare equal to `value`. template -container_algorithm_internal::ContainerIterPairType -c_equal_range(Sequence& sequence, const T& value) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 + container_algorithm_internal::ContainerIterPairType + c_equal_range(Sequence& sequence, const T& value) { return std::equal_range(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), value); } @@ -1151,8 +1326,9 @@ c_equal_range(Sequence& sequence, const T& value) { // Overload of c_equal_range() for performing a `comp` comparison other than // the default `operator<`. template -container_algorithm_internal::ContainerIterPairType -c_equal_range(Sequence& sequence, const T& value, LessThan&& comp) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 + container_algorithm_internal::ContainerIterPairType + c_equal_range(Sequence& sequence, const T& value, LessThan&& comp) { return std::equal_range(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), value, std::forward(comp)); @@ -1164,7 +1340,8 @@ c_equal_range(Sequence& sequence, const T& value, LessThan&& comp) { // to test if any element in the sorted container contains a value equivalent to // 'value'. template -bool c_binary_search(const Sequence& sequence, const T& value) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 bool c_binary_search( + const Sequence& sequence, const T& value) { return std::binary_search(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), value); @@ -1173,8 +1350,8 @@ bool c_binary_search(const Sequence& sequence, const T& value) { // Overload of c_binary_search() for performing a `comp` comparison other than // the default `operator<`. template -bool c_binary_search(const Sequence& sequence, const T& value, - LessThan&& comp) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 bool c_binary_search( + const Sequence& sequence, const T& value, LessThan&& comp) { return std::binary_search(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), value, std::forward(comp)); @@ -1189,7 +1366,8 @@ bool c_binary_search(const Sequence& sequence, const T& value, // Container-based version of the `std::merge()` function // to merge two sorted containers into a single sorted iterator. template -OutputIterator c_merge(const C1& c1, const C2& c2, OutputIterator result) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 OutputIterator +c_merge(const C1& c1, const C2& c2, OutputIterator result) { return std::merge(container_algorithm_internal::c_begin(c1), container_algorithm_internal::c_end(c1), container_algorithm_internal::c_begin(c2), @@ -1199,8 +1377,8 @@ OutputIterator c_merge(const C1& c1, const C2& c2, OutputIterator result) { // Overload of c_merge() for performing a `comp` comparison other than // the default `operator<`. template -OutputIterator c_merge(const C1& c1, const C2& c2, OutputIterator result, - LessThan&& comp) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 OutputIterator +c_merge(const C1& c1, const C2& c2, OutputIterator result, LessThan&& comp) { return std::merge(container_algorithm_internal::c_begin(c1), container_algorithm_internal::c_end(c1), container_algorithm_internal::c_begin(c2), @@ -1236,7 +1414,8 @@ void c_inplace_merge(C& c, // to test whether a sorted container `c1` entirely contains another sorted // container `c2`. template -bool c_includes(const C1& c1, const C2& c2) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 bool c_includes(const C1& c1, + const C2& c2) { return std::includes(container_algorithm_internal::c_begin(c1), container_algorithm_internal::c_end(c1), container_algorithm_internal::c_begin(c2), @@ -1246,7 +1425,8 @@ bool c_includes(const C1& c1, const C2& c2) { // Overload of c_includes() for performing a merge using a `comp` other than // `operator<`. template -bool c_includes(const C1& c1, const C2& c2, LessThan&& comp) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 bool c_includes(const C1& c1, const C2& c2, + LessThan&& comp) { return std::includes(container_algorithm_internal::c_begin(c1), container_algorithm_internal::c_end(c1), container_algorithm_internal::c_begin(c2), @@ -1266,7 +1446,8 @@ template ::value, void>::type> -OutputIterator c_set_union(const C1& c1, const C2& c2, OutputIterator output) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 OutputIterator +c_set_union(const C1& c1, const C2& c2, OutputIterator output) { return std::set_union(container_algorithm_internal::c_begin(c1), container_algorithm_internal::c_end(c1), container_algorithm_internal::c_begin(c2), @@ -1282,8 +1463,8 @@ template ::value, void>::type> -OutputIterator c_set_union(const C1& c1, const C2& c2, OutputIterator output, - LessThan&& comp) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 OutputIterator c_set_union( + const C1& c1, const C2& c2, OutputIterator output, LessThan&& comp) { return std::set_union(container_algorithm_internal::c_begin(c1), container_algorithm_internal::c_end(c1), container_algorithm_internal::c_begin(c2), @@ -1302,13 +1483,13 @@ template ::value, void>::type> -OutputIterator c_set_intersection(const C1& c1, const C2& c2, - OutputIterator output) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 OutputIterator +c_set_intersection(const C1& c1, const C2& c2, OutputIterator output) { // In debug builds, ensure that both containers are sorted with respect to the // default comparator. std::set_intersection requires the containers be sorted // using operator<. - assert(absl::c_is_sorted(c1)); - assert(absl::c_is_sorted(c2)); + ABSL_ASSERT(absl::c_is_sorted(c1)); + ABSL_ASSERT(absl::c_is_sorted(c2)); return std::set_intersection(container_algorithm_internal::c_begin(c1), container_algorithm_internal::c_end(c1), container_algorithm_internal::c_begin(c2), @@ -1324,13 +1505,13 @@ template ::value, void>::type> -OutputIterator c_set_intersection(const C1& c1, const C2& c2, - OutputIterator output, LessThan&& comp) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 OutputIterator c_set_intersection( + const C1& c1, const C2& c2, OutputIterator output, LessThan&& comp) { // In debug builds, ensure that both containers are sorted with respect to the // default comparator. std::set_intersection requires the containers be sorted // using the same comparator. - assert(absl::c_is_sorted(c1, comp)); - assert(absl::c_is_sorted(c2, comp)); + ABSL_ASSERT(absl::c_is_sorted(c1, comp)); + ABSL_ASSERT(absl::c_is_sorted(c2, comp)); return std::set_intersection(container_algorithm_internal::c_begin(c1), container_algorithm_internal::c_end(c1), container_algorithm_internal::c_begin(c2), @@ -1350,8 +1531,8 @@ template ::value, void>::type> -OutputIterator c_set_difference(const C1& c1, const C2& c2, - OutputIterator output) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 OutputIterator +c_set_difference(const C1& c1, const C2& c2, OutputIterator output) { return std::set_difference(container_algorithm_internal::c_begin(c1), container_algorithm_internal::c_end(c1), container_algorithm_internal::c_begin(c2), @@ -1367,8 +1548,8 @@ template ::value, void>::type> -OutputIterator c_set_difference(const C1& c1, const C2& c2, - OutputIterator output, LessThan&& comp) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 OutputIterator c_set_difference( + const C1& c1, const C2& c2, OutputIterator output, LessThan&& comp) { return std::set_difference(container_algorithm_internal::c_begin(c1), container_algorithm_internal::c_end(c1), container_algorithm_internal::c_begin(c2), @@ -1388,8 +1569,8 @@ template ::value, void>::type> -OutputIterator c_set_symmetric_difference(const C1& c1, const C2& c2, - OutputIterator output) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 OutputIterator +c_set_symmetric_difference(const C1& c1, const C2& c2, OutputIterator output) { return std::set_symmetric_difference( container_algorithm_internal::c_begin(c1), container_algorithm_internal::c_end(c1), @@ -1406,9 +1587,8 @@ template ::value, void>::type> -OutputIterator c_set_symmetric_difference(const C1& c1, const C2& c2, - OutputIterator output, - LessThan&& comp) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 OutputIterator c_set_symmetric_difference( + const C1& c1, const C2& c2, OutputIterator output, LessThan&& comp) { return std::set_symmetric_difference( container_algorithm_internal::c_begin(c1), container_algorithm_internal::c_end(c1), @@ -1426,7 +1606,8 @@ OutputIterator c_set_symmetric_difference(const C1& c1, const C2& c2, // Container-based version of the `std::push_heap()` function // to push a value onto a container heap. template -void c_push_heap(RandomAccessContainer& sequence) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 void c_push_heap( + RandomAccessContainer& sequence) { std::push_heap(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence)); } @@ -1434,7 +1615,8 @@ void c_push_heap(RandomAccessContainer& sequence) { // Overload of c_push_heap() for performing a push operation on a heap using a // `comp` other than `operator<`. template -void c_push_heap(RandomAccessContainer& sequence, LessThan&& comp) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 void c_push_heap( + RandomAccessContainer& sequence, LessThan&& comp) { std::push_heap(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), std::forward(comp)); @@ -1445,7 +1627,8 @@ void c_push_heap(RandomAccessContainer& sequence, LessThan&& comp) { // Container-based version of the `std::pop_heap()` function // to pop a value from a heap container. template -void c_pop_heap(RandomAccessContainer& sequence) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 void c_pop_heap( + RandomAccessContainer& sequence) { std::pop_heap(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence)); } @@ -1453,7 +1636,8 @@ void c_pop_heap(RandomAccessContainer& sequence) { // Overload of c_pop_heap() for performing a pop operation on a heap using a // `comp` other than `operator<`. template -void c_pop_heap(RandomAccessContainer& sequence, LessThan&& comp) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 void c_pop_heap( + RandomAccessContainer& sequence, LessThan&& comp) { std::pop_heap(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), std::forward(comp)); @@ -1464,7 +1648,8 @@ void c_pop_heap(RandomAccessContainer& sequence, LessThan&& comp) { // Container-based version of the `std::make_heap()` function // to make a container a heap. template -void c_make_heap(RandomAccessContainer& sequence) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 void c_make_heap( + RandomAccessContainer& sequence) { std::make_heap(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence)); } @@ -1472,7 +1657,8 @@ void c_make_heap(RandomAccessContainer& sequence) { // Overload of c_make_heap() for performing heap comparisons using a // `comp` other than `operator<` template -void c_make_heap(RandomAccessContainer& sequence, LessThan&& comp) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 void c_make_heap( + RandomAccessContainer& sequence, LessThan&& comp) { std::make_heap(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), std::forward(comp)); @@ -1483,7 +1669,8 @@ void c_make_heap(RandomAccessContainer& sequence, LessThan&& comp) { // Container-based version of the `std::sort_heap()` function // to sort a heap into ascending order (after which it is no longer a heap). template -void c_sort_heap(RandomAccessContainer& sequence) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 void c_sort_heap( + RandomAccessContainer& sequence) { std::sort_heap(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence)); } @@ -1491,7 +1678,8 @@ void c_sort_heap(RandomAccessContainer& sequence) { // Overload of c_sort_heap() for performing heap comparisons using a // `comp` other than `operator<` template -void c_sort_heap(RandomAccessContainer& sequence, LessThan&& comp) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 void c_sort_heap( + RandomAccessContainer& sequence, LessThan&& comp) { std::sort_heap(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), std::forward(comp)); @@ -1502,7 +1690,8 @@ void c_sort_heap(RandomAccessContainer& sequence, LessThan&& comp) { // Container-based version of the `std::is_heap()` function // to check whether the given container is a heap. template -bool c_is_heap(const RandomAccessContainer& sequence) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 bool c_is_heap( + const RandomAccessContainer& sequence) { return std::is_heap(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence)); } @@ -1510,7 +1699,8 @@ bool c_is_heap(const RandomAccessContainer& sequence) { // Overload of c_is_heap() for performing heap comparisons using a // `comp` other than `operator<` template -bool c_is_heap(const RandomAccessContainer& sequence, LessThan&& comp) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 bool c_is_heap( + const RandomAccessContainer& sequence, LessThan&& comp) { return std::is_heap(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), std::forward(comp)); @@ -1521,8 +1711,9 @@ bool c_is_heap(const RandomAccessContainer& sequence, LessThan&& comp) { // Container-based version of the `std::is_heap_until()` function // to find the first element in a given container which is not in heap order. template -container_algorithm_internal::ContainerIter -c_is_heap_until(RandomAccessContainer& sequence) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 + container_algorithm_internal::ContainerIter + c_is_heap_until(RandomAccessContainer& sequence) { return std::is_heap_until(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence)); } @@ -1530,8 +1721,9 @@ c_is_heap_until(RandomAccessContainer& sequence) { // Overload of c_is_heap_until() for performing heap comparisons using a // `comp` other than `operator<` template -container_algorithm_internal::ContainerIter -c_is_heap_until(RandomAccessContainer& sequence, LessThan&& comp) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 + container_algorithm_internal::ContainerIter + c_is_heap_until(RandomAccessContainer& sequence, LessThan&& comp) { return std::is_heap_until(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), std::forward(comp)); @@ -1626,8 +1818,8 @@ ABSL_INTERNAL_CONSTEXPR_SINCE_CXX17 // that capital letters ("A-Z") have ASCII values less than lowercase letters // ("a-z"). template -bool c_lexicographical_compare(const Sequence1& sequence1, - const Sequence2& sequence2) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 bool c_lexicographical_compare( + const Sequence1& sequence1, const Sequence2& sequence2) { return std::lexicographical_compare( container_algorithm_internal::c_begin(sequence1), container_algorithm_internal::c_end(sequence1), @@ -1638,8 +1830,8 @@ bool c_lexicographical_compare(const Sequence1& sequence1, // Overload of c_lexicographical_compare() for performing a lexicographical // comparison using a `comp` operator instead of `operator<`. template -bool c_lexicographical_compare(const Sequence1& sequence1, - const Sequence2& sequence2, LessThan&& comp) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 bool c_lexicographical_compare( + const Sequence1& sequence1, const Sequence2& sequence2, LessThan&& comp) { return std::lexicographical_compare( container_algorithm_internal::c_begin(sequence1), container_algorithm_internal::c_end(sequence1), @@ -1654,7 +1846,7 @@ bool c_lexicographical_compare(const Sequence1& sequence1, // to rearrange a container's elements into the next lexicographically greater // permutation. template -bool c_next_permutation(C& c) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 bool c_next_permutation(C& c) { return std::next_permutation(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c)); } @@ -1662,7 +1854,8 @@ bool c_next_permutation(C& c) { // Overload of c_next_permutation() for performing a lexicographical // comparison using a `comp` operator instead of `operator<`. template -bool c_next_permutation(C& c, LessThan&& comp) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 bool c_next_permutation(C& c, + LessThan&& comp) { return std::next_permutation(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), std::forward(comp)); @@ -1674,7 +1867,7 @@ bool c_next_permutation(C& c, LessThan&& comp) { // to rearrange a container's elements into the next lexicographically lesser // permutation. template -bool c_prev_permutation(C& c) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 bool c_prev_permutation(C& c) { return std::prev_permutation(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c)); } @@ -1682,7 +1875,8 @@ bool c_prev_permutation(C& c) { // Overload of c_prev_permutation() for performing a lexicographical // comparison using a `comp` operator instead of `operator<`. template -bool c_prev_permutation(C& c, LessThan&& comp) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 bool c_prev_permutation(C& c, + LessThan&& comp) { return std::prev_permutation(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), std::forward(comp)); @@ -1698,7 +1892,8 @@ bool c_prev_permutation(C& c, LessThan&& comp) { // to compute successive values of `value`, as if incremented with `++value` // after each element is written, and write them to the container. template -void c_iota(Sequence& sequence, const T& value) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 void c_iota(Sequence& sequence, + const T& value) { std::iota(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), value); } @@ -1710,10 +1905,11 @@ void c_iota(Sequence& sequence, const T& value) { // accumulation by value. // // Note: Due to a language technicality this function has return type -// absl::decay_t. As a user of this function you can casually read +// std::decay_t. As a user of this function you can casually read // this as "returns T by value" and assume it does the right thing. template -decay_t c_accumulate(const Sequence& sequence, T&& init) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 std::decay_t c_accumulate( + const Sequence& sequence, T&& init) { return std::accumulate(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), std::forward(init)); @@ -1722,8 +1918,8 @@ decay_t c_accumulate(const Sequence& sequence, T&& init) { // Overload of c_accumulate() for using a binary operations other than // addition for computing the accumulation. template -decay_t c_accumulate(const Sequence& sequence, T&& init, - BinaryOp&& binary_op) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 std::decay_t c_accumulate( + const Sequence& sequence, T&& init, BinaryOp&& binary_op) { return std::accumulate(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), std::forward(init), @@ -1736,11 +1932,11 @@ decay_t c_accumulate(const Sequence& sequence, T&& init, // to compute the cumulative inner product of container element pairs. // // Note: Due to a language technicality this function has return type -// absl::decay_t. As a user of this function you can casually read +// std::decay_t. As a user of this function you can casually read // this as "returns T by value" and assume it does the right thing. template -decay_t c_inner_product(const Sequence1& factors1, const Sequence2& factors2, - T&& sum) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 std::decay_t c_inner_product( + const Sequence1& factors1, const Sequence2& factors2, T&& sum) { return std::inner_product(container_algorithm_internal::c_begin(factors1), container_algorithm_internal::c_end(factors1), container_algorithm_internal::c_begin(factors2), @@ -1752,8 +1948,9 @@ decay_t c_inner_product(const Sequence1& factors1, const Sequence2& factors2, // the product between the two container's element pair). template -decay_t c_inner_product(const Sequence1& factors1, const Sequence2& factors2, - T&& sum, BinaryOp1&& op1, BinaryOp2&& op2) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 std::decay_t c_inner_product( + const Sequence1& factors1, const Sequence2& factors2, T&& sum, + BinaryOp1&& op1, BinaryOp2&& op2) { return std::inner_product(container_algorithm_internal::c_begin(factors1), container_algorithm_internal::c_end(factors1), container_algorithm_internal::c_begin(factors2), @@ -1767,8 +1964,8 @@ decay_t c_inner_product(const Sequence1& factors1, const Sequence2& factors2, // function to compute the difference between each element and the one preceding // it and write it to an iterator. template -OutputIt c_adjacent_difference(const InputSequence& input, - OutputIt output_first) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 OutputIt +c_adjacent_difference(const InputSequence& input, OutputIt output_first) { return std::adjacent_difference(container_algorithm_internal::c_begin(input), container_algorithm_internal::c_end(input), output_first); @@ -1777,8 +1974,8 @@ OutputIt c_adjacent_difference(const InputSequence& input, // Overload of c_adjacent_difference() for using a binary operation other than // subtraction to compute the adjacent difference. template -OutputIt c_adjacent_difference(const InputSequence& input, - OutputIt output_first, BinaryOp&& op) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 OutputIt c_adjacent_difference( + const InputSequence& input, OutputIt output_first, BinaryOp&& op) { return std::adjacent_difference(container_algorithm_internal::c_begin(input), container_algorithm_internal::c_end(input), output_first, std::forward(op)); @@ -1791,7 +1988,8 @@ OutputIt c_adjacent_difference(const InputSequence& input, // to an iterator. The partial sum is the sum of all element values so far in // the sequence. template -OutputIt c_partial_sum(const InputSequence& input, OutputIt output_first) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 OutputIt +c_partial_sum(const InputSequence& input, OutputIt output_first) { return std::partial_sum(container_algorithm_internal::c_begin(input), container_algorithm_internal::c_end(input), output_first); @@ -1800,8 +1998,8 @@ OutputIt c_partial_sum(const InputSequence& input, OutputIt output_first) { // Overload of c_partial_sum() for using a binary operation other than addition // to compute the "partial sum". template -OutputIt c_partial_sum(const InputSequence& input, OutputIt output_first, - BinaryOp&& op) { +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 OutputIt c_partial_sum( + const InputSequence& input, OutputIt output_first, BinaryOp&& op) { return std::partial_sum(container_algorithm_internal::c_begin(input), container_algorithm_internal::c_end(input), output_first, std::forward(op)); diff --git a/absl/base/attributes.h b/absl/base/attributes.h index d009f6d49..5887fcafb 100644 --- a/absl/base/attributes.h +++ b/absl/base/attributes.h @@ -531,6 +531,25 @@ #define ABSL_XRAY_LOG_ARGS(N) #endif +// ABSL_ATTRIBUTE_NULL_AFTER_MOVE +// +// Indicates that a user-defined smart-pointer-like type makes guarantees on the +// state of a moved-from object, leaving it in a null state, where it can be +// used as long as it is not dereferenced. In other words, these are the same +// semantics that smart pointers from the standard library provide. +// +// The clang-tidy check bugprone-use-after-move allows member functions of types +// marked with this attribute to be called on objects that have been moved from; +// without the attribute, this would result in a use-after-move warning. +#if ABSL_HAVE_CPP_ATTRIBUTE(clang::annotate) && defined(__clang__) && \ + __clang_major__ >= 12 +#define ABSL_ATTRIBUTE_NULL_AFTER_MOVE \ + [[clang::annotate("clang-tidy", "bugprone-use-after-move", \ + "null_after_move")]] +#else +#define ABSL_ATTRIBUTE_NULL_AFTER_MOVE +#endif + // ABSL_ATTRIBUTE_REINITIALIZES // // Indicates that a member function reinitializes the entire object to a known @@ -553,7 +572,7 @@ // // Prevents the compiler from complaining about variables that appear unused. // -// Deprecated: Use the standard C++17 `[[maybe_unused]` instead. +// Deprecated: Use the standard C++17 `[[maybe_unused]]` instead. // // Due to differences in positioning requirements between the old, compiler // specific __attribute__ syntax and the now standard `[[maybe_unused]]`, this @@ -580,7 +599,11 @@ // Instructs the compiler not to use natural alignment for a tagged data // structure, but instead to reduce its alignment to 1. // -// Therefore, DO NOT APPLY THIS ATTRIBUTE TO STRUCTS CONTAINING ATOMICS. Doing +// Use of this attribute is HIGHLY DISCOURAGED. Taking the address of or +// binding a reference to any unaligned member is UB, and it is very easy to +// do so unintentionally when passing such members as function arguments. +// +// DO NOT APPLY THIS ATTRIBUTE TO STRUCTS CONTAINING ATOMICS. Doing // so can cause atomic variables to be mis-aligned and silently violate // atomicity on x86. // @@ -718,46 +741,6 @@ #define ABSL_INTERNAL_RESTORE_DEPRECATED_DECLARATION_WARNING #endif // defined(__GNUC__) || defined(__clang__) -// ABSL_CONST_INIT -// -// A variable declaration annotated with the `ABSL_CONST_INIT` attribute will -// not compile (on supported platforms) unless the variable has a constant -// initializer. This is useful for variables with static and thread storage -// duration, because it guarantees that they will not suffer from the so-called -// "static init order fiasco". -// -// This attribute must be placed on the initializing declaration of the -// variable. Some compilers will give a -Wmissing-constinit warning when this -// attribute is placed on some other declaration but missing from the -// initializing declaration. -// -// In some cases (notably with thread_local variables), `ABSL_CONST_INIT` can -// also be used in a non-initializing declaration to tell the compiler that a -// variable is already initialized, reducing overhead that would otherwise be -// incurred by a hidden guard variable. Thus annotating all declarations with -// this attribute is recommended to potentially enhance optimization. -// -// Example: -// -// class MyClass { -// public: -// ABSL_CONST_INIT static MyType my_var; -// }; -// -// ABSL_CONST_INIT MyType MyClass::my_var = MakeMyType(...); -// -// For code or headers that are assured to only build with C++20 and up, prefer -// just using the standard `constinit` keyword directly over this macro. -// -// Note that this attribute is redundant if the variable is declared constexpr. -#if defined(__cpp_constinit) && __cpp_constinit >= 201907L -#define ABSL_CONST_INIT constinit -#elif ABSL_HAVE_CPP_ATTRIBUTE(clang::require_constant_initialization) -#define ABSL_CONST_INIT [[clang::require_constant_initialization]] -#else -#define ABSL_CONST_INIT -#endif - // ABSL_REQUIRE_EXPLICIT_INIT // // ABSL_REQUIRE_EXPLICIT_INIT is placed *after* the data members of an aggregate @@ -828,6 +811,46 @@ struct AbslInternal_YouForgotToExplicitlyInitializeAField { }; #endif +// ABSL_CONST_INIT +// +// A variable declaration annotated with the `ABSL_CONST_INIT` attribute will +// not compile (on supported platforms) unless the variable has a constant +// initializer. This is useful for variables with static and thread storage +// duration, because it guarantees that they will not suffer from the so-called +// "static init order fiasco". +// +// This attribute must be placed on the initializing declaration of the +// variable. Some compilers will give a -Wmissing-constinit warning when this +// attribute is placed on some other declaration but missing from the +// initializing declaration. +// +// In some cases (notably with thread_local variables), `ABSL_CONST_INIT` can +// also be used in a non-initializing declaration to tell the compiler that a +// variable is already initialized, reducing overhead that would otherwise be +// incurred by a hidden guard variable. Thus annotating all declarations with +// this attribute is recommended to potentially enhance optimization. +// +// Example: +// +// class MyClass { +// public: +// ABSL_CONST_INIT static MyType my_var; +// }; +// +// ABSL_CONST_INIT MyType MyClass::my_var = MakeMyType(...); +// +// For code or headers that are assured to only build with C++20 and up, prefer +// just using the standard `constinit` keyword directly over this macro. +// +// Note that this attribute is redundant if the variable is declared constexpr. +#if defined(__cpp_constinit) && __cpp_constinit >= 201907L +#define ABSL_CONST_INIT constinit +#elif ABSL_HAVE_CPP_ATTRIBUTE(clang::require_constant_initialization) +#define ABSL_CONST_INIT [[clang::require_constant_initialization]] +#else +#define ABSL_CONST_INIT +#endif + // ABSL_ATTRIBUTE_PURE_FUNCTION // // ABSL_ATTRIBUTE_PURE_FUNCTION is used to annotate declarations of "pure" @@ -929,7 +952,7 @@ struct AbslInternal_YouForgotToExplicitlyInitializeAField { // We disable this on Clang versions < 13 because of the following // false-positive: // -// absl::string_view f(absl::optional sv) { return *sv; } +// absl::string_view f(std::optional sv) { return *sv; } // // See the following links for details: // https://reviews.llvm.org/D64448 @@ -960,7 +983,7 @@ struct AbslInternal_YouForgotToExplicitlyInitializeAField { // We disable this on Clang versions < 13 because of the following // false-positive: // -// absl::string_view f(absl::optional sv) { return *sv; } +// absl::string_view f(std::optional sv) { return *sv; } // // See the following links for details: // https://reviews.llvm.org/D64448 diff --git a/absl/base/call_once.h b/absl/base/call_once.h index 7bfd91610..1abac8679 100644 --- a/absl/base/call_once.h +++ b/absl/base/call_once.h @@ -160,6 +160,7 @@ template old_control != kOnceRunning && old_control != kOnceWaiter && old_control != kOnceDone) { + // Memory corruption may cause this error. ABSL_RAW_LOG(FATAL, "Unexpected value for control word: 0x%lx", static_cast(old_control)); // NOLINT } diff --git a/absl/base/casts.cc b/absl/base/casts.cc new file mode 100644 index 000000000..d864a8c55 --- /dev/null +++ b/absl/base/casts.cc @@ -0,0 +1,61 @@ +// Copyright 2025 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/casts.h" + +#include +#include + +#include "absl/base/config.h" +#include "absl/base/internal/raw_logging.h" + +#ifdef ABSL_INTERNAL_HAS_CXA_DEMANGLE +#include +#endif + +namespace absl { +ABSL_NAMESPACE_BEGIN + +namespace base_internal { + +namespace { + +std::string DemangleCppString(const char* mangled) { + std::string out; + int status = 0; + char* demangled = nullptr; +#ifdef 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 + +void BadDownCastCrash(const char* source_type, const char* target_type) { + ABSL_RAW_LOG(FATAL, "down cast from %s to %s failed", + DemangleCppString(source_type).c_str(), + DemangleCppString(target_type).c_str()); +} + +} // namespace base_internal + +ABSL_NAMESPACE_END +} // namespace absl diff --git a/absl/base/casts.h b/absl/base/casts.h index e0b11bbe4..928f4091e 100644 --- a/absl/base/casts.h +++ b/absl/base/casts.h @@ -27,14 +27,24 @@ #include #include #include +#include #include +#ifdef __has_include +#if __has_include() +#include // For __cpp_lib_bit_cast. +#endif +#endif + #if defined(__cpp_lib_bit_cast) && __cpp_lib_bit_cast >= 201806L #include // For std::bit_cast. #endif // defined(__cpp_lib_bit_cast) && __cpp_lib_bit_cast >= 201806L -#include "absl/base/internal/identity.h" +#include "absl/base/attributes.h" +#include "absl/base/config.h" #include "absl/base/macros.h" +#include "absl/base/optimization.h" +#include "absl/base/options.h" #include "absl/meta/type_traits.h" namespace absl { @@ -90,9 +100,26 @@ ABSL_NAMESPACE_BEGIN // // Such implicit cast chaining may be useful within template logic. template -constexpr To implicit_cast(typename absl::internal::type_identity_t to) { +constexpr std::enable_if_t< + !type_traits_internal::IsView, std::remove_cv_t>>::value, + To> +implicit_cast(absl::type_identity_t to) { + return to; +} +template +constexpr std::enable_if_t< + type_traits_internal::IsView, + std::remove_cv_t>>::value, + To> +implicit_cast(absl::type_identity_t to ABSL_ATTRIBUTE_LIFETIME_BOUND) { return to; } +template +constexpr std::enable_if_t, To> implicit_cast( + absl::type_identity_t to ABSL_ATTRIBUTE_LIFETIME_BOUND) { + return std::forward>(to); +} // bit_cast() // @@ -174,6 +201,112 @@ inline Dest bit_cast(const Source& source) { #endif // defined(__cpp_lib_bit_cast) && __cpp_lib_bit_cast >= 201806L +namespace base_internal { + +[[noreturn]] ABSL_ATTRIBUTE_NOINLINE void BadDownCastCrash( + const char* source_type, const char* target_type); + +template +inline void ValidateDownCast(From* f ABSL_ATTRIBUTE_UNUSED) { + // Assert only if RTTI is enabled and in debug mode or hardened asserts are + // enabled. +#ifdef ABSL_INTERNAL_HAS_RTTI +#if !defined(NDEBUG) || (ABSL_OPTION_HARDENED == 1) + // Suppress erroneous nonnull comparison warning on older GCC. +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wnonnull-compare" +#endif + if (ABSL_PREDICT_FALSE(f != nullptr && dynamic_cast(f) == nullptr)) { +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic pop +#endif + absl::base_internal::BadDownCastCrash( + typeid(*f).name(), typeid(std::remove_pointer_t).name()); + } +#endif +#endif +} + +} // namespace base_internal + +// An "upcast", i.e. a conversion from a pointer to an object to a pointer to a +// base subobject, always succeeds if the base is unambiguous and accessible, +// and so it's fine to use implicit_cast. +// +// A "downcast", i.e. a conversion from a pointer to an object to a pointer +// to a more-derived object that may contain the original object as a base +// subobject, cannot safely be done using static_cast, because you do not +// generally know whether the source object is really the base subobject of +// a containing, more-derived object of the target type. Thus, when you +// downcast in a polymorphic type hierarchy, you should use the following +// function template. +// +// This function only returns null when the input is null. In debug mode, we +// use dynamic_cast to double-check whether the downcast is legal (we die if +// it's not). In normal mode, we do the efficient static_cast instead. Because +// the process will die in debug mode, it's important to test to make sure the +// cast is legal before calling this function! +// +// dynamic_cast should be avoided except as allowed by the style guide +// (https://google.github.io/styleguide/cppguide.html#Run-Time_Type_Information__RTTI_). + +template // use like this: down_cast(foo); +[[nodiscard]] +inline To down_cast(From* f) { // so we only accept pointers + static_assert(std::is_pointer::value, "target type not a pointer"); + // dynamic_cast allows casting to the same type or a more cv-qualified + // version of the same type without them being polymorphic. + if constexpr (!std::is_same>, + std::remove_cv_t>::value) { + static_assert(std::is_polymorphic::value, + "source type must be polymorphic"); + static_assert(std::is_polymorphic>::value, + "target type must be polymorphic"); + } + static_assert( + std::is_convertible>*, + std::remove_cv_t*>::value, + "target type not derived from source type"); + + absl::base_internal::ValidateDownCast(f); + + return static_cast(f); +} + +// Overload of down_cast for references. Use like this: +// absl::down_cast(foo). The code is slightly convoluted because we're still +// using the pointer form of dynamic cast. (The reference form throws an +// exception if it fails.) +// +// There's no need for a special const overload either for the pointer +// or the reference form. If you call down_cast with a const T&, the +// compiler will just bind From to const T. +template +[[nodiscard]] +inline To down_cast(From& f) { + static_assert(std::is_lvalue_reference::value, + "target type not a reference"); + // dynamic_cast allows casting to the same type or a more cv-qualified + // version of the same type without them being polymorphic. + if constexpr (!std::is_same>, + std::remove_cv_t>::value) { + static_assert(std::is_polymorphic::value, + "source type must be polymorphic"); + static_assert(std::is_polymorphic>::value, + "target type must be polymorphic"); + } + static_assert( + std::is_convertible>*, + std::remove_cv_t*>::value, + "target type not derived from source type"); + + absl::base_internal::ValidateDownCast*>( + std::addressof(f)); + + return static_cast(f); +} + ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/base/config.h b/absl/base/config.h index 1a9bc591d..6b2c81cad 100644 --- a/absl/base/config.h +++ b/absl/base/config.h @@ -117,8 +117,8 @@ // // LTS releases can be obtained from // https://github.com/abseil/abseil-cpp/releases. -#define ABSL_LTS_RELEASE_VERSION 20250512 -#define ABSL_LTS_RELEASE_PATCH_LEVEL 1 +#define ABSL_LTS_RELEASE_VERSION 20260526 +#define ABSL_LTS_RELEASE_PATCH_LEVEL 0 // Helper macro to convert a CPP variable to a string literal. #define ABSL_INTERNAL_DO_TOKEN_STR(x) #x @@ -237,6 +237,8 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || #error ABSL_HAVE_TLS cannot be directly set #elif (defined(__linux__)) && (defined(__clang__) || defined(_GLIBCXX_HAVE_TLS)) #define ABSL_HAVE_TLS 1 +#elif defined(__INTEL_LLVM_COMPILER) +#define ABSL_HAVE_TLS 1 #endif // ABSL_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE @@ -358,10 +360,10 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || // Darwin (macOS and iOS) __APPLE__ // Akaros (http://akaros.org) __ros__ // Windows _WIN32 -// NaCL __native_client__ // AsmJS __asmjs__ // WebAssembly (Emscripten) __EMSCRIPTEN__ // Fuchsia __Fuchsia__ +// WebAssembly (WASI) _WASI_EMULATED_MMAN (implies __wasi__) // // Note that since Android defines both __ANDROID__ and __linux__, one // may probe for either Linux or Android by simply testing for __linux__. @@ -372,12 +374,13 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || // 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(__EMSCRIPTEN__) || defined(__Fuchsia__) || \ - defined(__sun) || defined(__myriad2__) || defined(__HAIKU__) || \ - defined(__OpenBSD__) || defined(__NetBSD__) || defined(__QNX__) || \ - defined(__VXWORKS__) || defined(__hexagon__) || defined(__XTENSA__) +#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \ + defined(_AIX) || defined(__ros__) || defined(__asmjs__) || \ + defined(__EMSCRIPTEN__) || defined(__Fuchsia__) || defined(__sun) || \ + defined(__myriad2__) || defined(__HAIKU__) || defined(__OpenBSD__) || \ + defined(__NetBSD__) || defined(__QNX__) || defined(__VXWORKS__) || \ + defined(__hexagon__) || defined(__XTENSA__) || \ + defined(_WASI_EMULATED_MMAN) #define ABSL_HAVE_MMAP 1 #endif @@ -453,8 +456,6 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || // WASI doesn't support signals #elif defined(__Fuchsia__) // Signals don't exist on fuchsia. -#elif defined(__native_client__) -// Signals don't exist on hexagon/QuRT #elif defined(__hexagon__) #else // other standard libraries @@ -525,18 +526,45 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || #define ABSL_USES_STD_ANY 1 #define ABSL_HAVE_STD_OPTIONAL 1 #define ABSL_USES_STD_OPTIONAL 1 +#define ABSL_HAVE_STD_STRING_VIEW 1 +#define ABSL_USES_STD_STRING_VIEW 1 #define ABSL_HAVE_STD_VARIANT 1 #define ABSL_USES_STD_VARIANT 1 -// ABSL_HAVE_STD_STRING_VIEW +// ABSL_HAVE_STD_SOURCE_LOCATION // -// Deprecated: always defined to 1. -// std::string_view was added in C++17, which means all versions of C++ -// supported by Abseil have it. -#ifdef ABSL_HAVE_STD_STRING_VIEW -#error "ABSL_HAVE_STD_STRING_VIEW cannot be directly set." +// Checks whether C++20 std::source_location is available. +#ifdef ABSL_HAVE_STD_SOURCE_LOCATION +#error "ABSL_HAVE_STD_SOURCE_LOCATION cannot be directly set." +#elif (defined(__cpp_lib_source_location) && \ + __cpp_lib_source_location >= 201907L) || \ + (defined(ABSL_INTERNAL_CPLUSPLUS_LANG) && \ + ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L) +#ifdef __has_include +#if __has_include() +#define ABSL_HAVE_STD_SOURCE_LOCATION 1 +#endif #else -#define ABSL_HAVE_STD_STRING_VIEW 1 +// No __has_include support, so just assume C++ language version is correct. +#define ABSL_HAVE_STD_SOURCE_LOCATION 1 +#endif +#endif + +// ABSL_USES_STD_SOURCE_LOCATION +// +// Indicates whether absl::SourceLocation is an alias for std::source_location. +#if !defined(ABSL_OPTION_USE_STD_SOURCE_LOCATION) +#error options.h is misconfigured. +#elif ABSL_OPTION_USE_STD_SOURCE_LOCATION == 0 || \ + (ABSL_OPTION_USE_STD_SOURCE_LOCATION == 2 && \ + !defined(ABSL_HAVE_STD_SOURCE_LOCATION)) +#undef ABSL_USES_STD_SOURCE_LOCATION +#elif ABSL_OPTION_USE_STD_SOURCE_LOCATION == 1 || \ + (ABSL_OPTION_USE_STD_SOURCE_LOCATION == 2 && \ + defined(ABSL_HAVE_STD_SOURCE_LOCATION)) +#define ABSL_USES_STD_SOURCE_LOCATION 1 +#else +#error options.h is misconfigured. #endif // ABSL_HAVE_STD_ORDERING @@ -555,20 +583,6 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || #define ABSL_HAVE_STD_ORDERING 1 #endif -// ABSL_USES_STD_STRING_VIEW -// -// Indicates whether absl::string_view is an alias for std::string_view. -#if !defined(ABSL_OPTION_USE_STD_STRING_VIEW) -#error options.h is misconfigured. -#elif ABSL_OPTION_USE_STD_STRING_VIEW == 0 -#undef ABSL_USES_STD_STRING_VIEW -#elif ABSL_OPTION_USE_STD_STRING_VIEW == 1 || \ - ABSL_OPTION_USE_STD_STRING_VIEW == 2 -#define ABSL_USES_STD_STRING_VIEW 1 -#else -#error options.h is misconfigured. -#endif - // ABSL_USES_STD_ORDERING // // Indicates whether absl::{partial,weak,strong}_ordering are aliases for the @@ -718,7 +732,7 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || // Clang standalone LeakSanitizer (-fsanitize=leak) #elif ABSL_HAVE_FEATURE(leak_sanitizer) #define ABSL_HAVE_LEAK_SANITIZER 1 -#elif defined(ABSL_HAVE_ADDRESS_SANITIZER) +#elif defined(ABSL_HAVE_ADDRESS_SANITIZER) && !defined(_WIN32) // GCC or Clang using the LeakSanitizer integrated into AddressSanitizer. #define ABSL_HAVE_LEAK_SANITIZER 1 #endif @@ -754,7 +768,7 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || #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 +#undef ABSL_INTERNAL_HAS_CXA_DEMANGLE #elif defined(__GNUC__) #define ABSL_INTERNAL_HAS_CXA_DEMANGLE 1 #elif defined(__clang__) && !defined(_MSC_VER) @@ -821,6 +835,14 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || #define ABSL_INTERNAL_HAVE_ARM_NEON 1 #endif +#if ABSL_HAVE_BUILTIN(__builtin_LINE) && ABSL_HAVE_BUILTIN(__builtin_FILE) +#define ABSL_INTERNAL_HAVE_BUILTIN_LINE_FILE 1 +#elif defined(__GNUC__) && !defined(__clang__) && 5 <= __GNUC__ && __GNUC__ < 10 +#define ABSL_INTERNAL_HAVE_BUILTIN_LINE_FILE 1 +#elif defined(_MSC_VER) && _MSC_VER >= 1926 +#define ABSL_INTERNAL_HAVE_BUILTIN_LINE_FILE 1 +#endif + // ABSL_HAVE_CONSTANT_EVALUATED is used for compile-time detection of // constant evaluation support through `absl::is_constant_evaluated`. #ifdef ABSL_HAVE_CONSTANT_EVALUATED @@ -860,16 +882,16 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || #endif #ifdef __EMSCRIPTEN__ #include -#ifdef __EMSCRIPTEN_major__ -#if __EMSCRIPTEN_minor__ >= 1000 -#error __EMSCRIPTEN_minor__ is too big to fit in ABSL_INTERNAL_EMSCRIPTEN_VERSION +#ifdef __EMSCRIPTEN_MAJOR__ +#if __EMSCRIPTEN_MINOR__ >= 1000 +#error __EMSCRIPTEN_MINOR__ is too big to fit in ABSL_INTERNAL_EMSCRIPTEN_VERSION #endif -#if __EMSCRIPTEN_tiny__ >= 1000 -#error __EMSCRIPTEN_tiny__ is too big to fit in ABSL_INTERNAL_EMSCRIPTEN_VERSION +#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 + \ - (__EMSCRIPTEN_tiny__)) + ((__EMSCRIPTEN_MAJOR__) * 1000000 + (__EMSCRIPTEN_MINOR__) * 1000 + \ + (__EMSCRIPTEN_TINY__)) #endif #endif diff --git a/absl/base/fast_type_id.h b/absl/base/fast_type_id.h index ff2502767..6e4c573af 100644 --- a/absl/base/fast_type_id.h +++ b/absl/base/fast_type_id.h @@ -16,6 +16,8 @@ #ifndef ABSL_BASE_FAST_TYPE_ID_H_ #define ABSL_BASE_FAST_TYPE_ID_H_ +#include + #include "absl/base/config.h" namespace absl { @@ -29,14 +31,40 @@ struct FastTypeTag { } // namespace base_internal // The type returned by `absl::FastTypeId()`. -using FastTypeIdType = const void*; +class FastTypeIdType final { + public: + // Creates a value that does not correspond to any type. This value is + // distinct from any value returned by `FastTypeId()`. + constexpr FastTypeIdType() = default; + + template + friend H AbslHashValue(H h, FastTypeIdType x) { + return H::combine(std::move(h), x.ptr_); + } + + friend constexpr bool operator==(FastTypeIdType a, FastTypeIdType b) { + return a.ptr_ == b.ptr_; + } + friend constexpr bool operator!=(FastTypeIdType a, FastTypeIdType b) { + return a.ptr_ != b.ptr_; + } + + private: + // `FastTypeId()` is the generator method for FastTypeIdType values. + template + friend constexpr FastTypeIdType FastTypeId(); + + explicit constexpr FastTypeIdType(const void* ptr) : ptr_(ptr) {} + + const void* ptr_ = nullptr; +}; // `absl::FastTypeId()` evaluates at compile-time to a unique id for the // passed-in type. These are meant to be good match for keys into maps or // straight up comparisons. template constexpr FastTypeIdType FastTypeId() { - return &base_internal::FastTypeTag::kDummyVar; + return FastTypeIdType(&base_internal::FastTypeTag::kDummyVar); } ABSL_NAMESPACE_END diff --git a/absl/base/internal/dynamic_annotations.h b/absl/base/internal/dynamic_annotations.h index b23c5ec1c..537a2fe67 100644 --- a/absl/base/internal/dynamic_annotations.h +++ b/absl/base/internal/dynamic_annotations.h @@ -89,7 +89,7 @@ #endif // Memory annotations are also made available to LLVM's Memory Sanitizer -#if defined(ABSL_HAVE_MEMORY_SANITIZER) && !defined(__native_client__) +#if defined(ABSL_HAVE_MEMORY_SANITIZER) #define ABSL_INTERNAL_MEMORY_ANNOTATIONS_ENABLED 1 #endif diff --git a/absl/base/internal/hardening.h b/absl/base/internal/hardening.h new file mode 100644 index 000000000..31b25d9bd --- /dev/null +++ b/absl/base/internal/hardening.h @@ -0,0 +1,136 @@ +// +// Copyright 2026 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: hardening.h +// ----------------------------------------------------------------------------- +// +// This header file defines macros and functions for performing Abseil +// hardening checks and aborts. + +#ifndef ABSL_BASE_INTERNAL_HARDENING_H_ +#define ABSL_BASE_INTERNAL_HARDENING_H_ + +#include "absl/base/config.h" +#include "absl/base/macros.h" +#include "absl/base/options.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +namespace base_internal { + +// `HardeningAssert` performs runtime checks when Abseil Hardening is enabled, +// even if `NDEBUG` is defined. +// +// When `NDEBUG` is not defined, `HardeningAssert`'s behavior is identical to +// `ABSL_ASSERT`. +// +// Prefer a more specific assertion function over this more general one, +// as assertion functions which perform the comparison themselves +// can have the cost of the comparison attributed to them. +constexpr void HardeningAssert(bool cond) { + ABSL_ASSERT(cond); +#if (ABSL_OPTION_HARDENED == 1 || ABSL_OPTION_HARDENED == 2) && defined(NDEBUG) + if (ABSL_PREDICT_FALSE(!cond)) { + base_internal::HardeningAbort(); + } +#endif +} + +// `HardeningAssertSlow` is used to perform runtime checks which are too +// computationally expensive to enable widely by default. +// +// When `NDEBUG` is not defined, `HardeningAssertSlow`'s behavior is identical +// to `ABSL_ASSERT`. +constexpr void HardeningAssertSlow(bool cond) { + ABSL_ASSERT(cond); +#if (ABSL_OPTION_HARDENED == 1) && defined(NDEBUG) + if (ABSL_PREDICT_FALSE(!cond)) { + base_internal::HardeningAbort(); + } +#endif +} + +template +constexpr void HardeningAssertGT(T val1, T val2) { + ABSL_ASSERT(val1 > val2); +#if (ABSL_OPTION_HARDENED == 1 || ABSL_OPTION_HARDENED == 2) && defined(NDEBUG) + if (!ABSL_PREDICT_TRUE(val1 > val2)) { + base_internal::HardeningAbort(); + } +#endif +} + +template +constexpr void HardeningAssertGE(T val1, T val2) { + ABSL_ASSERT(val1 >= val2); +#if (ABSL_OPTION_HARDENED == 1 || ABSL_OPTION_HARDENED == 2) && defined(NDEBUG) + if (!ABSL_PREDICT_TRUE(val1 >= val2)) { + base_internal::HardeningAbort(); + } +#endif +} + +template +constexpr void HardeningAssertLT(T val1, T val2) { + ABSL_ASSERT(val1 < val2); +#if (ABSL_OPTION_HARDENED == 1 || ABSL_OPTION_HARDENED == 2) && defined(NDEBUG) + if (!ABSL_PREDICT_TRUE(val1 < val2)) { + base_internal::HardeningAbort(); + } +#endif +} + +template +constexpr void HardeningAssertLE(T val1, T val2) { + ABSL_ASSERT(val1 <= val2); +#if (ABSL_OPTION_HARDENED == 1 || ABSL_OPTION_HARDENED == 2) && defined(NDEBUG) + if (!ABSL_PREDICT_TRUE(val1 <= val2)) { + base_internal::HardeningAbort(); + } +#endif +} + +constexpr void HardeningAssertInBounds(size_t index, size_t size) { + HardeningAssertLT(index, size); +} + +template +constexpr void HardeningAssertNonEmpty(const T& container) { + ABSL_ASSERT(!container.empty()); +#if (ABSL_OPTION_HARDENED == 1 || ABSL_OPTION_HARDENED == 2) && defined(NDEBUG) + if (ABSL_PREDICT_FALSE(container.empty())) { + base_internal::HardeningAbort(); + } +#endif +} + +template +constexpr void HardeningAssertNonNull(T ptr) { + ABSL_ASSERT(ptr != nullptr); +#if (ABSL_OPTION_HARDENED == 1 || ABSL_OPTION_HARDENED == 2) && defined(NDEBUG) + if (ABSL_PREDICT_FALSE(ptr == nullptr)) { + base_internal::HardeningAbort(); + } +#endif +} + +} // namespace base_internal + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_BASE_INTERNAL_HARDENING_H_ diff --git a/absl/base/internal/iterator_traits.h b/absl/base/internal/iterator_traits.h index 472c43688..51132b573 100644 --- a/absl/base/internal/iterator_traits.h +++ b/absl/base/internal/iterator_traits.h @@ -37,7 +37,7 @@ struct IteratorCategory {}; template struct IteratorCategory< Iterator, - absl::void_t::iterator_category>> { + std::void_t::iterator_category>> { using type = typename std::iterator_traits::iterator_category; }; @@ -47,7 +47,7 @@ struct IteratorConceptImpl : IteratorCategory {}; template struct IteratorConceptImpl< Iterator, - absl::void_t::iterator_concept>> { + std::void_t::iterator_concept>> { using type = typename std::iterator_traits::iterator_concept; }; @@ -60,6 +60,10 @@ template using IsAtLeastIterator = std::is_convertible, IteratorTag>; +template +using IsAtLeastInputIterator = + IsAtLeastIterator; + template using IsAtLeastForwardIterator = IsAtLeastIterator; diff --git a/absl/base/internal/low_level_alloc.cc b/absl/base/internal/low_level_alloc.cc index 158b60982..3887a92f4 100644 --- a/absl/base/internal/low_level_alloc.cc +++ b/absl/base/internal/low_level_alloc.cc @@ -19,6 +19,9 @@ #include "absl/base/internal/low_level_alloc.h" +#include + +#include #include #include "absl/base/call_once.h" @@ -44,6 +47,12 @@ #ifdef __linux__ #include +#ifndef PR_SET_VMA +#define PR_SET_VMA 0x53564d41 +#endif +#ifndef PR_SET_VMA_ANON_NAME +#define PR_SET_VMA_ANON_NAME 0 +#endif #endif #include @@ -219,6 +228,32 @@ struct LowLevelAlloc::Arena { uint32_t random ABSL_GUARDED_BY(mu); }; +// --------------------------------------------------------------- +// An async-signal-safe arena for LowLevelAlloc +static std::atomic g_sig_safe_arena; + +base_internal::LowLevelAlloc::Arena *SigSafeArena() { + return g_sig_safe_arena.load(std::memory_order_acquire); +} + +void InitSigSafeArena() { + if (SigSafeArena() == nullptr) { + uint32_t flags = 0; +#ifndef ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING + flags |= base_internal::LowLevelAlloc::kAsyncSignalSafe; +#endif + base_internal::LowLevelAlloc::Arena *new_arena = + base_internal::LowLevelAlloc::NewArena(flags); + base_internal::LowLevelAlloc::Arena *old_value = nullptr; + if (!g_sig_safe_arena.compare_exchange_strong(old_value, new_arena, + std::memory_order_release, + std::memory_order_relaxed)) { + // We lost a race to allocate an arena; deallocate. + base_internal::LowLevelAlloc::DeleteArena(new_arena); + } + } +} + namespace { // Static storage space for the lazily-constructed, default global arena // instances. We require this space because the whole point of LowLevelAlloc @@ -289,11 +324,11 @@ class ABSL_SCOPED_LOCKABLE ArenaLock { mask_valid_ = pthread_sigmask(SIG_BLOCK, &all, &mask_) == 0; } #endif - arena_->mu.Lock(); + arena_->mu.lock(); } ~ArenaLock() { ABSL_RAW_CHECK(left_, "haven't left Arena region"); } void Leave() ABSL_UNLOCK_FUNCTION() { - arena_->mu.Unlock(); + arena_->mu.unlock(); #ifndef ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING if (mask_valid_) { const int err = pthread_sigmask(SIG_SETMASK, &mask_, nullptr); @@ -544,7 +579,7 @@ static void *DoAllocWithArena(size_t request, LowLevelAlloc::Arena *arena) { } // we unlock before mmap() both because mmap() may call a callback hook, // and because it may be slow. - arena->mu.Unlock(); + arena->mu.unlock(); // mmap generous 64K chunks to decrease // the chances/impact of fragmentation: size_t new_pages_size = RoundUp(req_rnd, arena->pagesize * 16); @@ -583,7 +618,7 @@ static void *DoAllocWithArena(size_t request, LowLevelAlloc::Arena *arena) { #endif #endif // __linux__ #endif // _WIN32 - arena->mu.Lock(); + arena->mu.lock(); s = reinterpret_cast(new_pages); s->header.size = new_pages_size; // Pretend the block is allocated; call AddToFreelist() to free it. diff --git a/absl/base/internal/low_level_alloc.h b/absl/base/internal/low_level_alloc.h index c2f1f25d8..23218dd5a 100644 --- a/absl/base/internal/low_level_alloc.h +++ b/absl/base/internal/low_level_alloc.h @@ -120,6 +120,12 @@ class LowLevelAlloc { LowLevelAlloc(); // no instances }; +// Returns a global async-signal-safe arena for LowLevelAlloc. +LowLevelAlloc::Arena *SigSafeArena(); + +// Ensures the global async-signal-safe arena for LowLevelAlloc is initialized. +void InitSigSafeArena(); + } // namespace base_internal ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/base/internal/low_level_scheduling.h b/absl/base/internal/low_level_scheduling.h index 9baccc065..bba27ef75 100644 --- a/absl/base/internal/low_level_scheduling.h +++ b/absl/base/internal/low_level_scheduling.h @@ -18,8 +18,11 @@ #ifndef ABSL_BASE_INTERNAL_LOW_LEVEL_SCHEDULING_H_ #define ABSL_BASE_INTERNAL_LOW_LEVEL_SCHEDULING_H_ +#include + #include "absl/base/internal/raw_logging.h" #include "absl/base/internal/scheduling_mode.h" +#include "absl/base/internal/thread_identity.h" #include "absl/base/macros.h" // The following two declarations exist so SchedulingGuard may friend them with @@ -64,7 +67,6 @@ class SchedulingGuard { SchedulingGuard(const SchedulingGuard&) = delete; SchedulingGuard& operator=(const SchedulingGuard&) = delete; - private: // Disable cooperative rescheduling of the calling thread. It may still // initiate scheduling operations (e.g. wake-ups), however, it may not itself // reschedule. Nestable. The returned result is opaque, clients should not @@ -96,13 +98,6 @@ class SchedulingGuard { private: int scheduling_disabled_depth_; }; - - // Access to SchedulingGuard is explicitly permitted. - friend class absl::CondVar; - friend class absl::Mutex; - friend class SchedulingHelper; - friend class SpinLock; - friend int absl::synchronization_internal::MutexDelay(int32_t c, int mode); }; //------------------------------------------------------------------------------ @@ -110,21 +105,88 @@ class SchedulingGuard { //------------------------------------------------------------------------------ inline bool SchedulingGuard::ReschedulingIsAllowed() { - return false; + ThreadIdentity* identity = CurrentThreadIdentityIfPresent(); + if (identity != nullptr) { + ThreadIdentity::SchedulerState* state = &identity->scheduler_state; + // For a thread to be eligible for re-scheduling it must have a bound + // schedulable (otherwise it's not cooperative) and not be within a + // SchedulerGuard region. + return state->bound_schedulable.load(std::memory_order_relaxed) != + nullptr && + state->scheduling_disabled_depth.load(std::memory_order_relaxed) == + 0; + } else { + // Cooperative threads always have a ThreadIdentity. + return false; + } } +// We don't use [[nodiscard]] here as some clients (e.g. +// FinishPotentiallyBlockingRegion()) cannot yet properly consume it. inline bool SchedulingGuard::DisableRescheduling() { - return false; + ThreadIdentity* identity; + identity = CurrentThreadIdentityIfPresent(); + if (identity != nullptr) { + // The depth is accessed concurrently from other threads, so it must be + // atomic, but it's only mutated from this thread, so we don't need an + // atomic increment. + int old_val = identity->scheduler_state.scheduling_disabled_depth.load( + std::memory_order_relaxed); + identity->scheduler_state.scheduling_disabled_depth.store( + old_val + 1, std::memory_order_relaxed); + return true; + } else { + return false; + } +} + +inline void SchedulingGuard::EnableRescheduling(bool disable_result) { + if (!disable_result) { + // There was no installed thread identity at the time that scheduling was + // disabled, so we have nothing to do. This is an implementation detail + // that may change in the future, clients may not depend on it. + // EnableRescheduling() must always be called. + return; + } + + ThreadIdentity* identity; + // A thread identity exists, see above + identity = CurrentThreadIdentityIfPresent(); + // The depth is accessed concurrently from other threads, so it must be + // atomic, but it's only mutated from this thread, so we don't need an atomic + // decrement. + int old_val = identity->scheduler_state.scheduling_disabled_depth.load( + std::memory_order_relaxed); + identity->scheduler_state.scheduling_disabled_depth.store( + old_val - 1, std::memory_order_relaxed); } -inline void SchedulingGuard::EnableRescheduling(bool /* disable_result */) { - return; +inline SchedulingGuard::ScopedEnable::ScopedEnable() { + ThreadIdentity* identity; + identity = CurrentThreadIdentityIfPresent(); + if (identity != nullptr) { + scheduling_disabled_depth_ = + identity->scheduler_state.scheduling_disabled_depth.load( + std::memory_order_relaxed); + if (scheduling_disabled_depth_ != 0) { + // The store below does not need to be compare_exchange because + // the value is never modified concurrently (only accessed). + identity->scheduler_state.scheduling_disabled_depth.store( + 0, std::memory_order_relaxed); + } + } else { + scheduling_disabled_depth_ = 0; + } } -inline SchedulingGuard::ScopedEnable::ScopedEnable() - : scheduling_disabled_depth_(0) {} inline SchedulingGuard::ScopedEnable::~ScopedEnable() { - ABSL_RAW_CHECK(scheduling_disabled_depth_ == 0, "disable unused warning"); + if (scheduling_disabled_depth_ == 0) { + return; + } + ThreadIdentity* identity = CurrentThreadIdentityIfPresent(); + // itentity is guaranteed to exist, see the constructor above. + identity->scheduler_state.scheduling_disabled_depth.store( + scheduling_disabled_depth_, std::memory_order_relaxed); } } // namespace base_internal diff --git a/absl/base/internal/nullability_traits.h b/absl/base/internal/nullability_traits.h new file mode 100644 index 000000000..790ec9096 --- /dev/null +++ b/absl/base/internal/nullability_traits.h @@ -0,0 +1,71 @@ +// Copyright 2025 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_BASE_INTERNAL_NULLABILITY_TRAITS_H_ +#define ABSL_BASE_INTERNAL_NULLABILITY_TRAITS_H_ + +#include + +#include "absl/base/config.h" +#include "absl/base/nullability.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +// `value` is true if the type `T` is compatible with nullability annotations +// (is a raw pointer, a smart pointer, or marked with +// ABSL_NULLABILITY_COMPATIBLE). Prefer to use the higher-level +// `AddNonnullIfCompatible` if that is sufficient. +// +// NOTE: This should not be used to detect if the compiler is Clang (since +// Clang is the only compiler that supports nullability annotations). +#if defined(__clang__) && !defined(__OBJC__) && \ + ABSL_HAVE_FEATURE(nullability_on_classes) +template +struct IsNullabilityCompatibleType { + constexpr static bool value = false; +}; + +template +struct IsNullabilityCompatibleType> { + constexpr static bool value = true; +}; +#else +// False when absl_nullable is a no-op (for non-Clang compilers or Objective-C.) +template +struct IsNullabilityCompatibleType { + constexpr static bool value = false; +}; +#endif + +// A trait to add `absl_nonnull` to a type if it is compatible with nullability +// annotations. +template ::value> +struct AddNonnullIfCompatible; + +template +struct AddNonnullIfCompatible { + using type = T; +}; +template +struct AddNonnullIfCompatible { + using type = absl_nonnull T; +}; + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_BASE_INTERNAL_NULLABILITY_TRAITS_H_ diff --git a/absl/base/internal/poison.cc b/absl/base/internal/poison.cc index b33d4c2d3..c639c9666 100644 --- a/absl/base/internal/poison.cc +++ b/absl/base/internal/poison.cc @@ -57,19 +57,20 @@ size_t GetPageSize() { void* InitializePoisonedPointerInternal() { const size_t block_size = GetPageSize(); + void* data = nullptr; #if defined(ABSL_HAVE_ADDRESS_SANITIZER) - void* data = malloc(block_size); + data = malloc(block_size); ASAN_POISON_MEMORY_REGION(data, block_size); #elif defined(ABSL_HAVE_MEMORY_SANITIZER) - void* data = malloc(block_size); + data = malloc(block_size); __msan_poison(data, block_size); #elif defined(ABSL_HAVE_MMAP) - void* data = DirectMmap(nullptr, block_size, PROT_NONE, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + data = DirectMmap(nullptr, block_size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, + -1, 0); if (data == MAP_FAILED) return GetBadPointerInternal(); #elif defined(_WIN32) - void* data = VirtualAlloc(nullptr, block_size, MEM_RESERVE | MEM_COMMIT, - PAGE_NOACCESS); + data = VirtualAlloc(nullptr, block_size, MEM_RESERVE | MEM_COMMIT, + PAGE_NOACCESS); if (data == nullptr) return GetBadPointerInternal(); #else return GetBadPointerInternal(); diff --git a/absl/base/internal/raw_logging.cc b/absl/base/internal/raw_logging.cc index 35a08f0ac..8537f3ec5 100644 --- a/absl/base/internal/raw_logging.cc +++ b/absl/base/internal/raw_logging.cc @@ -41,9 +41,8 @@ // // 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(__hexagon__) || defined(__Fuchsia__) || \ - defined(__native_client__) || defined(__OpenBSD__) || \ +#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \ + defined(__hexagon__) || defined(__Fuchsia__) || defined(__OpenBSD__) || \ defined(__EMSCRIPTEN__) || defined(__ASYLO__) #include @@ -158,7 +157,7 @@ void RawLogVA(absl::LogSeverity severity, const char* file, int line, #endif #ifdef ABSL_MIN_LOG_LEVEL - if (severity < static_cast(ABSL_MIN_LOG_LEVEL) && + if (severity < static_cast(ABSL_MIN_LOG_LEVEL) && severity < absl::LogSeverity::kFatal) { enabled = false; } diff --git a/absl/base/internal/spinlock.cc b/absl/base/internal/spinlock.cc index 430f775bd..41d2b482f 100644 --- a/absl/base/internal/spinlock.cc +++ b/absl/base/internal/spinlock.cc @@ -16,15 +16,18 @@ #include #include +#include #include #include "absl/base/attributes.h" +#include "absl/base/call_once.h" #include "absl/base/config.h" #include "absl/base/internal/atomic_hook.h" #include "absl/base/internal/cycleclock.h" +#include "absl/base/internal/scheduling_mode.h" #include "absl/base/internal/spinlock_wait.h" #include "absl/base/internal/sysinfo.h" /* For NumCPUs() */ -#include "absl/base/call_once.h" +#include "absl/base/internal/tsan_mutex_interface.h" // Description of lock-word: // 31..00: [............................3][2][1][0] @@ -58,7 +61,7 @@ namespace absl { ABSL_NAMESPACE_BEGIN namespace base_internal { -ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES static base_internal::AtomicHook submit_profile_data; @@ -67,25 +70,24 @@ void RegisterSpinLockProfiler(void (*fn)(const void *contendedlock, submit_profile_data.Store(fn); } -// Uncommon constructors. -SpinLock::SpinLock(base_internal::SchedulingMode mode) - : lockword_(IsCooperative(mode) ? kSpinLockCooperative : 0) { - ABSL_TSAN_MUTEX_CREATE(this, __tsan_mutex_not_static); -} - // Monitor the lock to see if its value changes within some time period -// (adaptive_spin_count loop iterations). The last value read from the lock +// (adaptive_spin_count_ loop iterations). The last value read from the lock // is returned from the method. +ABSL_CONST_INIT std::atomic SpinLock::adaptive_spin_count_{0}; uint32_t SpinLock::SpinLoop() { // We are already in the slow path of SpinLock, initialize the // adaptive_spin_count here. - ABSL_CONST_INIT static absl::once_flag init_adaptive_spin_count; - ABSL_CONST_INIT static int adaptive_spin_count = 0; - base_internal::LowLevelCallOnce(&init_adaptive_spin_count, []() { - adaptive_spin_count = base_internal::NumCPUs() > 1 ? 1000 : 1; - }); - - int c = adaptive_spin_count; + if (adaptive_spin_count_.load(std::memory_order_relaxed) == 0) { + int current_spin_count = 0; + int new_spin_count = NumCPUs() > 1 ? 1000 : 1; + // If this fails, the value will remain unchanged. We may not spin for the + // intended duration, but that is still safe. We will try again on the next + // call to SpinLoop. + adaptive_spin_count_.compare_exchange_weak( + current_spin_count, new_spin_count, std::memory_order_relaxed, + std::memory_order_relaxed); + } + int c = adaptive_spin_count_.load(std::memory_order_relaxed); uint32_t lock_value; do { lock_value = lockword_.load(std::memory_order_relaxed); @@ -100,11 +102,11 @@ void SpinLock::SlowLock() { return; } - base_internal::SchedulingMode scheduling_mode; + SchedulingMode scheduling_mode; if ((lock_value & kSpinLockCooperative) != 0) { - scheduling_mode = base_internal::SCHEDULE_COOPERATIVE_AND_KERNEL; + scheduling_mode = SCHEDULE_COOPERATIVE_AND_KERNEL; } else { - scheduling_mode = base_internal::SCHEDULE_KERNEL_ONLY; + scheduling_mode = SCHEDULE_KERNEL_ONLY; } // The lock was not obtained initially, so this thread needs to wait for @@ -134,7 +136,7 @@ void SpinLock::SlowLock() { // new lock state will be the number of cycles this thread waited if // this thread obtains the lock. lock_value = TryLockInternal(lock_value, wait_cycles); - continue; // Skip the delay at the end of the loop. + continue; // Skip the delay at the end of the loop. } else if ((lock_value & kWaitTimeMask) == 0) { // The lock is still held, without a waiter being marked, but something // else about the lock word changed, causing our CAS to fail. For @@ -150,8 +152,8 @@ void SpinLock::SlowLock() { // synchronization there to avoid false positives. ABSL_TSAN_MUTEX_PRE_DIVERT(this, 0); // Wait for an OS specific delay. - base_internal::SpinLockDelay(&lockword_, lock_value, ++lock_wait_call_count, - scheduling_mode); + SpinLockDelay(&lockword_, lock_value, ++lock_wait_call_count, + scheduling_mode); ABSL_TSAN_MUTEX_POST_DIVERT(this, 0); // Spin again after returning from the wait routine to give this thread // some chance of obtaining the lock. @@ -162,8 +164,8 @@ void SpinLock::SlowLock() { } void SpinLock::SlowUnlock(uint32_t lock_value) { - base_internal::SpinLockWake(&lockword_, - false); // wake waiter if necessary + SpinLockWake(&lockword_, + false); // wake waiter if necessary // If our acquisition was contended, collect contentionz profile info. We // reserve a unitary wait time to represent that a waiter exists without our diff --git a/absl/base/internal/spinlock.h b/absl/base/internal/spinlock.h index 2a1089697..d535093ef 100644 --- a/absl/base/internal/spinlock.h +++ b/absl/base/internal/spinlock.h @@ -19,7 +19,7 @@ // - for use by Abseil internal code that Mutex itself depends on // - for async signal safety (see below) -// SpinLock with a base_internal::SchedulingMode::SCHEDULE_KERNEL_ONLY is async +// SpinLock with a 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. @@ -31,20 +31,24 @@ #include #include +#include +#include #include "absl/base/attributes.h" +#include "absl/base/config.h" #include "absl/base/const_init.h" -#include "absl/base/dynamic_annotations.h" #include "absl/base/internal/low_level_scheduling.h" #include "absl/base/internal/raw_logging.h" #include "absl/base/internal/scheduling_mode.h" #include "absl/base/internal/tsan_mutex_interface.h" +#include "absl/base/macros.h" #include "absl/base/thread_annotations.h" namespace tcmalloc { namespace tcmalloc_internal { class AllocationGuardSpinLockHolder; +class Static; } // namespace tcmalloc_internal } // namespace tcmalloc @@ -55,17 +59,31 @@ namespace base_internal { class ABSL_LOCKABLE ABSL_ATTRIBUTE_WARN_UNUSED SpinLock { public: - SpinLock() : lockword_(kSpinLockCooperative) { - ABSL_TSAN_MUTEX_CREATE(this, __tsan_mutex_not_static); - } + constexpr SpinLock() : lockword_(kSpinLockCooperative) { RegisterWithTsan(); } // Constructors that allow non-cooperative spinlocks to be created for use // inside thread schedulers. Normal clients should not use these. - explicit SpinLock(base_internal::SchedulingMode mode); + constexpr explicit SpinLock(SchedulingMode mode) + : lockword_(IsCooperative(mode) ? kSpinLockCooperative : 0) { + RegisterWithTsan(); + } + +#if ABSL_HAVE_ATTRIBUTE(enable_if) && !defined(_WIN32) + // Constructor to inline users of the default scheduling mode. + // + // This only needs to exists for inliner runs, but doesn't work correctly in + // clang+windows builds, likely due to mangling differences. + ABSL_DEPRECATE_AND_INLINE() + constexpr explicit SpinLock(SchedulingMode mode) + __attribute__((enable_if(mode == SCHEDULE_COOPERATIVE_AND_KERNEL, + "Cooperative use default constructor"))) + : SpinLock() {} +#endif // Constructor for global SpinLock instances. See absl/base/const_init.h. - constexpr SpinLock(absl::ConstInitType, base_internal::SchedulingMode mode) - : lockword_(IsCooperative(mode) ? kSpinLockCooperative : 0) {} + ABSL_DEPRECATE_AND_INLINE() + constexpr SpinLock(absl::ConstInitType, SchedulingMode mode) + : SpinLock(mode) {} // For global SpinLock instances prefer trivial destructor when possible. // Default but non-trivial destructor in some build configurations causes an @@ -77,7 +95,7 @@ class ABSL_LOCKABLE ABSL_ATTRIBUTE_WARN_UNUSED SpinLock { #endif // Acquire this SpinLock. - inline void Lock() ABSL_EXCLUSIVE_LOCK_FUNCTION() { + inline void lock() ABSL_EXCLUSIVE_LOCK_FUNCTION() { ABSL_TSAN_MUTEX_PRE_LOCK(this, 0); if (!TryLockImpl()) { SlowLock(); @@ -85,11 +103,14 @@ class ABSL_LOCKABLE ABSL_ATTRIBUTE_WARN_UNUSED SpinLock { ABSL_TSAN_MUTEX_POST_LOCK(this, 0, 0); } + ABSL_DEPRECATE_AND_INLINE() + inline void Lock() ABSL_EXCLUSIVE_LOCK_FUNCTION() { return lock(); } + // Try to acquire this SpinLock without blocking and return true if the // acquisition was successful. If the lock was not acquired, false is - // returned. If this SpinLock is free at the time of the call, TryLock - // will return true with high probability. - [[nodiscard]] inline bool TryLock() ABSL_EXCLUSIVE_TRYLOCK_FUNCTION(true) { + // returned. If this SpinLock is free at the time of the call, try_lock will + // return true with high probability. + [[nodiscard]] inline bool try_lock() ABSL_EXCLUSIVE_TRYLOCK_FUNCTION(true) { ABSL_TSAN_MUTEX_PRE_LOCK(this, __tsan_mutex_try_lock); bool res = TryLockImpl(); ABSL_TSAN_MUTEX_POST_LOCK( @@ -98,15 +119,20 @@ class ABSL_LOCKABLE ABSL_ATTRIBUTE_WARN_UNUSED SpinLock { return res; } + ABSL_DEPRECATE_AND_INLINE() + [[nodiscard]] inline bool TryLock() ABSL_EXCLUSIVE_TRYLOCK_FUNCTION(true) { + return try_lock(); + } + // Release this SpinLock, which must be held by the calling thread. - inline void Unlock() ABSL_UNLOCK_FUNCTION() { + inline void unlock() ABSL_UNLOCK_FUNCTION() { ABSL_TSAN_MUTEX_PRE_UNLOCK(this, 0); uint32_t lock_value = lockword_.load(std::memory_order_relaxed); lock_value = lockword_.exchange(lock_value & kSpinLockCooperative, std::memory_order_release); if ((lock_value & kSpinLockDisabledScheduling) != 0) { - base_internal::SchedulingGuard::EnableRescheduling(true); + SchedulingGuard::EnableRescheduling(true); } if ((lock_value & kWaitTimeMask) != 0) { // Collect contentionz profile info, and speed the wakeup of any waiter. @@ -117,6 +143,9 @@ class ABSL_LOCKABLE ABSL_ATTRIBUTE_WARN_UNUSED SpinLock { ABSL_TSAN_MUTEX_POST_UNLOCK(this, 0); } + ABSL_DEPRECATE_AND_INLINE() + inline void Unlock() ABSL_UNLOCK_FUNCTION() { unlock(); } + // Determine if the lock is held. When the lock is held by the invoking // thread, true will always be returned. Intended to be used as // CHECK(lock.IsHeld()). @@ -146,6 +175,16 @@ class ABSL_LOCKABLE ABSL_ATTRIBUTE_WARN_UNUSED SpinLock { // Provide access to protected method above. Use for testing only. friend struct SpinLockTest; friend class tcmalloc::tcmalloc_internal::AllocationGuardSpinLockHolder; + friend class tcmalloc::tcmalloc_internal::Static; + + static int GetAdaptiveSpinCount() { + return adaptive_spin_count_.load(std::memory_order_relaxed); + } + static void SetAdaptiveSpinCount(int count) { + adaptive_spin_count_.store(count, std::memory_order_relaxed); + } + + static std::atomic adaptive_spin_count_; private: // lockword_ is used to store the following: @@ -175,9 +214,16 @@ class ABSL_LOCKABLE ABSL_ATTRIBUTE_WARN_UNUSED SpinLock { ~(kSpinLockHeld | kSpinLockCooperative | kSpinLockDisabledScheduling); // Returns true if the provided scheduling mode is cooperative. - static constexpr bool IsCooperative( - base_internal::SchedulingMode scheduling_mode) { - return scheduling_mode == base_internal::SCHEDULE_COOPERATIVE_AND_KERNEL; + static constexpr bool IsCooperative(SchedulingMode scheduling_mode) { + return scheduling_mode == SCHEDULE_COOPERATIVE_AND_KERNEL; + } + + constexpr void RegisterWithTsan() { +#if ABSL_HAVE_BUILTIN(__builtin_is_constant_evaluated) + if (!__builtin_is_constant_evaluated()) { + ABSL_TSAN_MUTEX_CREATE(this, __tsan_mutex_not_static); + } +#endif } bool IsCooperative() const { @@ -202,19 +248,18 @@ class ABSL_LOCKABLE ABSL_ATTRIBUTE_WARN_UNUSED SpinLock { // Corresponding locker object that arranges to acquire a spinlock for // the duration of a C++ scope. -class ABSL_SCOPED_LOCKABLE [[nodiscard]] SpinLockHolder { +class ABSL_SCOPED_LOCKABLE [[nodiscard]] SpinLockHolder + : public std::lock_guard { public: + inline explicit SpinLockHolder( + SpinLock& l ABSL_INTERNAL_ATTRIBUTE_CAPTURED_BY(this)) + ABSL_EXCLUSIVE_LOCK_FUNCTION(l) + : std::lock_guard(l) {} + ABSL_DEPRECATE_AND_INLINE() inline explicit SpinLockHolder(SpinLock* l) ABSL_EXCLUSIVE_LOCK_FUNCTION(l) - : lock_(l) { - l->Lock(); - } - inline ~SpinLockHolder() ABSL_UNLOCK_FUNCTION() { lock_->Unlock(); } + : SpinLockHolder(*l) {} - SpinLockHolder(const SpinLockHolder&) = delete; - SpinLockHolder& operator=(const SpinLockHolder&) = delete; - - private: - SpinLock* lock_; + inline ~SpinLockHolder() ABSL_UNLOCK_FUNCTION() = default; }; // Register a hook for profiling support. @@ -243,7 +288,7 @@ inline uint32_t SpinLock::TryLockInternal(uint32_t lock_value, if ((lock_value & kSpinLockCooperative) == 0) { // For non-cooperative locks we must make sure we mark ourselves as // non-reschedulable before we attempt to CompareAndSwap. - if (base_internal::SchedulingGuard::DisableRescheduling()) { + if (SchedulingGuard::DisableRescheduling()) { sched_disabled_bit = kSpinLockDisabledScheduling; } } @@ -252,7 +297,7 @@ inline uint32_t SpinLock::TryLockInternal(uint32_t lock_value, lock_value, kSpinLockHeld | lock_value | wait_cycles | sched_disabled_bit, std::memory_order_acquire, std::memory_order_relaxed)) { - base_internal::SchedulingGuard::EnableRescheduling(sched_disabled_bit != 0); + SchedulingGuard::EnableRescheduling(sched_disabled_bit != 0); } return lock_value; diff --git a/absl/base/internal/sysinfo.cc b/absl/base/internal/sysinfo.cc index 1937db307..cd08e51fb 100644 --- a/absl/base/internal/sysinfo.cc +++ b/absl/base/internal/sysinfo.cc @@ -244,8 +244,7 @@ static int64_t ReadMonotonicClockNanos() { int rc = clock_gettime(CLOCK_MONOTONIC, &t); #endif if (rc != 0) { - ABSL_INTERNAL_LOG( - FATAL, "clock_gettime() failed: (" + std::to_string(errno) + ")"); + ABSL_RAW_LOG(FATAL, "clock_gettime() failed: (%d)", errno); } return int64_t{t.tv_sec} * 1000000000 + t.tv_nsec; } @@ -456,15 +455,6 @@ pid_t GetTID() { return getthrid(); } pid_t GetTID() { return static_cast(_lwp_self()); } -#elif defined(__native_client__) - -pid_t GetTID() { - auto* thread = pthread_self(); - static_assert(sizeof(pid_t) == sizeof(thread), - "In NaCL int expected to be the same size as a pointer"); - return reinterpret_cast(thread); -} - #elif defined(__Fuchsia__) pid_t GetTID() { diff --git a/absl/base/internal/thread_identity.h b/absl/base/internal/thread_identity.h index acfc15a8f..0c2d6195c 100644 --- a/absl/base/internal/thread_identity.h +++ b/absl/base/internal/thread_identity.h @@ -146,6 +146,54 @@ struct ThreadIdentity { // ThreadIdentity itself. PerThreadSynch per_thread_synch; + struct SchedulerState { + std::atomic bound_schedulable{nullptr}; + // Storage space for a SpinLock, which is created through a placement new to + // break a dependency cycle. + uint32_t association_lock_word; + std::atomic scheduling_disabled_depth; + int potentially_blocking_depth; + uint32_t schedule_next_state; + + // When true, current thread is unlocking a mutex and actively waking a + // thread that was previously waiting, but that lock has yet more waiters. + // Used to signal to schedulers that work being woken should get an + // elevated priority. + bool waking_designated_waker; + + inline SpinLock* association_lock() { + return reinterpret_cast(&association_lock_word); + } + } scheduler_state; // Private: Reserved for use in Gloop + + // For worker threads that may not be doing any interesting user work, this + // tracks the current state of the worker. This is used to handle those + // threads differently e.g. when printing stacktraces. + // + // It should only be written to by the thread itself. + // + // Note that this is different from the mutex idle bit - threads running user + // work can be waiting but still be active. + // + // Note: not all parts of the code-base may maintain this field correctly and + // therefore this field should only be used to improve debugging/monitoring. + // + // Put it here to reuse some of the padding space. + enum class WaitState : uint8_t { + kActive = 0, + kWaitingForWork = 1, + }; + std::atomic wait_state; + static_assert(std::atomic::is_always_lock_free); + + // Add a padding such that scheduler_state is on a different cache line than + // waiter state. We use padding here, so that the size of the structure does + // not substantially grow due to the added padding. + static constexpr size_t kToBePaddedSize = + sizeof(SchedulerState) + sizeof(std::atomic); + static_assert(ABSL_CACHELINE_SIZE >= kToBePaddedSize); + char padding[ABSL_CACHELINE_SIZE - kToBePaddedSize]; + // Private: Reserved for absl::synchronization_internal::Waiter. struct WaiterState { alignas(void*) char data[256]; @@ -161,6 +209,10 @@ struct ThreadIdentity { std::atomic wait_start; // Ticker value when thread started waiting. std::atomic is_idle; // Has thread become idle yet? + // For tracking depth of __cxa_guard_acquire. This used to recognize heap + // allocations for function static objects. + int static_initialization_depth; + ThreadIdentity* next; }; diff --git a/absl/base/internal/unscaledcycleclock.cc b/absl/base/internal/unscaledcycleclock.cc index 68f92730a..73e4145e5 100644 --- a/absl/base/internal/unscaledcycleclock.cc +++ b/absl/base/internal/unscaledcycleclock.cc @@ -62,7 +62,7 @@ double UnscaledCycleClock::Frequency() { int64_t UnscaledCycleClock::Now() { #ifdef __GLIBC__ - return __ppc_get_timebase(); + return static_cast(__ppc_get_timebase()); #else #ifdef __powerpc64__ int64_t tbr; @@ -85,6 +85,10 @@ int64_t UnscaledCycleClock::Now() { double UnscaledCycleClock::Frequency() { #ifdef __GLIBC__ return __ppc_get_timebase_freq(); +#elif defined(__linux__) + // Fallback for musl + ppc64le: use constant timebase frequency (512 MHz) + // Must come after __GLIBC__. + return static_cast(512000000); #elif defined(_AIX) // This is the same constant value as returned by // __ppc_get_timebase_freq(). diff --git a/absl/base/internal/unscaledcycleclock.h b/absl/base/internal/unscaledcycleclock.h index bfd988731..fb5751b56 100644 --- a/absl/base/internal/unscaledcycleclock.h +++ b/absl/base/internal/unscaledcycleclock.h @@ -47,6 +47,10 @@ #if ABSL_USE_UNSCALED_CYCLECLOCK +namespace gloop_do_not_use { +class UnscaledCycleClockWrapperForPerCpuTest; +} // namespace gloop_do_not_use + namespace absl { ABSL_NAMESPACE_BEGIN namespace time_internal { @@ -75,6 +79,7 @@ class UnscaledCycleClock { friend class base_internal::CycleClock; friend class time_internal::UnscaledCycleClockWrapperForGetCurrentTime; friend class base_internal::UnscaledCycleClockWrapperForInitializeFrequency; + friend class gloop_do_not_use::UnscaledCycleClockWrapperForPerCpuTest; }; #if defined(__x86_64__) diff --git a/absl/base/internal/unscaledcycleclock_config.h b/absl/base/internal/unscaledcycleclock_config.h index 43a3dabee..9a0841dfa 100644 --- a/absl/base/internal/unscaledcycleclock_config.h +++ b/absl/base/internal/unscaledcycleclock_config.h @@ -34,7 +34,7 @@ // CycleClock that runs at atleast 1 MHz. We've found some Android // ARM64 devices where this is not the case, so we disable it by // default on Android ARM64. -#if defined(__native_client__) || (defined(__APPLE__)) || \ +#if defined(__APPLE__) || \ (defined(__ANDROID__) && defined(__aarch64__)) #define ABSL_USE_UNSCALED_CYCLECLOCK_DEFAULT 0 #else diff --git a/absl/base/macros.h b/absl/base/macros.h index ff89944ae..8c4a34d73 100644 --- a/absl/base/macros.h +++ b/absl/base/macros.h @@ -53,9 +53,40 @@ namespace macros_internal { template auto ArraySizeHelper(const T (&array)[N]) -> char (&)[N]; } // namespace macros_internal + +namespace base_internal { +#if ABSL_HAVE_CPP_ATTRIBUTE(clang::nomerge) +[[clang::nomerge]] // Needed when this function is not inlined +#endif +[[noreturn]] inline void HardeningAbort() { +#if ABSL_HAVE_CPP_ATTRIBUTE(clang::nomerge) + [[clang::nomerge]] // Needed when this function is inlined +#endif + ABSL_INTERNAL_IMMEDIATE_ABORT_IMPL(); + ABSL_INTERNAL_UNREACHABLE_IMPL(); +} +} // namespace base_internal ABSL_NAMESPACE_END } // namespace absl +// ABSL_INTERNAL_UNEVALUATED() +// +// Expands into a no-op expression that contains the given expression. Used to +// avoid unused-variable warnings in configurations that don't need to evaluate +// the given expression (e.g., NDEBUG). +#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L +// We use `decltype` here to avoid generating unnecessary code that the +// optimizer then has to optimize away. +// This not only improves compilation performance by reducing codegen bloat +// and optimization work, but also guarantees fast run-time performance without +// having to rely on the optimizer. +#define ABSL_INTERNAL_UNEVALUATED(expr) (decltype((void)(expr))()) +#else +// Pre-C++20, lambdas can't be inside unevaluated operands, so we're forced to +// rely on the optimizer. +#define ABSL_INTERNAL_UNEVALUATED(expr) (false ? (void)(expr) : void()) +#endif + // ABSL_BAD_CALL_IF() // // Used on a function overload to trap bad calls: any call that matches the @@ -93,33 +124,18 @@ ABSL_NAMESPACE_END // This macro is inspired by // https://akrzemi1.wordpress.com/2017/05/18/asserts-in-constexpr-functions/ #if defined(NDEBUG) -#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L -// We use `decltype` here to avoid generating unnecessary code that the -// optimizer then has to optimize away. -// This not only improves compilation performance by reducing codegen bloat -// and optimization work, but also guarantees fast run-time performance without -// having to rely on the optimizer. -#define ABSL_ASSERT(expr) (decltype((expr) ? void() : void())()) -#else -// Pre-C++20, lambdas can't be inside unevaluated operands, so we're forced to -// rely on the optimizer. -#define ABSL_ASSERT(expr) (false ? ((expr) ? void() : void()) : void()) -#endif +#define ABSL_ASSERT(expr) ABSL_INTERNAL_UNEVALUATED((expr) ? void() : void()) #else #define ABSL_ASSERT(expr) \ (ABSL_PREDICT_TRUE((expr)) ? static_cast(0) \ - : [] { assert(false && #expr); }()) // NOLINT + : assert(false && #expr)) // NOLINT #endif // `ABSL_INTERNAL_HARDENING_ABORT()` controls how `ABSL_HARDENING_ASSERT()` // aborts the program in release mode (when NDEBUG is defined). The // implementation should abort the program as quickly as possible and ideally it // should not be possible to ignore the abort request. -#define ABSL_INTERNAL_HARDENING_ABORT() \ - do { \ - ABSL_INTERNAL_IMMEDIATE_ABORT_IMPL(); \ - ABSL_INTERNAL_UNREACHABLE_IMPL(); \ - } while (false) +#define ABSL_INTERNAL_HARDENING_ABORT() ::absl::base_internal::HardeningAbort() // ABSL_HARDENING_ASSERT() // @@ -135,7 +151,7 @@ ABSL_NAMESPACE_END #if (ABSL_OPTION_HARDENED == 1 || ABSL_OPTION_HARDENED == 2) && defined(NDEBUG) #define ABSL_HARDENING_ASSERT(expr) \ (ABSL_PREDICT_TRUE((expr)) ? static_cast(0) \ - : [] { ABSL_INTERNAL_HARDENING_ABORT(); }()) + : ABSL_INTERNAL_HARDENING_ABORT()) #else #define ABSL_HARDENING_ASSERT(expr) ABSL_ASSERT(expr) #endif @@ -154,7 +170,7 @@ ABSL_NAMESPACE_END #if ABSL_OPTION_HARDENED == 1 && defined(NDEBUG) #define ABSL_HARDENING_ASSERT_SLOW(expr) \ (ABSL_PREDICT_TRUE((expr)) ? static_cast(0) \ - : [] { ABSL_INTERNAL_HARDENING_ABORT(); }()) + : ABSL_INTERNAL_HARDENING_ABORT()) #else #define ABSL_HARDENING_ASSERT_SLOW(expr) ABSL_ASSERT(expr) #endif @@ -169,42 +185,65 @@ ABSL_NAMESPACE_END #define ABSL_INTERNAL_RETHROW do {} while (false) #endif // ABSL_HAVE_EXCEPTIONS -// ABSL_DEPRECATE_AND_INLINE() +// ABSL_REFACTOR_INLINE +// +// Marks a function or type for automated refactoring by go/cpp-inliner. It can +// be used on inline function definitions or type aliases in header files and +// should be combined with the `[[deprecated]]` attribute. // -// Marks a function or type alias as deprecated and tags it to be picked up for -// automated refactoring by go/cpp-inliner. It can added to inline function -// definitions or type aliases. It should only be used within a header file. It -// differs from `ABSL_DEPRECATED` in the following ways: +// Using `ABSL_REFACTOR_INLINE` differs from using the `[[deprecated]]` alone in +// the following ways: // // 1. New uses of the function or type will be discouraged via Tricorder // warnings. // 2. If enabled via `METADATA`, automated changes will be sent out inlining the // functions's body or replacing the type where it is used. // -// For example: +// Examples: // -// ABSL_DEPRECATE_AND_INLINE() inline int OldFunc(int x) { +// [[deprecated("Use NewFunc() instead")]] ABSL_REFACTOR_INLINE +// inline int OldFunc(int x) { // return NewFunc(x, 0); // } // -// will mark `OldFunc` as deprecated, and the go/cpp-inliner service will -// replace calls to `OldFunc(x)` with calls to `NewFunc(x, 0)`. Once all calls -// to `OldFunc` have been replaced, `OldFunc` can be deleted. +// using OldType [[deprecated("Use NewType instead")]] ABSL_REFACTOR_INLINE = +// NewType; +// +// will mark `OldFunc` and `OldType` as deprecated, and the go/cpp-inliner +// service will replace calls to `OldFunc(x)` with calls to `NewFunc(x, 0)` and +// `OldType` with `NewType`. Once all replacements have been completed, the old +// function or type can be deleted. // // See go/cpp-inliner for more information. // // Note: go/cpp-inliner is Google-internal service for automated refactoring. // While open-source users do not have access to this service, the macro is -// provided for compatibility, and so that users receive deprecation warnings. -#if ABSL_HAVE_CPP_ATTRIBUTE(deprecated) && \ - ABSL_HAVE_CPP_ATTRIBUTE(clang::annotate) -#define ABSL_DEPRECATE_AND_INLINE() [[deprecated, clang::annotate("inline-me")]] -#elif ABSL_HAVE_CPP_ATTRIBUTE(deprecated) -#define ABSL_DEPRECATE_AND_INLINE() [[deprecated]] +// provided for compatibility. +#if ABSL_HAVE_CPP_ATTRIBUTE(clang::annotate) +#define ABSL_REFACTOR_INLINE [[clang::annotate("inline-me")]] #else -#define ABSL_DEPRECATE_AND_INLINE() +#define ABSL_REFACTOR_INLINE #endif +// ABSL_DEPRECATE_AND_INLINE() +// +// This is the original macro used by go/cpp-inliner that combines +// [[deprecated]] and ABSL_REFACTOR_INLINE. +// +// Examples: +// +// ABSL_DEPRECATE_AND_INLINE() inline int OldFunc(int x) { +// return NewFunc(x, 0); +// } +// +// using OldType ABSL_DEPRECATE_AND_INLINE() = NewType; +// +// The combination of `[[deprecated("Use X instead")]]` and +// `ABSL_REFACTOR_INLINE` is preferred because it provides a more informative +// deprecation message to developers, especially those that do not have access +// to the automated refactoring capabilities of go/cpp-inliner. +#define ABSL_DEPRECATE_AND_INLINE() [[deprecated]] ABSL_REFACTOR_INLINE + // Requires the compiler to prove that the size of the given object is at least // the expected amount. #if ABSL_HAVE_ATTRIBUTE(diagnose_if) && ABSL_HAVE_BUILTIN(__builtin_object_size) diff --git a/absl/base/nullability.h b/absl/base/nullability.h index 3a5d6e83e..ad80178ad 100644 --- a/absl/base/nullability.h +++ b/absl/base/nullability.h @@ -81,7 +81,7 @@ // const Employee* absl_nonnull e; // // // A non-null pointer to a const nullable pointer to an `Employee`. -// Employee* absl_nullable const* absl_nonnull e = nullptr; +// Employee* absl_nullable const* absl_nonnull e; // // // A non-null function pointer. // void (*absl_nonnull func)(int, double); @@ -95,7 +95,7 @@ // // describes is preferred, unless inconsistent with surrounding code. // absl_nonnull std::unique_ptr employee; // -// // Invalid annotation usage – this attempts to declare a pointer to a +// // Invalid annotation usage - this attempts to declare a pointer to a // // nullable `Employee`, which is meaningless. // absl_nullable Employee* e; // @@ -130,7 +130,7 @@ // // // CompleteTransaction() guarantees the returned pointer to an `Account` to // // be non-null. -// Account* absl_nonnull balance CompleteTransaction(double fee) { +// Account* absl_nonnull CompleteTransaction(double fee) { // ... // } // @@ -180,11 +180,11 @@ // `absl_nonnull` is *not guaranteed* to be non-null, and the compiler won't // alert or prevent assignment of a `T* absl_nullable` to a `T* absl_nonnull`. // =========================================================================== +// SKIP_ABSL_INLINE_NAMESPACE_CHECK #ifndef ABSL_BASE_NULLABILITY_H_ #define ABSL_BASE_NULLABILITY_H_ #include "absl/base/config.h" -#include "absl/base/internal/nullability_deprecated.h" // ABSL_POINTERS_DEFAULT_NONNULL // @@ -204,7 +204,7 @@ // T* absl_nullable GetNullablePtr(); // explicitly nullable // T* absl_nullability_unknown GetUnknownPtr(); // explicitly unknown // -// The macro can be safely used in header files – it will not affect any files +// The macro can be safely used in header files - it will not affect any files // that include it. // // In files with the macro, plain `T*` syntax means `T* absl_nonnull`, and the diff --git a/absl/base/optimization.h b/absl/base/optimization.h index 429ea9ce7..9b52a9244 100644 --- a/absl/base/optimization.h +++ b/absl/base/optimization.h @@ -31,6 +31,7 @@ #define ABSL_BASE_OPTIMIZATION_H_ #include +#include #ifdef __cplusplus // Included for std::unreachable() @@ -53,9 +54,7 @@ // ABSL_BLOCK_TAIL_CALL_OPTIMIZATION(); // return result; // } -#if defined(__pnacl__) -#define ABSL_BLOCK_TAIL_CALL_OPTIMIZATION() if (volatile int x = 0) { (void)x; } -#elif defined(__clang__) +#if defined(__clang__) // Clang will not tail call given inline volatile assembly. #define ABSL_BLOCK_TAIL_CALL_OPTIMIZATION() __asm__ __volatile__("") #elif defined(__GNUC__) @@ -219,12 +218,12 @@ #elif defined(_MSC_VER) #define ABSL_INTERNAL_UNREACHABLE_IMPL() __assume(false) #else -#define ABSL_INTERNAL_UNREACHABLE_IMPL() +#define ABSL_INTERNAL_UNREACHABLE_IMPL() ((void)0) #endif // `ABSL_UNREACHABLE()` is an unreachable statement. A program which reaches // one has undefined behavior, and the compiler may optimize accordingly. -#if ABSL_OPTION_HARDENED == 1 && defined(NDEBUG) +#if (ABSL_OPTION_HARDENED == 1 || ABSL_OPTION_HARDENED == 2) && defined(NDEBUG) // Abort in hardened mode to avoid dangerous undefined behavior. #define ABSL_UNREACHABLE() \ do { \ diff --git a/absl/base/options.h b/absl/base/options.h index f904f6446..bc344e93e 100644 --- a/absl/base/options.h +++ b/absl/base/options.h @@ -73,31 +73,33 @@ // Type Compatibility Options // ----------------------------------------------------------------------------- -// ABSL_OPTION_USE_STD_STRING_VIEW +// ABSL_OPTION_USE_STD_SOURCE_LOCATION // -// This option controls whether absl::string_view is implemented as an alias to -// std::string_view, or as an independent implementation. +// This option controls whether absl::SourceLocation is implemented as an alias +// to the std::source_location type, 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 0 means to use Abseil's implementation. This requires only C++17 +// support, and is expected to run on every toolchain we support, and to +// properly capture source location information on every toolchain that supports +// the necessary built-ins (such as `__builtin_LINE`). // -// A value of 1 means to use an alias to std::string_view. This requires that -// all code using Abseil is built in C++17 mode or later. +// 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 a working std::string_view is 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, absl::string_view will name a -// different type, with a different mangled name and binary layout, depending on -// the compiler flags passed by the end user. For more info, see +// and use an alias only if working std::source_location 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 -// absl::string_view is a typedef of std::string_view, use the feature macro -// ABSL_USES_STD_STRING_VIEW. - -#define ABSL_OPTION_USE_STD_STRING_VIEW 2 +// the source location type is an alias of std::source_location type, use the +// feature macro ABSL_USES_STD_SOURCE_LOCATION. +// +#define ABSL_OPTION_USE_STD_SOURCE_LOCATION 2 // ABSL_OPTION_USE_STD_ORDERING // @@ -149,7 +151,7 @@ // allowed. #define ABSL_OPTION_USE_INLINE_NAMESPACE 1 -#define ABSL_OPTION_INLINE_NAMESPACE_NAME lts_20250512 +#define ABSL_OPTION_INLINE_NAMESPACE_NAME lts_20260526 // ABSL_OPTION_HARDENED // @@ -181,4 +183,30 @@ #define ABSL_OPTION_HARDENED 0 +// ABSL_OPTION_INLINE_HW_ACCEL_STRATEGY +// +// This option controls whether Abseil is allowed to use non-portable +// hardware-accelerated implementations in headers (where they are typically +// inlined into the caller's translation unit). +// +// Using such optimizations in headers can lead to One Definition Rule (ODR) +// violations if different translation units are built with different CPU +// architecture flags (e.g., -march=native vs -march=generic) and linked +// together. +// +// A value of 0 means to use the portable software implementation in headers. +// This provides the best ODR guarantees when linking code built with +// inconsistent flags, but may be slower. +// +// A value of 1 means that the implementation requires the use of a +// hardware-accelerated implementation. This requires the compiler environment +// to support these instructions; otherwise, the build will fail. +// +// A value of 2 means to select the best available implementation based on +// the compiler flags, but can't guarantee ODR safety if translation units are +// built with inconsistent flags. +// +// User code should not inspect this macro. +#define ABSL_OPTION_INLINE_HW_ACCEL_STRATEGY 0 + #endif // ABSL_BASE_OPTIONS_H_ diff --git a/absl/base/policy_checks.h b/absl/base/policy_checks.h index f84944cfe..88b577439 100644 --- a/absl/base/policy_checks.h +++ b/absl/base/policy_checks.h @@ -46,17 +46,17 @@ // Toolchain Check // ----------------------------------------------------------------------------- -// We support Visual Studio 2019 (MSVC++ 16.0) and later. +// We support Visual Studio 2022 (MSVC++ 17.0) and later. // This minimum will go up. -#if defined(_MSC_VER) && _MSC_VER < 1920 && !defined(__clang__) -#error "This package requires Visual Studio 2019 (MSVC++ 16.0) or higher." +#if defined(_MSC_VER) && _MSC_VER < 1930 && !defined(__clang__) +#error "This package requires Visual Studio 2022 (MSVC++ 17.0) or higher." #endif // We support GCC 7 and later. // This minimum will go up. #if defined(__GNUC__) && !defined(__clang__) -#if __GNUC__ < 7 -#error "This package requires GCC 7 or higher." +#if __GNUC__ < 10 +#error "This package requires GCC 10 or higher." #endif #endif diff --git a/absl/base/throw_delegate.cc b/absl/base/throw_delegate.cc new file mode 100644 index 000000000..b960edd10 --- /dev/null +++ b/absl/base/throw_delegate.cc @@ -0,0 +1,205 @@ +// 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/base/throw_delegate.h" + +#include +#include +#include +#include + +#include "absl/base/config.h" +#include "absl/base/internal/raw_logging.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +void ThrowStdLogicError(const std::string& what_arg) { +#ifdef ABSL_HAVE_EXCEPTIONS + throw std::logic_error(what_arg); +#else + ABSL_RAW_LOG(FATAL, "%s", what_arg.c_str()); + std::abort(); +#endif +} +void ThrowStdLogicError(const char* what_arg) { +#ifdef ABSL_HAVE_EXCEPTIONS + throw std::logic_error(what_arg); +#else + ABSL_RAW_LOG(FATAL, "%s", what_arg); + std::abort(); +#endif +} +void ThrowStdInvalidArgument(const std::string& what_arg) { +#ifdef ABSL_HAVE_EXCEPTIONS + throw std::invalid_argument(what_arg); +#else + ABSL_RAW_LOG(FATAL, "%s", what_arg.c_str()); + std::abort(); +#endif +} +void ThrowStdInvalidArgument(const char* what_arg) { +#ifdef ABSL_HAVE_EXCEPTIONS + throw std::invalid_argument(what_arg); +#else + ABSL_RAW_LOG(FATAL, "%s", what_arg); + std::abort(); +#endif +} + +void ThrowStdDomainError(const std::string& what_arg) { +#ifdef ABSL_HAVE_EXCEPTIONS + throw std::domain_error(what_arg); +#else + ABSL_RAW_LOG(FATAL, "%s", what_arg.c_str()); + std::abort(); +#endif +} +void ThrowStdDomainError(const char* what_arg) { +#ifdef ABSL_HAVE_EXCEPTIONS + throw std::domain_error(what_arg); +#else + ABSL_RAW_LOG(FATAL, "%s", what_arg); + std::abort(); +#endif +} + +void ThrowStdLengthError(const std::string& what_arg) { +#ifdef ABSL_HAVE_EXCEPTIONS + throw std::length_error(what_arg); +#else + ABSL_RAW_LOG(FATAL, "%s", what_arg.c_str()); + std::abort(); +#endif +} +void ThrowStdLengthError(const char* what_arg) { +#ifdef ABSL_HAVE_EXCEPTIONS + throw std::length_error(what_arg); +#else + ABSL_RAW_LOG(FATAL, "%s", what_arg); + std::abort(); +#endif +} + +void ThrowStdOutOfRange(const std::string& what_arg) { +#ifdef ABSL_HAVE_EXCEPTIONS + throw std::out_of_range(what_arg); +#else + ABSL_RAW_LOG(FATAL, "%s", what_arg.c_str()); + std::abort(); +#endif +} +void ThrowStdOutOfRange(const char* what_arg) { +#ifdef ABSL_HAVE_EXCEPTIONS + throw std::out_of_range(what_arg); +#else + ABSL_RAW_LOG(FATAL, "%s", what_arg); + std::abort(); +#endif +} + +void ThrowStdRuntimeError(const std::string& what_arg) { +#ifdef ABSL_HAVE_EXCEPTIONS + throw std::runtime_error(what_arg); +#else + ABSL_RAW_LOG(FATAL, "%s", what_arg.c_str()); + std::abort(); +#endif +} +void ThrowStdRuntimeError(const char* what_arg) { +#ifdef ABSL_HAVE_EXCEPTIONS + throw std::runtime_error(what_arg); +#else + ABSL_RAW_LOG(FATAL, "%s", what_arg); + std::abort(); +#endif +} + +void ThrowStdRangeError(const std::string& what_arg) { +#ifdef ABSL_HAVE_EXCEPTIONS + throw std::range_error(what_arg); +#else + ABSL_RAW_LOG(FATAL, "%s", what_arg.c_str()); + std::abort(); +#endif +} +void ThrowStdRangeError(const char* what_arg) { +#ifdef ABSL_HAVE_EXCEPTIONS + throw std::range_error(what_arg); +#else + ABSL_RAW_LOG(FATAL, "%s", what_arg); + std::abort(); +#endif +} + +void ThrowStdOverflowError(const std::string& what_arg) { +#ifdef ABSL_HAVE_EXCEPTIONS + throw std::overflow_error(what_arg); +#else + ABSL_RAW_LOG(FATAL, "%s", what_arg.c_str()); + std::abort(); +#endif +} +void ThrowStdOverflowError(const char* what_arg) { +#ifdef ABSL_HAVE_EXCEPTIONS + throw std::overflow_error(what_arg); +#else + ABSL_RAW_LOG(FATAL, "%s", what_arg); + std::abort(); +#endif +} + +void ThrowStdUnderflowError(const std::string& what_arg) { +#ifdef ABSL_HAVE_EXCEPTIONS + throw std::underflow_error(what_arg); +#else + ABSL_RAW_LOG(FATAL, "%s", what_arg.c_str()); + std::abort(); +#endif +} +void ThrowStdUnderflowError(const char* what_arg) { +#ifdef ABSL_HAVE_EXCEPTIONS + throw std::underflow_error(what_arg); +#else + ABSL_RAW_LOG(FATAL, "%s", what_arg); + std::abort(); +#endif +} + +void ThrowStdBadFunctionCall() { +#ifdef ABSL_HAVE_EXCEPTIONS + throw std::bad_function_call(); +#else + std::abort(); +#endif +} + +void ThrowStdBadAlloc() { +#ifdef ABSL_HAVE_EXCEPTIONS + throw std::bad_alloc(); +#else + std::abort(); +#endif +} + +void ThrowStdBadArrayNewLength() { +#ifdef ABSL_HAVE_EXCEPTIONS + throw std::bad_array_new_length(); +#else + std::abort(); +#endif +} + +ABSL_NAMESPACE_END +} // namespace absl diff --git a/absl/base/throw_delegate.h b/absl/base/throw_delegate.h new file mode 100644 index 000000000..e89712ab7 --- /dev/null +++ b/absl/base/throw_delegate.h @@ -0,0 +1,65 @@ +// 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. + +#ifndef ABSL_BASE_THROW_DELEGATE_H_ +#define ABSL_BASE_THROW_DELEGATE_H_ + +#include + +#include "absl/base/config.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +// Helper functions that allow throwing exceptions consistently from anywhere. +// The main use case is for header-based libraries (eg templates), as they will +// be built by many different targets with their own compiler options. +// In particular, this will allow a safe way to throw exceptions even if the +// caller is compiled with -fno-exceptions. This is intended for implementing +// things like map<>::at(), which the standard documents as throwing an +// exception on error. +// +// Using other techniques like #if tricks could lead to ODR violations. +// +// You shouldn't use it unless you're writing code that you know will be built +// both with and without exceptions and you need to conform to an interface +// that uses exceptions. + +[[noreturn]] void ThrowStdLogicError(const std::string& what_arg); +[[noreturn]] void ThrowStdLogicError(const char* what_arg); +[[noreturn]] void ThrowStdInvalidArgument(const std::string& what_arg); +[[noreturn]] void ThrowStdInvalidArgument(const char* what_arg); +[[noreturn]] void ThrowStdDomainError(const std::string& what_arg); +[[noreturn]] void ThrowStdDomainError(const char* what_arg); +[[noreturn]] void ThrowStdLengthError(const std::string& what_arg); +[[noreturn]] void ThrowStdLengthError(const char* what_arg); +[[noreturn]] void ThrowStdOutOfRange(const std::string& what_arg); +[[noreturn]] void ThrowStdOutOfRange(const char* what_arg); +[[noreturn]] void ThrowStdRuntimeError(const std::string& what_arg); +[[noreturn]] void ThrowStdRuntimeError(const char* what_arg); +[[noreturn]] void ThrowStdRangeError(const std::string& what_arg); +[[noreturn]] void ThrowStdRangeError(const char* what_arg); +[[noreturn]] void ThrowStdOverflowError(const std::string& what_arg); +[[noreturn]] void ThrowStdOverflowError(const char* what_arg); +[[noreturn]] void ThrowStdUnderflowError(const std::string& what_arg); +[[noreturn]] void ThrowStdUnderflowError(const char* what_arg); + +[[noreturn]] void ThrowStdBadFunctionCall(); +[[noreturn]] void ThrowStdBadAlloc(); +[[noreturn]] void ThrowStdBadArrayNewLength(); + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_BASE_THROW_DELEGATE_H_ diff --git a/absl/cleanup/cleanup.h b/absl/cleanup/cleanup.h index 311e4828c..078674afb 100644 --- a/absl/cleanup/cleanup.h +++ b/absl/cleanup/cleanup.h @@ -19,6 +19,10 @@ // `absl::Cleanup` implements the scope guard idiom, invoking the contained // callback's `operator()() &&` on scope exit. // +// This class doesn't allocate or take any locks, and is safe to use in a signal +// handler. Of course the callback with which it is constructed also must be +// signal safe in order for this to be useful. +// // Example: // // ``` @@ -71,6 +75,7 @@ #include #include "absl/base/config.h" +#include "absl/base/internal/hardening.h" #include "absl/base/macros.h" #include "absl/cleanup/internal/cleanup.h" @@ -91,12 +96,12 @@ class [[nodiscard]] Cleanup final { Cleanup(Cleanup&& other) = default; void Cancel() && { - ABSL_HARDENING_ASSERT(storage_.IsCallbackEngaged()); + absl::base_internal::HardeningAssert(storage_.IsCallbackEngaged()); storage_.DestroyCallback(); } void Invoke() && { - ABSL_HARDENING_ASSERT(storage_.IsCallbackEngaged()); + absl::base_internal::HardeningAssert(storage_.IsCallbackEngaged()); storage_.InvokeCallback(); storage_.DestroyCallback(); } diff --git a/absl/cleanup/internal/cleanup.h b/absl/cleanup/internal/cleanup.h index 4dd6f9132..0a6c3ee26 100644 --- a/absl/cleanup/internal/cleanup.h +++ b/absl/cleanup/internal/cleanup.h @@ -19,6 +19,7 @@ #include #include +#include "absl/base/internal/hardening.h" #include "absl/base/macros.h" #include "absl/base/thread_annotations.h" #include "absl/utility/utility.h" @@ -49,13 +50,13 @@ class Storage { explicit Storage(Callback callback) { // Placement-new into a character buffer is used for eager destruction when // the cleanup is invoked or cancelled. To ensure this optimizes well, the - // behavior is implemented locally instead of using an absl::optional. + // behavior is implemented locally instead of using a std::optional. ::new (GetCallbackBuffer()) Callback(std::move(callback)); is_callback_engaged_ = true; } Storage(Storage&& other) { - ABSL_HARDENING_ASSERT(other.IsCallbackEngaged()); + absl::base_internal::HardeningAssert(other.IsCallbackEngaged()); ::new (GetCallbackBuffer()) Callback(std::move(other.GetCallback())); is_callback_engaged_ = true; diff --git a/absl/container/btree_map.h b/absl/container/btree_map.h index 32a82ef06..0746f72bd 100644 --- a/absl/container/btree_map.h +++ b/absl/container/btree_map.h @@ -57,18 +57,44 @@ #ifndef ABSL_CONTAINER_BTREE_MAP_H_ #define ABSL_CONTAINER_BTREE_MAP_H_ +#include +#include +#include +#include + #include "absl/base/attributes.h" #include "absl/container/internal/btree.h" // IWYU pragma: export #include "absl/container/internal/btree_container.h" // IWYU pragma: export +#include "absl/container/internal/common.h" +#include "absl/container/internal/container_memory.h" namespace absl { ABSL_NAMESPACE_BEGIN namespace container_internal { +template +struct map_params_impl; + +template +struct btree_map_defaults { + using Compare = std::less; + using Alloc = std::allocator>; + using TargetNodeSize = std::integral_constant; + using IsMulti = std::false_type; +}; + template -struct map_params; +using map_params = typename ApplyWithoutDefaultSuffix< + map_params_impl, + TypeList::Compare, + typename btree_map_defaults::Alloc, + typename btree_map_defaults::TargetNodeSize, + typename btree_map_defaults::IsMulti>, + TypeList, + std::integral_constant>>::type; } // namespace container_internal @@ -117,8 +143,8 @@ class ABSL_ATTRIBUTE_OWNER btree_map // // * Copy assignment operator // - // absl::btree_map map4; - // map4 = map3; + // absl::btree_map map4; + // map4 = map3; // // * Move constructor // @@ -555,8 +581,8 @@ class ABSL_ATTRIBUTE_OWNER btree_multimap // // * Copy assignment operator // - // absl::btree_multimap map4; - // map4 = map3; + // absl::btree_multimap map4; + // map4 = map3; // // * Move constructor // @@ -855,11 +881,20 @@ namespace container_internal { // A parameters structure for holding the type parameters for a btree_map. // Compare and Alloc should be nothrow copy-constructible. -template -struct map_params : common_params> { - using super_type = typename map_params::common_params; +template +struct map_params_impl + : common_params< + Key, + GetFromListOr::Compare, 0, + Params...>, + GetFromListOr::Alloc, 1, + Params...>, + GetFromListOr::TargetNodeSize, + 2, Params...>::value, + GetFromListOr::IsMulti, 3, + Params...>::value, + /*IsMap=*/true, map_slot_policy> { + using super_type = typename map_params_impl::common_params; using mapped_type = Data; // This type allows us to move keys when it is safe to do so. It is safe // for maps in which value_type and mutable_value_type are layout compatible. @@ -868,6 +903,21 @@ struct map_params : common_params::Compare, 0, + Params...>, + GetFromListOr::Alloc, 1, + Params...>, + GetFromListOr< + typename btree_map_defaults::TargetNodeSize, 2, + Params...>::value, + GetFromListOr::IsMulti, 3, + Params...>::value>, + map_params_impl>); + template static auto key(const V &value ABSL_ATTRIBUTE_LIFETIME_BOUND) -> decltype((value.first)) { diff --git a/absl/container/btree_set.h b/absl/container/btree_set.h index 16181de57..2dbcc2c54 100644 --- a/absl/container/btree_set.h +++ b/absl/container/btree_set.h @@ -56,9 +56,15 @@ #ifndef ABSL_CONTAINER_BTREE_SET_H_ #define ABSL_CONTAINER_BTREE_SET_H_ +#include +#include +#include +#include + #include "absl/base/attributes.h" #include "absl/container/internal/btree.h" // IWYU pragma: export #include "absl/container/internal/btree_container.h" // IWYU pragma: export +#include "absl/container/internal/common.h" namespace absl { ABSL_NAMESPACE_BEGIN @@ -68,9 +74,27 @@ namespace container_internal { template struct set_slot_policy; +template +struct set_params_impl; + +template +struct btree_set_defaults { + using Compare = std::less; + using Alloc = std::allocator; + using TargetNodeSize = std::integral_constant; + using IsMulti = std::false_type; +}; + template -struct set_params; +using set_params = typename ApplyWithoutDefaultSuffix< + set_params_impl, + TypeList::Compare, + typename btree_set_defaults::Alloc, + typename btree_set_defaults::TargetNodeSize, + typename btree_set_defaults::IsMulti>, + TypeList, + std::integral_constant>>::type; } // namespace container_internal @@ -119,8 +143,8 @@ class ABSL_ATTRIBUTE_OWNER btree_set // // * Copy assignment operator // - // absl::btree_set set4; - // set4 = set3; + // absl::btree_set set4; + // set4 = set3; // // * Move constructor // @@ -475,8 +499,8 @@ class ABSL_ATTRIBUTE_OWNER btree_multiset // // * Copy assignment operator // - // absl::btree_multiset set4; - // set4 = set3; + // absl::btree_multiset set4; + // set4 = set3; // // * Move constructor // @@ -781,34 +805,56 @@ struct set_slot_policy { template static void construct(Alloc *alloc, slot_type *slot, Args &&...args) { - absl::allocator_traits::construct(*alloc, slot, - std::forward(args)...); + std::allocator_traits::construct(*alloc, slot, + std::forward(args)...); } template static void construct(Alloc *alloc, slot_type *slot, slot_type *other) { - absl::allocator_traits::construct(*alloc, slot, std::move(*other)); + std::allocator_traits::construct(*alloc, slot, std::move(*other)); } template static void construct(Alloc *alloc, slot_type *slot, const slot_type *other) { - absl::allocator_traits::construct(*alloc, slot, *other); + std::allocator_traits::construct(*alloc, slot, *other); } template static void destroy(Alloc *alloc, slot_type *slot) { - absl::allocator_traits::destroy(*alloc, slot); + std::allocator_traits::destroy(*alloc, slot); } }; // A parameters structure for holding the type parameters for a btree_set. // Compare and Alloc should be nothrow copy-constructible. -template -struct set_params : common_params> { +template +struct set_params_impl + : common_params< + Key, + GetFromListOr::Compare, 0, + Params...>, + GetFromListOr::Alloc, 1, Params...>, + GetFromListOr::TargetNodeSize, 2, + Params...>::value, + GetFromListOr::IsMulti, 3, + Params...>::value, + /*IsMap=*/false, set_slot_policy> { using value_type = Key; - using slot_type = typename set_params::common_params::slot_type; + using slot_type = typename set_params_impl::common_params::slot_type; + + static_assert( + std::is_same_v< + set_params< + Key, + GetFromListOr::Compare, 0, + Params...>, + GetFromListOr::Alloc, 1, + Params...>, + GetFromListOr::TargetNodeSize, 2, + Params...>::value, + GetFromListOr::IsMulti, 3, + Params...>::value>, + set_params_impl>); template static const V &key(const V &value) { diff --git a/absl/container/chunked_queue.h b/absl/container/chunked_queue.h new file mode 100644 index 000000000..ff814474a --- /dev/null +++ b/absl/container/chunked_queue.h @@ -0,0 +1,757 @@ +// Copyright 2025 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: chunked_queue.h +// ----------------------------------------------------------------------------- +// +// `std::deque` provides random access and fast push/pop back/front. It is +// implemented as an array of fixed blocks. It provides no control of block size +// and implementations differ; libstdc++ tries to allocate blocks of ~512 bytes +// and libc++ tries for blocks of ~4k bytes. +// +// `absl::chunked_queue` provides the same minus random access. It is +// implemented as a double-linked list of fixed or variable sized blocks. +// +// `absl::chunked_queue` is useful when memory usage is paramount as it provides +// finegrained and configurable block sizing. +// +// The interface supported by this class is limited to: +// +// empty() +// size() +// max_size() +// shrink_to_fit() +// resize() +// assign() +// push_back() +// emplace_back() +// pop_front() +// front() +// back() +// swap() +// clear() +// begin(), end() +// cbegin(), cend() +// +// === ADVANCED USAGE +// +// == clear() +// +// As an optimization clear() leaves the first block of the chunked_queue +// allocated (but empty). So clear will not delete all memory of the container. +// In order to do so, call shrink_to_fit() or swap the container with an empty +// one. +// +// absl::chunked_queue q = {1, 2, 3}; +// q.clear(); +// q.shrink_to_fit(); +// +// == block size customization +// +// chunked_queue allows customization of the block size for each block. By +// default the block size is set to 1 element and the size doubles for the next +// block until it reaches the default max block size, which is 128 elements. +// +// = fixed size +// +// When only the first block size parameter is specified, it sets a fixed block +// size for all blocks: +// +// chunked_queue: 32 elements per block +// +// The smaller the block size, the less the memory usage for small queues at the +// cost of performance. Caveat: For large queues, a smaller block size will +// increase memory usage, and reduce performance. +// +// = variable size +// +// When both block size parameters are specified, they set the min and max block +// sizes for the blocks. Initially the queue starts with the min block size and +// as it grows, the size of each block grows until it reaches the max block +// size. +// New blocks are double the size of the tail block (so they at least +// double the size of the queue). +// +// chunked_queue: first block 4 elements, second block 8 elements, +// third block 16 elements, fourth block 32 elements, +// all other blocks 64 elements +// +// One can specify a min and max such that small queues will not waste memory +// and large queues will not have too many blocks. + +#ifndef ABSL_CONTAINER_CHUNKED_QUEUE_H_ +#define ABSL_CONTAINER_CHUNKED_QUEUE_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "absl/base/attributes.h" +#include "absl/base/config.h" +#include "absl/base/internal/hardening.h" +#include "absl/base/internal/iterator_traits.h" +#include "absl/base/macros.h" +#include "absl/container/internal/chunked_queue.h" +#include "absl/container/internal/layout.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +template > +class chunked_queue { + public: + static constexpr size_t kBlockSizeMin = (BLo == 0 && BHi == 0) ? 1 : BLo; + static constexpr size_t kBlockSizeMax = (BLo == 0 && BHi == 0) ? 128 : BHi; + + private: + static_assert(kBlockSizeMin > 0, "Min block size cannot be zero"); + static_assert(kBlockSizeMin <= kBlockSizeMax, "Invalid block size bounds"); + + using Block = container_internal::ChunkedQueueBlock; + using AllocatorTraits = std::allocator_traits; + + class iterator_common { + public: + friend bool operator==(const iterator_common& a, const iterator_common& b) { + return a.ptr == b.ptr; + } + + friend bool operator!=(const iterator_common& a, const iterator_common& b) { + return !(a == b); + } + + protected: + iterator_common() = default; + explicit iterator_common(Block* b) + : block(b), ptr(b->start()), limit(b->limit()) {} + + void Incr() { + // If we do not have a next block, make ptr point one past the end of this + // block. If we do have a next block, make ptr point to the first element + // of the next block. + ++ptr; + if (ptr == limit && block->next()) *this = iterator_common(block->next()); + } + + void IncrBy(size_t n) { + while (ptr + n > limit) { + n -= limit - ptr; + *this = iterator_common(block->next()); + } + ptr += n; + } + + Block* block = nullptr; + T* ptr = nullptr; + T* limit = nullptr; + }; + + // CT can be either T or const T. + template + class basic_iterator : public iterator_common { + public: + using iterator_category = std::forward_iterator_tag; + using value_type = typename AllocatorTraits::value_type; + using difference_type = typename AllocatorTraits::difference_type; + using pointer = + typename std::conditional::value, + typename AllocatorTraits::const_pointer, + typename AllocatorTraits::pointer>::type; + using reference = CT&; + + basic_iterator() = default; + + // Copy ctor if CT is T. + // Otherwise it's a conversion of iterator to const_iterator. + basic_iterator(const basic_iterator& it) // NOLINT(runtime/explicit) + : iterator_common(it) {} + + basic_iterator& operator=(const basic_iterator& other) = default; + + reference operator*() const { return *this->ptr; } + pointer operator->() const { return this->ptr; } + basic_iterator& operator++() { + this->Incr(); + return *this; + } + basic_iterator operator++(int) { + basic_iterator t = *this; + ++*this; + return t; + } + + private: + explicit basic_iterator(Block* b) : iterator_common(b) {} + + friend chunked_queue; + }; + + public: + using allocator_type = typename AllocatorTraits::allocator_type; + using value_type = typename AllocatorTraits::value_type; + using size_type = typename AllocatorTraits::size_type; + using difference_type = typename AllocatorTraits::difference_type; + using reference = value_type&; + using const_reference = const value_type&; + using iterator = basic_iterator; + using const_iterator = basic_iterator; + + // Constructs an empty queue. + chunked_queue() : chunked_queue(allocator_type()) {} + + // Constructs an empty queue with a custom allocator. + explicit chunked_queue(const allocator_type& alloc) + : alloc_and_size_(alloc) {} + + // Constructs a queue with `count` default-inserted elements. + explicit chunked_queue(size_type count, + const allocator_type& alloc = allocator_type()) + : alloc_and_size_(alloc) { + resize(count); + } + + // Constructs a queue with `count` copies of `value`. + chunked_queue(size_type count, const T& value, + const allocator_type& alloc = allocator_type()) + : alloc_and_size_(alloc) { + assign(count, value); + } + + // Constructs a queue with the contents of the range [first, last). + template ::value>> + chunked_queue(Iter first, Iter last, + const allocator_type& alloc = allocator_type()) + : alloc_and_size_(alloc) { + using Tag = typename std::iterator_traits::iterator_category; + RangeInit(first, last, Tag()); + } + + // Constructs a queue with the contents of the initializer list `list`. + chunked_queue(std::initializer_list list, + const allocator_type& alloc = allocator_type()) + : chunked_queue(list.begin(), list.end(), alloc) {} + + ~chunked_queue(); + + // Copy constructor. + chunked_queue(const chunked_queue& other) + : chunked_queue(other, + AllocatorTraits::select_on_container_copy_construction( + other.alloc_and_size_.allocator())) {} + + // Copy constructor with specific allocator. + chunked_queue(const chunked_queue& other, const allocator_type& alloc) + : alloc_and_size_(alloc) { + for (const_reference item : other) { + push_back(item); + } + } + + // Move constructor. + chunked_queue(chunked_queue&& other) noexcept + : head_(other.head_), + tail_(other.tail_), + alloc_and_size_(std::move(other.alloc_and_size_)) { + other.head_ = {}; + other.tail_ = {}; + other.alloc_and_size_.size = 0; + } + + // Replaces contents with those from initializer list `il`. + chunked_queue& operator=(std::initializer_list il) { + assign(il.begin(), il.end()); + return *this; + } + + // Copy assignment operator. + chunked_queue& operator=(const chunked_queue& other) { + if (this == &other) { + return *this; + } + if (AllocatorTraits::propagate_on_container_copy_assignment::value && + (alloc_and_size_.allocator() != other.alloc_and_size_.allocator())) { + // Destroy all current elements and blocks with the current allocator, + // before switching this to use the allocator propagated from "other". + DestroyAndDeallocateAll(); + alloc_and_size_ = AllocatorAndSize(other.alloc_and_size_.allocator()); + } + assign(other.begin(), other.end()); + return *this; + } + + // Move assignment operator. + chunked_queue& operator=(chunked_queue&& other) noexcept; + + // Returns true if the queue contains no elements. + bool empty() const { return alloc_and_size_.size == 0; } + + // Returns the number of elements in the queue. + size_t size() const { return alloc_and_size_.size; } + + // Returns the maximum number of elements the queue is able to hold. + size_type max_size() const noexcept { + return AllocatorTraits::max_size(alloc_and_size_.allocator()); + } + + // Resizes the container to contain `new_size` elements. + // If `new_size > size()`, additional default-inserted elements are appended. + // If `new_size < size()`, elements are removed from the end. + void resize(size_t new_size); + + // Resizes the container to contain `new_size` elements. + // If `new_size > size()`, additional copies of `value` are appended. + // If `new_size < size()`, elements are removed from the end. + void resize(size_type new_size, const T& value) { + if (new_size > size()) { + size_t to_add = new_size - size(); + for (size_t i = 0; i < to_add; ++i) { + push_back(value); + } + } else { + resize(new_size); + } + } + + // Requests the removal of unused capacity. + void shrink_to_fit() { + // As an optimization clear() leaves the first block of the chunked_queue + // allocated (but empty). When empty, shrink_to_fit() deallocates the first + // block by swapping it a newly constructed container that has no first + // block. + if (empty()) { + chunked_queue(alloc_and_size_.allocator()).swap(*this); + } + } + + // Replaces the contents with copies of those in the range [first, last). + template ::value>> + void assign(Iter first, Iter last) { + auto out = begin(); + Block* prev_block = nullptr; + + // Overwrite existing elements. + for (; out != end() && first != last; ++first) { + // Track the previous block so we can correctly update tail_ if we stop + // exactly at a block boundary. + if (out.ptr + 1 == out.block->limit()) { + prev_block = out.block; + } + *out = *first; + ++out; + } + + // If we stopped exactly at the start of a block (meaning the previous block + // was full), we must ensure tail_ points to the end of the previous block, + // not the start of the current (now empty and to be deleted) block. + // This maintains the invariant required by back() which assumes tail_ + // never points to the start of a block (unless it's the only block). + if (!empty() && out.block != nullptr && out.ptr == out.block->start() && + prev_block != nullptr) { + // Delete the current block and all subsequent blocks. + // + // NOTE: Calling EraseAllFrom on an iterator that points to the limit of + // the previous block will not delete any element from the previous block. + iterator prev_block_end(prev_block); + prev_block_end.ptr = prev_block->limit(); + EraseAllFrom(prev_block_end); + + // Update tail_ to point to the end of the previous block. + tail_ = prev_block_end; + prev_block->set_next(nullptr); + } else { + // Standard erase from the current position to the end. + EraseAllFrom(out); + } + + // Append any remaining new elements. + for (; first != last; ++first) { + push_back(*first); + } + } + + // Replaces the contents with `count` copies of `value`. + void assign(size_type count, const T& value) { + clear(); + for (size_type i = 0; i < count; ++i) { + push_back(value); + } + } + + // Replaces the contents with the elements from the initializer list `il`. + void assign(std::initializer_list il) { assign(il.begin(), il.end()); } + + // Appends the given element value to the end of the container. + // Invalidates `end()` iterator. References to other elements remain valid. + void push_back(const T& val) { emplace_back(val); } + void push_back(T&& val) { emplace_back(std::move(val)); } + + // Appends a new element to the end of the container. + // The element is constructed in-place with `args`. + // Returns a reference to the new element. + // Invalidates `end()` iterator. References to other elements remain valid. + template + T& emplace_back(A&&... args) { + T* storage = AllocateBack(); + AllocatorTraits::construct(alloc_and_size_.allocator(), storage, + std::forward(args)...); + return *storage; + } + + // Removes the first element of the container. + // Invalidates iterators to the removed element. + // REQUIRES: !empty() + void pop_front(); + + // Returns a reference to the first element in the container. + // REQUIRES: !empty() + T& front() { + absl::base_internal::HardeningAssertNonEmpty(*this); + return *head_; + } + const T& front() const { + absl::base_internal::HardeningAssertNonEmpty(*this); + return *head_; + } + + // Returns a reference to the last element in the container. + // REQUIRES: !empty() + T& back() { + absl::base_internal::HardeningAssertNonEmpty(*this); + return *(&*tail_ - 1); + } + const T& back() const { + absl::base_internal::HardeningAssertNonEmpty(*this); + return *(&*tail_ - 1); + } + + // Swaps the contents of this queue with `other`. + void swap(chunked_queue& other) noexcept { + using std::swap; + swap(head_, other.head_); + swap(tail_, other.tail_); + if (AllocatorTraits::propagate_on_container_swap::value) { + swap(alloc_and_size_, other.alloc_and_size_); + } else { + // Swap only the sizes; each object keeps its allocator. + // + // (It is undefined behavior to swap between two containers with unequal + // allocators if propagate_on_container_swap is false, so we don't have to + // handle that here like we do in the move-assignment operator.) + absl::base_internal::HardeningAssert(get_allocator() == + other.get_allocator()); + swap(alloc_and_size_.size, other.alloc_and_size_.size); + } + } + + // Erases all elements from the container. + // Note: Leaves one empty block allocated as an optimization. + // To free all memory, call shrink_to_fit() after calling clear(). + void clear(); + + iterator begin() { return head_; } + iterator end() { return tail_; } + + const_iterator begin() const { return head_; } + const_iterator end() const { return tail_; } + + const_iterator cbegin() const { return head_; } + const_iterator cend() const { return tail_; } + + // Returns the allocator associated with the container. + allocator_type get_allocator() const { return alloc_and_size_.allocator(); } + + private: + // Empty base-class optimization: bundle storage for our allocator together + // with a field we had to store anyway (size), via inheriting from the + // allocator, so this allocator instance doesn't consume any storage + // when its type has no data members. + struct AllocatorAndSize : private allocator_type { + explicit AllocatorAndSize(const allocator_type& alloc) + : allocator_type(alloc) {} + const allocator_type& allocator() const { return *this; } + allocator_type& allocator() { return *this; } + size_t size = 0; + }; + + template + void RangeInit(Iter first, Iter last, std::input_iterator_tag) { + while (first != last) { + AddTailBlock(); + for (; first != last && tail_.ptr != tail_.limit; + ++alloc_and_size_.size, ++tail_.ptr, ++first) { + AllocatorTraits::construct(alloc_and_size_.allocator(), tail_.ptr, + *first); + } + } + } + + void Construct(T* start, T* limit) { + ABSL_ASSERT(start <= limit); + for (; start != limit; ++start) { + AllocatorTraits::construct(alloc_and_size_.allocator(), start); + } + } + + size_t Destroy(T* start, T* limit) { + ABSL_ASSERT(start <= limit); + const size_t n = limit - start; + for (; start != limit; ++start) { + AllocatorTraits::destroy(alloc_and_size_.allocator(), start); + } + return n; + } + + T* block_begin(Block* b) const { + return b == head_.block ? head_.ptr : b->start(); + } + T* block_end(Block* b) const { + // We have the choice of !b->next or b == tail_.block to determine if b is + // the tail or not. !b->next is usually faster because the caller of + // block_end() is most likely traversing the list of blocks so b->next is + // already fetched into some register. + return !b->next() ? tail_.ptr : b->limit(); + } + + void AddTailBlock(); + size_t NewBlockSize() { + // Double the last block size and bound to [kBlockSizeMin, kBlockSizeMax]. + if (!tail_.block) return kBlockSizeMin; + return (std::min)(kBlockSizeMax, 2 * tail_.block->size()); + } + + T* AllocateBack(); + void EraseAllFrom(iterator i); + + // Destroys any contained elements and destroys all allocated storage. + // (Like clear(), except this doesn't leave any empty blocks behind.) + void DestroyAndDeallocateAll(); + + // The set of elements in the queue is the following: + // + // (1) When we have just one block: + // [head_.ptr .. tail_.ptr-1] + // (2) When we have multiple blocks: + // [head_.ptr .. head_.limit-1] + // ... concatenation of all elements from interior blocks ... + // [tail_.ptr .. tail_.limit-1] + // + // Rep invariants: + // When have just one block: + // head_.limit == tail_.limit == &head_.block->element[kBlockSize] + // Always: + // head_.ptr <= head_.limit + // tail_.ptr <= tail_.limit + + iterator head_; + iterator tail_; + AllocatorAndSize alloc_and_size_; +}; + +template +constexpr size_t chunked_queue::kBlockSizeMin; + +template +constexpr size_t chunked_queue::kBlockSizeMax; + +template +inline void swap(chunked_queue& a, + chunked_queue& b) noexcept { + a.swap(b); +} + +template +chunked_queue& +chunked_queue::operator=( + chunked_queue&& other) noexcept { + if (this == &other) { + return *this; + } + DestroyAndDeallocateAll(); + + if constexpr (AllocatorTraits::propagate_on_container_move_assignment:: + value) { + // Take over the storage of "other", along with its allocator. + head_ = other.head_; + tail_ = other.tail_; + alloc_and_size_ = std::move(other.alloc_and_size_); + other.head_ = {}; + other.tail_ = {}; + other.alloc_and_size_.size = 0; + } else if (get_allocator() == other.get_allocator()) { + // Take over the storage of "other", with which we share an allocator. + head_ = other.head_; + tail_ = other.tail_; + alloc_and_size_.size = other.alloc_and_size_.size; + other.head_ = {}; + other.tail_ = {}; + other.alloc_and_size_.size = 0; + } else { + // We cannot take over of the storage from "other", since it has a different + // allocator; we're stuck move-assigning elements individually. + for (auto& elem : other) { + push_back(std::move(elem)); + } + } + return *this; +} + +template +inline chunked_queue::~chunked_queue() { + Block* b = head_.block; + while (b) { + Block* next = b->next(); + Destroy(block_begin(b), block_end(b)); + Block::Delete(b, &alloc_and_size_.allocator()); + b = next; + } +} + +template +void chunked_queue::resize(size_t new_size) { + while (new_size > size()) { + ptrdiff_t to_add = new_size - size(); + if (tail_.ptr == tail_.limit) { + AddTailBlock(); + } + T* start = tail_.ptr; + T* limit = (std::min)(tail_.limit, start + to_add); + Construct(start, limit); + tail_.ptr = limit; + alloc_and_size_.size += limit - start; + } + if (size() == new_size) { + return; + } + ABSL_ASSERT(new_size < size()); + auto new_end = begin(); + new_end.IncrBy(new_size); + ABSL_ASSERT(new_end != end()); + EraseAllFrom(new_end); +} + +template +inline void chunked_queue::AddTailBlock() { + ABSL_ASSERT(tail_.ptr == tail_.limit); + auto* b = Block::New(NewBlockSize(), &alloc_and_size_.allocator()); + if (!head_.block) { + ABSL_ASSERT(!tail_.block); + head_ = iterator(b); + } else { + ABSL_ASSERT(tail_.block); + tail_.block->set_next(b); + } + tail_ = iterator(b); +} + +template +inline T* chunked_queue::AllocateBack() { + if (tail_.ptr == tail_.limit) { + AddTailBlock(); + } + ++alloc_and_size_.size; + return tail_.ptr++; +} + +template +inline void chunked_queue::EraseAllFrom(iterator i) { + if (!i.block) { + return; + } + ABSL_ASSERT(i.ptr); + ABSL_ASSERT(i.limit); + alloc_and_size_.size -= Destroy(i.ptr, block_end(i.block)); + Block* b = i.block->next(); + while (b) { + Block* next = b->next(); + alloc_and_size_.size -= Destroy(b->start(), block_end(b)); + Block::Delete(b, &alloc_and_size_.allocator()); + b = next; + } + tail_ = i; + tail_.block->set_next(nullptr); +} + +template +inline void chunked_queue::DestroyAndDeallocateAll() { + Block* b = head_.block; + while (b) { + Block* next = b->next(); + Destroy(block_begin(b), block_end(b)); + Block::Delete(b, &alloc_and_size_.allocator()); + b = next; + } + head_ = iterator(); + tail_ = iterator(); + alloc_and_size_.size = 0; +} + +template +inline void chunked_queue::pop_front() { + absl::base_internal::HardeningAssertNonEmpty(*this); + ABSL_ASSERT(head_.block); + AllocatorTraits::destroy(alloc_and_size_.allocator(), head_.ptr); + ++head_.ptr; + --alloc_and_size_.size; + if (empty()) { + // Reset head and tail to the start of the (only) block. + ABSL_ASSERT(head_.block == tail_.block); + head_.ptr = tail_.ptr = head_.block->start(); + return; + } + if (head_.ptr == head_.limit) { + Block* n = head_.block->next(); + Block::Delete(head_.block, &alloc_and_size_.allocator()); + head_ = iterator(n); + } +} + +template +void chunked_queue::clear() { + // NOTE: As an optimization we leave one block allocated. + Block* b = head_.block; + if (!b) { + ABSL_ASSERT(empty()); + return; + } + while (b) { + Block* next = b->next(); + Destroy(block_begin(b), block_end(b)); + if (head_.block != b) { + Block::Delete(b, &alloc_and_size_.allocator()); + } + b = next; + } + b = head_.block; + b->set_next(nullptr); + head_ = tail_ = iterator(b); + alloc_and_size_.size = 0; +} + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_CHUNKED_QUEUE_H_ diff --git a/absl/container/fixed_array.h b/absl/container/fixed_array.h index 6c238fc38..77949bfec 100644 --- a/absl/container/fixed_array.h +++ b/absl/container/fixed_array.h @@ -44,11 +44,12 @@ #include "absl/base/attributes.h" #include "absl/base/config.h" #include "absl/base/dynamic_annotations.h" +#include "absl/base/internal/hardening.h" #include "absl/base/internal/iterator_traits.h" -#include "absl/base/internal/throw_delegate.h" #include "absl/base/macros.h" #include "absl/base/optimization.h" #include "absl/base/port.h" +#include "absl/base/throw_delegate.h" #include "absl/container/internal/compressed_tuple.h" #include "absl/hash/internal/weakly_mixed_integer.h" #include "absl/memory/memory.h" @@ -84,11 +85,9 @@ class ABSL_ATTRIBUTE_WARN_UNUSED FixedArray { static constexpr size_t kInlineBytesDefault = 256; using AllocatorTraits = std::allocator_traits; - // std::iterator_traits isn't guaranteed to be SFINAE-friendly until C++17, - // but this seems to be mostly pedantic. template - using EnableIfForwardIterator = std::enable_if_t< - base_internal::IsAtLeastForwardIterator::value>; + using EnableIfInputIterator = + std::enable_if_t::value>; static constexpr bool NoexceptCopyable() { return std::is_nothrow_copy_constructible::value && absl::allocator_is_nothrow::value; @@ -98,7 +97,7 @@ class ABSL_ATTRIBUTE_WARN_UNUSED FixedArray { absl::allocator_is_nothrow::value; } static constexpr bool DefaultConstructorIsNonTrivial() { - return !absl::is_trivially_default_constructible::value; + return !std::is_trivially_default_constructible::value; } public: @@ -161,8 +160,8 @@ class ABSL_ATTRIBUTE_WARN_UNUSED FixedArray { // Creates an array initialized with the elements from the input // range. The array's size will always be `std::distance(first, last)`. - // REQUIRES: Iterator must be a forward_iterator or better. - template * = nullptr> + // REQUIRES: Iterator must be a input_iterator or better. + template * = nullptr> FixedArray(Iterator first, Iterator last, const allocator_type& a = allocator_type()) : storage_(std::distance(first, last), a) { @@ -224,7 +223,7 @@ class ABSL_ATTRIBUTE_WARN_UNUSED FixedArray { // Returns a reference the ith element of the fixed array. // REQUIRES: 0 <= i < size() reference operator[](size_type i) ABSL_ATTRIBUTE_LIFETIME_BOUND { - ABSL_HARDENING_ASSERT(i < size()); + absl::base_internal::HardeningAssertLT(i, size()); return data()[i]; } @@ -232,7 +231,7 @@ class ABSL_ATTRIBUTE_WARN_UNUSED FixedArray { // ith element of the fixed array. // REQUIRES: 0 <= i < size() const_reference operator[](size_type i) const ABSL_ATTRIBUTE_LIFETIME_BOUND { - ABSL_HARDENING_ASSERT(i < size()); + absl::base_internal::HardeningAssertLT(i, size()); return data()[i]; } @@ -242,7 +241,7 @@ class ABSL_ATTRIBUTE_WARN_UNUSED FixedArray { // array, or throws std::out_of_range reference at(size_type i) ABSL_ATTRIBUTE_LIFETIME_BOUND { if (ABSL_PREDICT_FALSE(i >= size())) { - base_internal::ThrowStdOutOfRange("FixedArray::at failed bounds check"); + ThrowStdOutOfRange("FixedArray::at failed bounds check"); } return data()[i]; } @@ -251,7 +250,7 @@ class ABSL_ATTRIBUTE_WARN_UNUSED FixedArray { // of the fixed array. const_reference at(size_type i) const ABSL_ATTRIBUTE_LIFETIME_BOUND { if (ABSL_PREDICT_FALSE(i >= size())) { - base_internal::ThrowStdOutOfRange("FixedArray::at failed bounds check"); + ThrowStdOutOfRange("FixedArray::at failed bounds check"); } return data()[i]; } @@ -260,14 +259,14 @@ class ABSL_ATTRIBUTE_WARN_UNUSED FixedArray { // // Returns a reference to the first element of the fixed array. reference front() ABSL_ATTRIBUTE_LIFETIME_BOUND { - ABSL_HARDENING_ASSERT(!empty()); + absl::base_internal::HardeningAssertNonEmpty(*this); return data()[0]; } // Overload of FixedArray::front() to return a reference to the first element // of a fixed array of const values. const_reference front() const ABSL_ATTRIBUTE_LIFETIME_BOUND { - ABSL_HARDENING_ASSERT(!empty()); + absl::base_internal::HardeningAssertNonEmpty(*this); return data()[0]; } @@ -275,14 +274,14 @@ class ABSL_ATTRIBUTE_WARN_UNUSED FixedArray { // // Returns a reference to the last element of the fixed array. reference back() ABSL_ATTRIBUTE_LIFETIME_BOUND { - ABSL_HARDENING_ASSERT(!empty()); + absl::base_internal::HardeningAssertNonEmpty(*this); return data()[size() - 1]; } // Overload of FixedArray::back() to return a reference to the last element // of a fixed array of const values. const_reference back() const ABSL_ATTRIBUTE_LIFETIME_BOUND { - ABSL_HARDENING_ASSERT(!empty()); + absl::base_internal::HardeningAssertNonEmpty(*this); return data()[size() - 1]; } @@ -392,8 +391,7 @@ class ABSL_ATTRIBUTE_WARN_UNUSED FixedArray { template friend H AbslHashValue(H h, const FixedArray& v) { - return H::combine(H::combine_contiguous(std::move(h), v.data(), v.size()), - hash_internal::WeaklyMixedInteger{v.size()}); + return H::combine_contiguous(std::move(h), v.data(), v.size()); } private: @@ -417,14 +415,14 @@ class ABSL_ATTRIBUTE_WARN_UNUSED FixedArray { // error: call to int __builtin___sprintf_chk(etc...) // will always overflow destination buffer [-Werror] // - template , + template , size_t InnerN = std::extent::value> struct StorageElementWrapper { InnerT array[InnerN]; }; using StorageElement = - absl::conditional_t::value, + std::conditional_t::value, StorageElementWrapper, value_type>; static pointer AsValueType(pointer ptr) { return ptr; } @@ -461,7 +459,7 @@ class ABSL_ATTRIBUTE_WARN_UNUSED FixedArray { }; using InlinedStorage = - absl::conditional_t; // Storage diff --git a/absl/container/flat_hash_map.h b/absl/container/flat_hash_map.h index bc86ced99..af9018d29 100644 --- a/absl/container/flat_hash_map.h +++ b/absl/container/flat_hash_map.h @@ -115,25 +115,29 @@ struct FlatHashMapPolicy; // absl::flat_hash_map ducks = // {{"a", "huey"}, {"b", "dewey"}, {"c", "louie"}}; // -// // Insert a new element into the flat hash map -// ducks.insert({"d", "donald"}); +// // Insert a new element into the flat hash map +// ducks.insert({"d", "donald"}); // -// // Force a rehash of the flat hash map -// ducks.rehash(0); +// // Force a rehash of the flat hash map +// ducks.rehash(0); // -// // Find the element with the key "b" -// std::string search_key = "b"; -// auto result = ducks.find(search_key); -// if (result != ducks.end()) { -// std::cout << "Result: " << result->second << std::endl; -// } -template , - class Eq = DefaultHashContainerEq, - class Allocator = std::allocator>> +// // Find the element with the key "b" +// std::string search_key = "b"; +// auto result = ducks.find(search_key); +// if (result != ducks.end()) { +// std::cout << "Result: " << result->second << std::endl; +// } +template < + class K, class V, + class Hash = + typename container_internal::FlatHashMapPolicy::DefaultHash, + class Eq = typename container_internal::FlatHashMapPolicy::DefaultEq, + class Allocator = + typename container_internal::FlatHashMapPolicy::DefaultAlloc> class ABSL_ATTRIBUTE_OWNER flat_hash_map - : public absl::container_internal::raw_hash_map< + : public absl::container_internal::InstantiateRawHashMap< absl::container_internal::FlatHashMapPolicy, Hash, Eq, - Allocator> { + Allocator>::type { using Base = typename flat_hash_map::raw_hash_map; public: @@ -158,25 +162,37 @@ class ABSL_ATTRIBUTE_OWNER flat_hash_map // // * Copy assignment operator // - // // Hash functor and Comparator are copied as well - // absl::flat_hash_map map4; - // map4 = map3; + // // Hash functor and Comparator are copied as well + // absl::flat_hash_map map4; + // map4 = map3; // // * Move constructor // // // Move is guaranteed efficient // absl::flat_hash_map map5(std::move(map4)); // + // // After the move, map4 is in a valid but unspecified state. The only + // // operations guaranteed to be safe on a moved-from map are destruction, + // // assignment, and clear(). Any other operation (e.g. size(), empty(), + // // iteration) results in undefined behavior. + // // * Move assignment operator // // // May be efficient if allocators are compatible // absl::flat_hash_map map6; // map6 = std::move(map5); // + // // Same moved-from guarantees apply to map5 after this operation. + // // * Range constructor // // std::vector> v = {{1, "a"}, {2, "b"}}; // absl::flat_hash_map map7(v.begin(), v.end()); + // + // * from_range constructor (C++23) + // + // std::vector> v = {{1, "a"}, {2, "b"}}; + // absl::flat_hash_map map8(std::from_range, v); flat_hash_map() {} using Base::Base; @@ -462,7 +478,9 @@ class ABSL_ATTRIBUTE_OWNER flat_hash_map // // Sets the number of slots in the `flat_hash_map` to the number needed to // accommodate at least `count` total elements without exceeding the current - // maximum load factor, and may rehash the container if needed. + // maximum load factor, and may rehash the container if needed. After this + // returns, it is guaranteed that `count - size()` elements can be inserted + // into the `flat_hash_map` without another rehash. using Base::reserve; // flat_hash_map::at() @@ -603,22 +621,22 @@ namespace container_internal { // Erasure and/or insertion of elements in the function is not allowed. template -decay_t c_for_each_fast(const flat_hash_map& c, - Function&& f) { +std::decay_t c_for_each_fast(const flat_hash_map& c, + Function&& f) { container_internal::ForEach(f, &c); return f; } template -decay_t c_for_each_fast(flat_hash_map& c, - Function&& f) { +std::decay_t c_for_each_fast(flat_hash_map& c, + Function&& f) { container_internal::ForEach(f, &c); return f; } template -decay_t c_for_each_fast(flat_hash_map&& c, - Function&& f) { +std::decay_t c_for_each_fast(flat_hash_map&& c, + Function&& f) { container_internal::ForEach(f, &c); return f; } @@ -635,6 +653,10 @@ struct FlatHashMapPolicy { using mapped_type = V; using init_type = std::pair; + using DefaultHash = DefaultHashContainerHash; + using DefaultEq = DefaultHashContainerEq; + using DefaultAlloc = std::allocator>; + template static void construct(Allocator* alloc, slot_type* slot, Args&&... args) { slot_policy::construct(alloc, slot, std::forward(args)...); @@ -660,10 +682,10 @@ struct FlatHashMapPolicy { std::forward(args)...); } - template + template static constexpr HashSlotFn get_hash_slot_fn() { return memory_internal::IsLayoutCompatible::value - ? &TypeErasedApplyToSlotFn + ? &TypeErasedApplyToSlotFn : nullptr; } diff --git a/absl/container/flat_hash_set.h b/absl/container/flat_hash_set.h index bf63eb598..60f661b12 100644 --- a/absl/container/flat_hash_set.h +++ b/absl/container/flat_hash_set.h @@ -114,22 +114,26 @@ struct FlatHashSetPolicy; // absl::flat_hash_set ducks = // {"huey", "dewey", "louie"}; // -// // Insert a new element into the flat hash set -// ducks.insert("donald"); +// // Insert a new element into the flat hash set +// ducks.insert("donald"); // -// // Force a rehash of the flat hash set -// ducks.rehash(0); +// // Force a rehash of the flat hash set +// ducks.rehash(0); // -// // See if "dewey" is present -// if (ducks.contains("dewey")) { -// std::cout << "We found dewey!" << std::endl; -// } -template , - class Eq = DefaultHashContainerEq, - class Allocator = std::allocator> +// // See if "dewey" is present +// if (ducks.contains("dewey")) { +// std::cout << "We found dewey!" << std::endl; +// } +template < + class T, + class Hash = typename container_internal::FlatHashSetPolicy::DefaultHash, + class Eq = typename container_internal::FlatHashSetPolicy::DefaultEq, + class Allocator = + typename container_internal::FlatHashSetPolicy::DefaultAlloc> class ABSL_ATTRIBUTE_OWNER flat_hash_set - : public absl::container_internal::raw_hash_set< - absl::container_internal::FlatHashSetPolicy, Hash, Eq, Allocator> { + : public absl::container_internal::InstantiateRawHashSet< + absl::container_internal::FlatHashSetPolicy, Hash, Eq, + Allocator>::type { using Base = typename flat_hash_set::raw_hash_set; public: @@ -154,25 +158,37 @@ class ABSL_ATTRIBUTE_OWNER flat_hash_set // // * Copy assignment operator // - // // Hash functor and Comparator are copied as well - // absl::flat_hash_set set4; - // set4 = set3; + // // Hash functor and Comparator are copied as well + // absl::flat_hash_set set4; + // set4 = set3; // // * Move constructor // // // Move is guaranteed efficient // absl::flat_hash_set set5(std::move(set4)); // + // // After the move, set4 is in a valid but unspecified state. The only + // // operations guaranteed to be safe on a moved-from set are destruction, + // // assignment, and clear(). Any other operation (e.g. size(), empty(), + // // iteration) results in undefined behavior. + // // * Move assignment operator // // // May be efficient if allocators are compatible // absl::flat_hash_set set6; // set6 = std::move(set5); // + // // Same moved-from guarantees apply to set5 after this operation. + // // * Range constructor // // std::vector v = {"a", "b"}; // absl::flat_hash_set set7(v.begin(), v.end()); + // + // * from_range constructor (C++23) + // + // std::vector v = {"a", "b"}; + // absl::flat_hash_set set8(std::from_range, v); flat_hash_set() {} using Base::Base; @@ -396,7 +412,9 @@ class ABSL_ATTRIBUTE_OWNER flat_hash_set // // Sets the number of slots in the `flat_hash_set` to the number needed to // accommodate at least `count` total elements without exceeding the current - // maximum load factor, and may rehash the container if needed. + // maximum load factor, and may rehash the container if needed. After this + // returns, it is guaranteed that `count - size()` elements can be inserted + // into the `flat_hash_set` without another rehash. using Base::reserve; // flat_hash_set::contains() @@ -506,18 +524,20 @@ namespace container_internal { // There is no guarantees on the order of the function calls. // Erasure and/or insertion of elements in the function is not allowed. template -decay_t c_for_each_fast(const flat_hash_set& c, - Function&& f) { +std::decay_t c_for_each_fast(const flat_hash_set& c, + Function&& f) { container_internal::ForEach(f, &c); return f; } template -decay_t c_for_each_fast(flat_hash_set& c, Function&& f) { +std::decay_t c_for_each_fast(flat_hash_set& c, + Function&& f) { container_internal::ForEach(f, &c); return f; } template -decay_t c_for_each_fast(flat_hash_set&& c, Function&& f) { +std::decay_t c_for_each_fast(flat_hash_set&& c, + Function&& f) { container_internal::ForEach(f, &c); return f; } @@ -533,16 +553,20 @@ struct FlatHashSetPolicy { using init_type = T; using constant_iterators = std::true_type; + using DefaultHash = DefaultHashContainerHash; + using DefaultEq = DefaultHashContainerEq; + using DefaultAlloc = std::allocator; + template static void construct(Allocator* alloc, slot_type* slot, Args&&... args) { - absl::allocator_traits::construct(*alloc, slot, - std::forward(args)...); + std::allocator_traits::construct(*alloc, slot, + std::forward(args)...); } // Return std::true_type in case destroy is trivial. template static auto destroy(Allocator* alloc, slot_type* slot) { - absl::allocator_traits::destroy(*alloc, slot); + std::allocator_traits::destroy(*alloc, slot); return IsDestructionTrivial(); } @@ -558,9 +582,9 @@ struct FlatHashSetPolicy { static size_t space_used(const T*) { return 0; } - template + template static constexpr HashSlotFn get_hash_slot_fn() { - return &TypeErasedApplyToSlotFn; + return &TypeErasedApplyToSlotFn; } }; } // namespace container_internal diff --git a/absl/container/inlined_vector.h b/absl/container/inlined_vector.h index f871b3491..020e37f46 100644 --- a/absl/container/inlined_vector.h +++ b/absl/container/inlined_vector.h @@ -47,11 +47,12 @@ #include "absl/algorithm/algorithm.h" #include "absl/base/attributes.h" +#include "absl/base/internal/hardening.h" #include "absl/base/internal/iterator_traits.h" -#include "absl/base/internal/throw_delegate.h" #include "absl/base/macros.h" #include "absl/base/optimization.h" #include "absl/base/port.h" +#include "absl/base/throw_delegate.h" #include "absl/container/internal/inlined_vector.h" #include "absl/hash/internal/weakly_mixed_integer.h" #include "absl/memory/memory.h" @@ -192,7 +193,7 @@ class ABSL_ATTRIBUTE_WARN_UNUSED InlinedVector { // allocator doesn't do anything fancy, and there is nothing on the heap // then we know it is legal for us to simply memcpy the other vector's // inlined bytes to form our copy of its elements. - if (absl::is_trivially_copy_constructible::value && + if (std::is_trivially_copy_constructible::value && std::is_same>::value && !other.storage_.GetIsAllocated()) { storage_.MemcpyFrom(other.storage_); @@ -363,14 +364,14 @@ class ABSL_ATTRIBUTE_WARN_UNUSED InlinedVector { // // Returns a `reference` to the `i`th element of the inlined vector. reference operator[](size_type i) ABSL_ATTRIBUTE_LIFETIME_BOUND { - ABSL_HARDENING_ASSERT(i < size()); + absl::base_internal::HardeningAssertLT(i, size()); return data()[i]; } // Overload of `InlinedVector::operator[](...)` that returns a // `const_reference` to the `i`th element of the inlined vector. const_reference operator[](size_type i) const ABSL_ATTRIBUTE_LIFETIME_BOUND { - ABSL_HARDENING_ASSERT(i < size()); + absl::base_internal::HardeningAssertLT(i, size()); return data()[i]; } @@ -382,8 +383,7 @@ class ABSL_ATTRIBUTE_WARN_UNUSED InlinedVector { // in both debug and non-debug builds, `std::out_of_range` will be thrown. reference at(size_type i) ABSL_ATTRIBUTE_LIFETIME_BOUND { if (ABSL_PREDICT_FALSE(i >= size())) { - base_internal::ThrowStdOutOfRange( - "`InlinedVector::at(size_type)` failed bounds check"); + ThrowStdOutOfRange("`InlinedVector::at(size_type)` failed bounds check"); } return data()[i]; } @@ -395,7 +395,7 @@ class ABSL_ATTRIBUTE_WARN_UNUSED InlinedVector { // in both debug and non-debug builds, `std::out_of_range` will be thrown. const_reference at(size_type i) const ABSL_ATTRIBUTE_LIFETIME_BOUND { if (ABSL_PREDICT_FALSE(i >= size())) { - base_internal::ThrowStdOutOfRange( + ThrowStdOutOfRange( "`InlinedVector::at(size_type) const` failed bounds check"); } return data()[i]; @@ -405,14 +405,14 @@ class ABSL_ATTRIBUTE_WARN_UNUSED InlinedVector { // // Returns a `reference` to the first element of the inlined vector. reference front() ABSL_ATTRIBUTE_LIFETIME_BOUND { - ABSL_HARDENING_ASSERT(!empty()); + absl::base_internal::HardeningAssertNonEmpty(*this); return data()[0]; } // Overload of `InlinedVector::front()` that returns a `const_reference` to // the first element of the inlined vector. const_reference front() const ABSL_ATTRIBUTE_LIFETIME_BOUND { - ABSL_HARDENING_ASSERT(!empty()); + absl::base_internal::HardeningAssertNonEmpty(*this); return data()[0]; } @@ -420,14 +420,14 @@ class ABSL_ATTRIBUTE_WARN_UNUSED InlinedVector { // // Returns a `reference` to the last element of the inlined vector. reference back() ABSL_ATTRIBUTE_LIFETIME_BOUND { - ABSL_HARDENING_ASSERT(!empty()); + absl::base_internal::HardeningAssertNonEmpty(*this); return data()[size() - 1]; } // Overload of `InlinedVector::back()` that returns a `const_reference` to the // last element of the inlined vector. const_reference back() const ABSL_ATTRIBUTE_LIFETIME_BOUND { - ABSL_HARDENING_ASSERT(!empty()); + absl::base_internal::HardeningAssertNonEmpty(*this); return data()[size() - 1]; } @@ -601,7 +601,7 @@ class ABSL_ATTRIBUTE_WARN_UNUSED InlinedVector { // NOTE: If `n` is smaller than `size()`, extra elements are destroyed. If `n` // is larger than `size()`, new elements are value-initialized. void resize(size_type n) { - ABSL_HARDENING_ASSERT(n <= max_size()); + absl::base_internal::HardeningAssertLE(n, max_size()); storage_.Resize(DefaultValueAdapter(), n); } @@ -611,7 +611,7 @@ class ABSL_ATTRIBUTE_WARN_UNUSED InlinedVector { // NOTE: if `n` is smaller than `size()`, extra elements are destroyed. If `n` // is larger than `size()`, new elements are copied-constructed from `v`. void resize(size_type n, const_reference v) { - ABSL_HARDENING_ASSERT(n <= max_size()); + absl::base_internal::HardeningAssertLE(n, max_size()); storage_.Resize(CopyValueAdapter(std::addressof(v)), n); } @@ -636,8 +636,8 @@ class ABSL_ATTRIBUTE_WARN_UNUSED InlinedVector { // the newly inserted elements. iterator insert(const_iterator pos, size_type n, const_reference v) ABSL_ATTRIBUTE_LIFETIME_BOUND { - ABSL_HARDENING_ASSERT(pos >= begin()); - ABSL_HARDENING_ASSERT(pos <= end()); + absl::base_internal::HardeningAssertGE(pos, cbegin()); + absl::base_internal::HardeningAssertLE(pos, cend()); if (ABSL_PREDICT_TRUE(n != 0)) { value_type dealias = v; @@ -677,8 +677,8 @@ class ABSL_ATTRIBUTE_WARN_UNUSED InlinedVector { EnableIfAtLeastForwardIterator = 0> iterator insert(const_iterator pos, ForwardIterator first, ForwardIterator last) ABSL_ATTRIBUTE_LIFETIME_BOUND { - ABSL_HARDENING_ASSERT(pos >= begin()); - ABSL_HARDENING_ASSERT(pos <= end()); + absl::base_internal::HardeningAssertGE(pos, cbegin()); + absl::base_internal::HardeningAssertLE(pos, cend()); if (ABSL_PREDICT_TRUE(first != last)) { return storage_.Insert( @@ -698,8 +698,8 @@ class ABSL_ATTRIBUTE_WARN_UNUSED InlinedVector { DisableIfAtLeastForwardIterator = 0> iterator insert(const_iterator pos, InputIterator first, InputIterator last) ABSL_ATTRIBUTE_LIFETIME_BOUND { - ABSL_HARDENING_ASSERT(pos >= begin()); - ABSL_HARDENING_ASSERT(pos <= end()); + absl::base_internal::HardeningAssertGE(pos, cbegin()); + absl::base_internal::HardeningAssertLE(pos, cend()); size_type index = static_cast(std::distance(cbegin(), pos)); for (size_type i = index; first != last; ++i, static_cast(++first)) { @@ -716,8 +716,8 @@ class ABSL_ATTRIBUTE_WARN_UNUSED InlinedVector { template iterator emplace(const_iterator pos, Args&&... args) ABSL_ATTRIBUTE_LIFETIME_BOUND { - ABSL_HARDENING_ASSERT(pos >= begin()); - ABSL_HARDENING_ASSERT(pos <= end()); + absl::base_internal::HardeningAssertGE(pos, cbegin()); + absl::base_internal::HardeningAssertLE(pos, cend()); value_type dealias(std::forward(args)...); // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=102329#c2 @@ -762,7 +762,7 @@ class ABSL_ATTRIBUTE_WARN_UNUSED InlinedVector { // // Destroys the element at `back()`, reducing the size by `1`. void pop_back() noexcept { - ABSL_HARDENING_ASSERT(!empty()); + absl::base_internal::HardeningAssertNonEmpty(*this); AllocatorTraits::destroy(storage_.GetAllocator(), data() + (size() - 1)); storage_.SubtractSize(1); @@ -775,8 +775,8 @@ class ABSL_ATTRIBUTE_WARN_UNUSED InlinedVector { // // NOTE: may return `end()`, which is not dereferenceable. iterator erase(const_iterator pos) ABSL_ATTRIBUTE_LIFETIME_BOUND { - ABSL_HARDENING_ASSERT(pos >= begin()); - ABSL_HARDENING_ASSERT(pos < end()); + absl::base_internal::HardeningAssertGE(pos, cbegin()); + absl::base_internal::HardeningAssertLT(pos, cend()); // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=102329#c2 // It appears that GCC thinks that since `pos` is a const pointer and may @@ -801,9 +801,9 @@ class ABSL_ATTRIBUTE_WARN_UNUSED InlinedVector { // NOTE: may return `end()`, which is not dereferenceable. iterator erase(const_iterator from, const_iterator to) ABSL_ATTRIBUTE_LIFETIME_BOUND { - ABSL_HARDENING_ASSERT(from >= begin()); - ABSL_HARDENING_ASSERT(from <= to); - ABSL_HARDENING_ASSERT(to <= end()); + absl::base_internal::HardeningAssertGE(from, cbegin()); + absl::base_internal::HardeningAssertLE(from, to); + absl::base_internal::HardeningAssertLE(to, cend()); if (ABSL_PREDICT_TRUE(from != to)) { return storage_.Erase(from, to); @@ -815,13 +815,11 @@ class ABSL_ATTRIBUTE_WARN_UNUSED InlinedVector { // `InlinedVector::clear()` // // Destroys all elements in the inlined vector, setting the size to `0` and - // deallocating any held memory. + // preserving capacity. void clear() noexcept { inlined_vector_internal::DestroyAdapter::DestroyElements( storage_.GetAllocator(), data(), size()); - storage_.DeallocateIfAllocated(); - - storage_.SetInlinedSize(0); + storage_.SetSize(0); } // `InlinedVector::reserve(...)` @@ -860,7 +858,7 @@ class ABSL_ATTRIBUTE_WARN_UNUSED InlinedVector { // Assumption check: we shouldn't be told to use memcpy to implement move // assignment unless we have trivially destructible elements and an // allocator that does nothing fancy. - static_assert(absl::is_trivially_destructible::value, ""); + static_assert(std::is_trivially_destructible::value, ""); static_assert(std::is_same>::value, ""); // Throw away our existing heap allocation, if any. There is no need to @@ -878,7 +876,7 @@ class ABSL_ATTRIBUTE_WARN_UNUSED InlinedVector { // // REQUIRES: other.storage_.GetIsAllocated() void DestroyExistingAndAdopt(InlinedVector&& other) { - ABSL_HARDENING_ASSERT(other.storage_.GetIsAllocated()); + absl::base_internal::HardeningAssert(other.storage_.GetIsAllocated()); inlined_vector_internal::DestroyAdapter::DestroyElements( storage_.GetAllocator(), data(), size()); @@ -1008,9 +1006,17 @@ bool operator>=(const absl::InlinedVector& a, // call this directly. template H AbslHashValue(H h, const absl::InlinedVector& a) { - auto size = a.size(); - return H::combine(H::combine_contiguous(std::move(h), a.data(), size), - hash_internal::WeaklyMixedInteger{size}); + return H::combine_contiguous(std::move(h), a.data(), a.size()); +} + +template +constexpr typename InlinedVector::size_type erase_if( + InlinedVector& v, Predicate pred) { + const auto it = std::remove_if(v.begin(), v.end(), std::move(pred)); + const auto removed = static_cast::size_type>( + std::distance(it, v.end())); + v.erase(it, v.end()); + return removed; } ABSL_NAMESPACE_END diff --git a/absl/container/internal/btree.h b/absl/container/internal/btree.h index ed541e758..2e53db682 100644 --- a/absl/container/internal/btree.h +++ b/absl/container/internal/btree.h @@ -53,11 +53,13 @@ #include #include #include +#include #include #include #include #include "absl/base/config.h" +#include "absl/base/internal/hardening.h" #include "absl/base/internal/raw_logging.h" #include "absl/base/macros.h" #include "absl/base/optimization.h" @@ -231,7 +233,7 @@ struct key_compare_adapter { explicit operator Compare() const { return comp(); } template >::value, int> = 0> bool operator()(const T &lhs, const U &rhs) const { @@ -247,7 +249,7 @@ struct key_compare_adapter { template < typename T, typename U, - absl::enable_if_t, + std::enable_if_t, absl::weak_ordering>::value, int> = 0> absl::weak_ordering operator()(const T &lhs, const U &rhs) const { @@ -270,7 +272,7 @@ struct key_compare_adapter { return lhs_comp_rhs; } }; - using type = absl::conditional_t< + using type = std::conditional_t< std::is_base_of::value, Compare, checked_compare>; }; @@ -330,11 +332,11 @@ template struct prefers_linear_node_search : std::false_type {}; template struct has_linear_node_search_preference< - T, absl::void_t> + T, std::void_t> : std::true_type {}; template struct prefers_linear_node_search< - T, absl::void_t> + T, std::void_t> : T::absl_btree_prefer_linear_node_search {}; template @@ -377,7 +379,7 @@ struct common_params : common_policy_traits { // this, then there will be cascading compilation failures that are confusing // for users. using key_compare = - absl::conditional_t(), + std::conditional_t(), Compare, typename key_compare_adapter::type>; @@ -406,7 +408,7 @@ struct common_params : common_policy_traits { using const_reference = const value_type &; using value_compare = - absl::conditional_t, original_key_compare>; using is_map_container = std::integral_constant; @@ -438,7 +440,7 @@ struct common_params : common_policy_traits { // This is an integral type large enough to hold as many slots as will fit a // node of TargetNodeSize bytes. using node_count_type = - absl::conditional_t<(kNodeSlotSpace / sizeof(slot_type) > + std::conditional_t<(kNodeSlotSpace / sizeof(slot_type) > (std::numeric_limits::max)()), uint16_t, uint8_t>; // NOLINT }; @@ -1119,7 +1121,7 @@ class btree_iterator : private btree_iterator_generation_info { using slot_type = typename params_type::slot_type; // In sets, all iterators are const. - using iterator = absl::conditional_t< + using iterator = std::conditional_t< is_map_container::value, btree_iterator, btree_iterator>; @@ -1146,7 +1148,7 @@ class btree_iterator : private btree_iterator_generation_info { // const_iterator, but it specifically avoids hiding the copy constructor so // that the trivial one will be used when possible. template , iterator>::value && std::is_same::value, int> = 0> @@ -1200,12 +1202,16 @@ class btree_iterator : private btree_iterator_generation_info { // Accessors for the key/value the iterator is pointing at. reference operator*() const { - ABSL_HARDENING_ASSERT(node_ != nullptr); + absl::base_internal::HardeningAssertNonNull(node_); assert_valid_generation(node_); - ABSL_HARDENING_ASSERT(position_ >= node_->start()); + absl::base_internal::HardeningAssertGE(position_, + static_cast(node_->start())); if (position_ >= node_->finish()) { - ABSL_HARDENING_ASSERT(!IsEndIterator() && "Dereferencing end() iterator"); - ABSL_HARDENING_ASSERT(position_ < node_->finish()); + // If this assertion fails, we have tried to dereference an end() + // iterator. + absl::base_internal::HardeningAssert(!IsEndIterator()); + absl::base_internal::HardeningAssertLT(position_, + static_cast(node_->finish())); } return node_->value(static_cast(position_)); } @@ -1252,7 +1258,7 @@ class btree_iterator : private btree_iterator_generation_info { // NOTE: the const_cast is safe because this constructor is only called by // non-const methods and the container owns the nodes. template , const_iterator>::value && std::is_same::value, int> = 0> @@ -1262,10 +1268,11 @@ class btree_iterator : private btree_iterator_generation_info { position_(other.position_) {} bool Equals(const const_iterator other) const { - ABSL_HARDENING_ASSERT(((node_ == nullptr && other.node_ == nullptr) || - (node_ != nullptr && other.node_ != nullptr)) && - "Comparing default-constructed iterator with " - "non-default-constructed iterator."); + absl::base_internal::HardeningAssert( + ((node_ == nullptr && other.node_ == nullptr) || + (node_ != nullptr && other.node_ != nullptr)) && + "Comparing default-constructed iterator with " + "non-default-constructed iterator."); // Note: we use assert instead of ABSL_HARDENING_ASSERT here because this // changes the complexity of Equals from O(1) to O(log(N) + log(M)) where // N/M are sizes of the containers containing node_/other.node_. @@ -2221,7 +2228,7 @@ btree_iterator &btree_iterator::increment_n_slow( node = node->parent(); } if (position == node->finish()) { - ABSL_HARDENING_ASSERT(n == 0); + absl::base_internal::HardeningAssert(n == 0); return *this = save; } } @@ -2258,7 +2265,8 @@ btree_iterator &btree_iterator::decrement_n_slow( position = node->position() - 1; node = node->parent(); } - ABSL_HARDENING_ASSERT(position >= node->start()); + absl::base_internal::HardeningAssertGE(position, + static_cast(node->start())); } } else { --n; @@ -2489,7 +2497,7 @@ auto btree

::operator=(const btree &other) -> btree & { clear(); *mutable_key_comp() = other.key_comp(); - if (absl::allocator_traits< + if (std::allocator_traits< allocator_type>::propagate_on_container_copy_assignment::value) { *mutable_allocator() = other.allocator(); } @@ -2505,7 +2513,7 @@ auto btree

::operator=(btree &&other) noexcept -> btree & { clear(); using std::swap; - if (absl::allocator_traits< + if (std::allocator_traits< allocator_type>::propagate_on_container_move_assignment::value) { swap(root_, other.root_); // Note: `rightmost_` also contains the allocator and the key comparator. @@ -2681,7 +2689,7 @@ void btree

::clear() { template void btree

::swap(btree &other) { using std::swap; - if (absl::allocator_traits< + if (std::allocator_traits< allocator_type>::propagate_on_container_swap::value) { // Note: `rightmost_` also contains the allocator and the key comparator. swap(rightmost_, other.rightmost_); diff --git a/absl/container/internal/btree_container.h b/absl/container/internal/btree_container.h index 21f00ae41..d4ce52356 100644 --- a/absl/container/internal/btree_container.h +++ b/absl/container/internal/btree_container.h @@ -18,11 +18,12 @@ #include #include #include +#include #include #include #include "absl/base/attributes.h" -#include "absl/base/internal/throw_delegate.h" +#include "absl/base/throw_delegate.h" #include "absl/container/internal/btree.h" // IWYU pragma: export #include "absl/container/internal/common.h" #include "absl/hash/internal/weakly_mixed_integer.h" @@ -80,8 +81,8 @@ class btree_container { explicit btree_container(const allocator_type &alloc) : tree_(key_compare(), alloc) {} - btree_container(const btree_container &other) - : btree_container(other, absl::allocator_traits:: + btree_container(const btree_container& other) + : btree_container(other, std::allocator_traits:: select_on_container_copy_construction( other.get_allocator())) {} btree_container(const btree_container &other, const allocator_type &alloc) @@ -412,8 +413,8 @@ class btree_set_container : public btree_container { // `this`, it is left unmodified in `src`. template < typename T, - typename absl::enable_if_t< - absl::conjunction< + typename std::enable_if_t< + std::conjunction< std::is_same, std::is_same, std::is_same { template < typename T, - typename absl::enable_if_t< - absl::conjunction< + typename std::enable_if_t< + std::conjunction< std::is_same, std::is_same, std::is_same { typename Tree::params_type::mapped_type, M>>; template using LifetimeBoundKV = - absl::conjunction>, - LifetimeBoundV>; + std::conjunction>, + LifetimeBoundV>; public: using key_type = typename Tree::key_type; @@ -640,27 +641,25 @@ class btree_map_container : public btree_set_container { } template >()> mapped_type &operator[](key_arg &&k) ABSL_ATTRIBUTE_LIFETIME_BOUND { - return try_emplace(std::forward(k)).first->second; + return try_emplace(std::forward>(k)).first->second; } template > = 0> mapped_type &operator[](key_arg &&k ABSL_INTERNAL_ATTRIBUTE_CAPTURED_BY( this)) ABSL_ATTRIBUTE_LIFETIME_BOUND { - return this->template operator[](std::forward(k)); + return this->template operator[](std::forward>(k)); } template mapped_type &at(const key_arg &key) ABSL_ATTRIBUTE_LIFETIME_BOUND { auto it = this->find(key); - if (it == this->end()) - base_internal::ThrowStdOutOfRange("absl::btree_map::at"); + if (it == this->end()) ThrowStdOutOfRange("absl::btree_map::at"); return it->second; } template const mapped_type &at(const key_arg &key) const ABSL_ATTRIBUTE_LIFETIME_BOUND { auto it = this->find(key); - if (it == this->end()) - base_internal::ThrowStdOutOfRange("absl::btree_map::at"); + if (it == this->end()) ThrowStdOutOfRange("absl::btree_map::at"); return it->second; } @@ -672,27 +671,36 @@ class btree_map_container : public btree_set_container { std::pair insert_or_assign_impl(K &&k, M &&obj) { const std::pair ret = this->tree_.insert_unique(k, std::forward(k), std::forward(obj)); - if (!ret.second) ret.first->second = std::forward(obj); + if (!ret.second) { + // NOLINTNEXTLINE(bugprone-use-after-move) + ret.first->second = std::forward(obj); + } return ret; } template iterator insert_or_assign_hint_impl(const_iterator hint, K &&k, M &&obj) { const std::pair ret = this->tree_.insert_hint_unique( iterator(hint), k, std::forward(k), std::forward(obj)); - if (!ret.second) ret.first->second = std::forward(obj); + if (!ret.second) { + // NOLINTNEXTLINE(bugprone-use-after-move) + ret.first->second = std::forward(obj); + } return ret.first; } template std::pair try_emplace_impl(K &&k, Args &&... args) { return this->tree_.insert_unique( + // NOLINTNEXTLINE(bugprone-use-after-move) k, std::piecewise_construct, std::forward_as_tuple(std::forward(k)), std::forward_as_tuple(std::forward(args)...)); } template iterator try_emplace_hint_impl(const_iterator hint, K &&k, Args &&... args) { return this->tree_ - .insert_hint_unique(iterator(hint), k, std::piecewise_construct, + .insert_hint_unique(iterator(hint), + // NOLINTNEXTLINE(bugprone-use-after-move) + k, std::piecewise_construct, std::forward_as_tuple(std::forward(k)), std::forward_as_tuple(std::forward(args)...)) .first; @@ -817,8 +825,8 @@ class btree_multiset_container : public btree_container { // Moves all elements from `src` into `this`. template < typename T, - typename absl::enable_if_t< - absl::conjunction< + typename std::enable_if_t< + std::conjunction< std::is_same, std::is_same, std::is_same { template < typename T, - typename absl::enable_if_t< - absl::conjunction< + typename std::enable_if_t< + std::conjunction< std::is_same, std::is_same, std::is_same +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "absl/base/attributes.h" +#include "absl/base/config.h" +#include "absl/base/macros.h" +#include "absl/container/internal/layout.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { + +// ChunkedQueueBlock defines a node in a forward list of uninitialized storage +// of size T's. The user is responsible for constructing and destroying T's in +// said storage. +// +// ChunkedQueueBlock::New(size) returns said node, with at least size_hint T's +// of uninitialized storage. +template +class ChunkedQueueBlock { + private: + using ChunkedQueueBlockAllocator = typename std::allocator_traits< + Allocator>::template rebind_alloc; + using ByteAllocator = + typename std::allocator_traits::template rebind_alloc; + + public: + // NB, instances of this must not be created or destroyed directly, only via + // the New() and Delete() methods. (This notionally-private constructor is + // public only to allow access from allocator types used by New().) + explicit ChunkedQueueBlock(size_t size) + : next_(nullptr), limit_(start() + size) {} + + // Must be deleted by ChunkedQueueBlock::Delete. + static ChunkedQueueBlock* New(size_t size_hint, Allocator* alloc) { // NOLINT + ABSL_ASSERT(size_hint >= size_t{1}); + size_t allocation_bytes = AllocSize(size_hint); + void* mem; + std::tie(mem, allocation_bytes) = Allocate(allocation_bytes, alloc); + const size_t element_count = + (allocation_bytes - start_offset()) / sizeof(T); + ChunkedQueueBlock* as_block = static_cast(mem); + ChunkedQueueBlockAllocator block_alloc(*alloc); + std::allocator_traits::construct( + block_alloc, as_block, element_count); + return as_block; + } + + static void Delete(ChunkedQueueBlock* ptr, Allocator* alloc) { + const size_t allocation_bytes = AllocSize(ptr->size()); + ChunkedQueueBlockAllocator block_alloc(*alloc); + std::allocator_traits::destroy(block_alloc, + ptr); + if constexpr (std::is_same_v>) { +#ifdef __STDCPP_DEFAULT_NEW_ALIGNMENT__ + if (alignment() > __STDCPP_DEFAULT_NEW_ALIGNMENT__) { + ::operator delete(ptr +#ifdef __cpp_sized_deallocation + , + allocation_bytes +#endif + , + std::align_val_t(alignment())); + return; + } +#endif + ::operator delete(ptr); + } else { + void* mem = ptr; + ByteAllocator byte_alloc(*alloc); + std::allocator_traits::deallocate( + byte_alloc, static_cast(mem), allocation_bytes); + } + } + + ChunkedQueueBlock* next() const { return next_; } + void set_next(ChunkedQueueBlock* next) { next_ = next; } + T* start() { + return reinterpret_cast(reinterpret_cast(this) + + start_offset()); + } + T* limit() { return limit_; } + size_t size() { return limit() - start(); } + + static constexpr size_t block_size_from_bytes(size_t bytes) { + return bytes <= static_cast(start_offset()) + ? size_t{1} + : elements_in_bytes(bytes - start_offset()); + } + + private: + ChunkedQueueBlock(const ChunkedQueueBlock&) = delete; + ChunkedQueueBlock& operator=(const ChunkedQueueBlock&) = delete; + + // The byte size to allocate to ensure space for `min_element_count` elements. + static constexpr size_t AllocSize(size_t min_element_count) { + return absl::container_internal::Layout( + 1, min_element_count) + .AllocSize(); + } + + static constexpr ptrdiff_t start_offset() { + return absl::container_internal::Layout(1, 1) + .template Offset<1>(); + } + + static constexpr size_t alignment() { + return absl::container_internal::Layout(1, 1) + .Alignment(); + } + + static constexpr size_t elements_in_bytes(size_t bytes) { + return (bytes + sizeof(T) - 1) / sizeof(T); + } + + static std::pair Allocate(size_t allocation_bytes, + Allocator* alloc) { + // If we're using the default allocator, then we can use new. + void* mem; + if constexpr (std::is_same_v>) { + // Older GCC versions have an unused variable warning on `alloc` inside + // this constexpr branch. + static_cast(alloc); +#ifdef __STDCPP_DEFAULT_NEW_ALIGNMENT__ + if (alignment() > __STDCPP_DEFAULT_NEW_ALIGNMENT__) { + // Align the allocation to respect alignof(T). + mem = ::operator new(allocation_bytes, std::align_val_t(alignment())); + return {mem, allocation_bytes}; + } +#endif + mem = ::operator new(allocation_bytes); + } else { + ByteAllocator byte_alloc(*alloc); + mem = std::allocator_traits::allocate(byte_alloc, + allocation_bytes); + } + return {mem, allocation_bytes}; + } + + ChunkedQueueBlock* next_; + T* limit_; +}; + +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_INTERNAL_CHUNKED_QUEUE_H_ diff --git a/absl/container/internal/common.h b/absl/container/internal/common.h index 5ef6c569a..429bf31bb 100644 --- a/absl/container/internal/common.h +++ b/absl/container/internal/common.h @@ -15,7 +15,11 @@ #ifndef ABSL_CONTAINER_INTERNAL_COMMON_H_ #define ABSL_CONTAINER_INTERNAL_COMMON_H_ +#include #include +#include +#include +#include #include #include "absl/meta/type_traits.h" @@ -54,7 +58,7 @@ template using EnableIf = std::enable_if_t; template -using HasValue = std::conditional_t>; +using HasValue = std::conditional_t>; template struct IfRRef { @@ -71,7 +75,7 @@ struct IfRRef { template struct IsTransparent : std::false_type {}; template -struct IsTransparent> +struct IsTransparent> : std::true_type {}; template @@ -143,7 +147,7 @@ class node_handle_base { void reset() { assert(alloc_.has_value()); - alloc_ = absl::nullopt; + alloc_ = std::nullopt; } slot_type* slot() const { @@ -153,7 +157,7 @@ class node_handle_base { allocator_type* alloc() { return std::addressof(*alloc_); } private: - absl::optional alloc_ = {}; + std::optional alloc_ = {}; alignas(slot_type) mutable unsigned char slot_space_[sizeof(slot_type)] = {}; }; @@ -179,7 +183,7 @@ class node_handle : public node_handle_base { // For maps. template class node_handle> + std::void_t> : public node_handle_base { using Base = node_handle_base; using slot_type = typename PolicyTraits::slot_type; @@ -243,6 +247,54 @@ struct InsertReturnType { NodeType node; }; +// Utilities to strip redundant template parameters from the underlying +// implementation types. +// We use a variadic pack (ie Params...) to specify required prefix of types for +// non-default types, and then we use GetFromListOr to select the provided types +// or the default ones otherwise. +// +// These default types do not contribute information for debugging and just +// bloat the binary. +// Removing the redundant tail types reduces mangled names and stringified +// function names like __PRETTY_FUNCTION__. +// +// How to use: +// 1. Define a template with `typename ...Params` +// 2. Instantiate it via `ApplyWithoutDefaultSuffix<>` to only pass the minimal +// set of types. +// 3. Inside the template use `GetFromListOr` to map back from the existing +// `Params` list to the actual types, filling the gaps when types are +// missing. + +template +using GetFromListOr = std::tuple_element_t<(std::min)(N, sizeof...(Params)), + std::tuple>; + +template +struct TypeList { + template