Make GHA tests blocking. PiperOrigin-RevId: 998975715
diff --git a/.bazelrc b/.bazelrc new file mode 100644 index 0000000..b1c92dd --- /dev/null +++ b/.bazelrc
@@ -0,0 +1,86 @@ +# 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. + +common --show_timestamps + +# Retry longer to avoid flakes when fetching external dependencies. +common --http_connector_attempts=10 +common --http_timeout_scaling=2.0 + +# Link GoogleTest against Abseil. +build --define=absl=1 + +test --keep_going +test --test_env=GTEST_INSTALL_FAILURE_SIGNAL_HANDLER=1 +test --test_env=TZDIR=absl/time/internal/cctz/testdata/zoneinfo +test --test_output=errors +test --test_tag_filters=-benchmark + +# Configure our Linux Docker toolchain setup. +common --extra_toolchains=@docker_toolchain//:all +build:linux --cxxopt=-std=c++17 --host_cxxopt=-std=c++17 +build:linux --copt=-fno-exceptions +build:linux --copt=-Werror +build:linux --copt=-DGTEST_REMOVE_LEGACY_TEST_CASEAPI_=1 +build:linux --features=external_include_paths +build:linux --per_file_copt=external/.*@-w +build:linux --host_per_file_copt=external/.*@-w + +build:gcc --config=linux --platforms=//ci:linux_gcc +build:gcc-exceptions --config=gcc --copt=-fexceptions +build:clang --config=linux --platforms=//ci:linux_clang --conlyopt=-Wno-unused-command-line-argument +build:clang-exceptions --config=clang --copt=-fexceptions +build:clang-arm --config=linux --platforms=//ci:linux_arm_clang --conlyopt=-Wno-unused-command-line-argument +build:libstdc++ --config=clang --features=libstdc++ --copt=-march=haswell + +# Sanitizer configurations. +build:ubsan --features=ubsan +build:ubsan --copt=-fsanitize=float-divide-by-zero,nullability,undefined +build:ubsan --copt=-fno-sanitize-recover=float-divide-by-zero,nullability,undefined +build:ubsan --copt=-fno-sanitize-blacklist +test:ubsan --test_env=UBSAN_OPTIONS=print_stacktrace=1 + +build:asan --config=clang --config=ubsan --features=asan +build:asan --copt=-D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_DEBUG +build:asan --linkopt=-fsanitize-link-c++-runtime +test:asan --test_tag_filters=-benchmark,-noasan + +build:tsan --config=clang --features=tsan +build:tsan --copt=-fno-sanitize-blacklist +build:tsan --build_tag_filters=-notsan +test:tsan --test_tag_filters=-benchmark,-notsan + +# Remote build configuration for CI. +build:remote-base --define=EXECUTOR=remote +build:remote-base --remote_timeout=1200 --bes_timeout=600s +build:remote-base --jobs=50 +build:remote-base --remote_download_minimal +# Retry up for up to 15 minutes under heavy load (exponential backoff). +build:remote-base --remote_retries=20 --remote_retry_max_delay=60s + +build:remote --config=remote-base +build:remote --extra_execution_platforms=//ci:rbe +build:remote --host_platform=//ci:rbe +build:remote --platforms=//ci:rbe +build:remote --remote_cache=grpcs://us-east1-remotebuildexecution.googleapis.com +build:remote --remote_executor=grpcs://us-east1-remotebuildexecution.googleapis.com +build:remote --remote_instance_name=projects/google.com:absl-177019/instances/absl-ci-east + +build:remote-arm --config=remote-base +build:remote-arm --extra_execution_platforms=//ci:rbe-arm +build:remote-arm --host_platform=//ci:rbe-arm +build:remote-arm --platforms=//ci:rbe-arm +build:remote-arm --remote_cache=grpcs://us-central1-remotebuildexecution.googleapis.com +build:remote-arm --remote_executor=grpcs://us-central1-remotebuildexecution.googleapis.com +build:remote-arm --remote_instance_name=projects/google.com:absl-177019/instances/gloop-ci-central
diff --git a/.github/workflows/bazel.yml b/.github/workflows/bazel.yml new file mode 100644 index 0000000..b54e910 --- /dev/null +++ b/.github/workflows/bazel.yml
@@ -0,0 +1,113 @@ +# 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. + +name: Bazel Tests + +on: + workflow_call: + inputs: + os: + description: "GitHub Actions runner OS (e.g. ubuntu-24.04, macos-latest, windows-2022)" + required: true + type: string + toolchain: + description: "Bazel toolchain config (--config=<toolchain>)" + required: true + type: string + remote_config: + description: "Bazel remote config (--config=<remote_config>)" + required: false + default: "remote" + type: string + container_image: + description: "Docker container image to run in (empty string for host execution)" + required: false + default: "" + type: string + alternate_options: + description: "Whether to use ci/absl_alternate_options.h" + required: false + default: false + type: boolean + stds: + description: "JSON array of C++ standards to test" + required: false + default: '["c++17", "c++20", "c++23"]' + type: string + compilation_modes: + description: "JSON array of Bazel compilation modes to test" + required: false + default: '["opt", "fastbuild"]' + type: string + +permissions: + contents: read + +jobs: + test: + runs-on: ${{ inputs.os }} + name: (${{ matrix.std }}, ${{ matrix.compilation_mode }}) + container: + image: ${{ inputs.container_image }} + options: --cap-add=SYS_PTRACE + credentials: + # TODO(b/573304451): Switch to Workload Identity Federation to avoid the need for secrets. + username: _json_key + password: ${{ secrets.GAR_SERVICE_ACCOUNT }} + strategy: + fail-fast: false # Don't cancel all jobs if one fails. + matrix: + std: ${{ fromJSON(inputs.stds) }} + compilation_mode: ${{ fromJSON(inputs.compilation_modes) }} + defaults: + run: + shell: bash + env: + BAZEL_LLVM: C:\Program Files\LLVM + MSYS2_ARG_CONV_EXCL: "*" + USE_BAZEL_VERSION: 9.x + steps: + - name: Checkout + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # 7.0.0 + + - name: Set up alternate options + if: ${{ inputs.alternate_options }} + run: cp ci/absl_alternate_options.h absl/base/options.h + + - name: Setup continuous run + if: ${{ github.event_name == 'schedule' }} + # Force builds and tests to run on scheduled runs. + run: echo "EXTRA_BAZEL_ARGS=--cache_test_results=no --action_env=FORCE_REBUILD=1" >> $GITHUB_ENV + + - name: Set up Bazel and caches + uses: bazel-contrib/setup-bazel@c5acdfb288317d0b5c0bbd7a396a3dc868bb0f86 # 0.19.0 + with: + cache-save: true + bazelisk-cache: true + external-cache: true + repository-cache: true + google-credentials: ${{ secrets.GAR_SERVICE_ACCOUNT }} + bazelrc: common --config=${{ inputs.remote_config }} + + - name: Fetch all external dependencies + run: bazel fetch //... + + - name: Run tests + run: > + bazel test ... + --config=${{ inputs.toolchain }} + --cxxopt=${{ startsWith(inputs.os, 'windows') && '/std:' || '-std=' }}${{ matrix.std }} + --host_cxxopt=${{ startsWith(inputs.os, 'windows') && '/std:' || '-std=' }}${{ matrix.std }} + --compilation_mode=${{ matrix.compilation_mode }} + ${{ env.EXTRA_BAZEL_ARGS }}
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..768f1ca --- /dev/null +++ b/.github/workflows/ci.yml
@@ -0,0 +1,120 @@ +# 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. + +name: Tests + +on: + # continuous runs + schedule: + # Run every hour + - cron: "0 * * * *" + + # postsubmit runs + push: + branches: + - master + - lts_* + + # presubmit runs + pull_request: + branches: + - master + - lts_* + + # manual runs + workflow_dispatch: + +permissions: + contents: read + +# Avoid running twice at the same time over the same changes. +concurrency: + group: ${{ github.event_name }}-${{ github.workflow }}-${{ github.head_ref || github.ref }} + cancel-in-progress: ${{ contains(fromJSON('["pull_request", "workflow_dispatch", "schedule"]'), github.event_name) }} + +jobs: + # Mirrors the 10 Linux "latest" Bazel Kokoro jobs (parallelized to 1 job per + # Bazel invocation). Not yet covered here: + # - "Linux GCC/libstdc++ (floor) Bazel" (requires a floor GCC Docker image) + # - CMake, macOS, and Windows Kokoro jobs + linux: + name: Linux ${{ matrix.config.name }} Bazel + strategy: + fail-fast: false # Don't cancel all jobs if one fails. + matrix: + config: + # linux_clang-latest_libcxx_bazel.cfg + - name: Clang/libc++ + toolchain: clang + # linux_clang-latest_libcxx_exceptions_bazel.cfg (fastbuild only) + - name: Clang/libc++ -fexceptions + toolchain: clang-exceptions + compilation_modes: '["fastbuild"]' + # linux_clang-latest_libcxx_alternate_options_bazel.cfg + - name: Clang/libc++ alternate options + toolchain: clang + alternate_options: true + # linux_clang-latest_libcxx_asan_bazel.cfg (fastbuild only) + - name: Clang/libc++ ASAN + toolchain: asan + compilation_modes: '["fastbuild"]' + # linux_clang-latest_libcxx_tsan_bazel.cfg (fastbuild only) + - name: Clang/libc++ TSAN + toolchain: tsan + compilation_modes: '["fastbuild"]' + # linux_clang-latest_libstdcxx_bazel.cfg + - name: Clang/libstdc++ + toolchain: libstdc++ + # linux_gcc-latest_libstdcxx_bazel.cfg + - name: GCC/libstdc++ + toolchain: gcc + # linux_gcc-latest_libstdcxx_exceptions_bazel.cfg (fastbuild only) + - name: GCC/libstdc++ -fexceptions + toolchain: gcc-exceptions + compilation_modes: '["fastbuild"]' + # linux_gcc-latest_libstdcxx_alternate_options_bazel.cfg + - name: GCC/libstdc++ alternate options + toolchain: gcc + alternate_options: true + # linux_arm_clang-latest_libcxx_bazel.cfg (c++17 and c++20 only) + - name: ARM64 Clang/libc++ + toolchain: clang-arm + arch_suffix: -arm + stds: '["c++17", "c++20"]' + uses: ./.github/workflows/bazel.yml + secrets: inherit + with: + os: ubuntu-24.04${{ matrix.config.arch_suffix }} + toolchain: ${{ matrix.config.toolchain }} + remote_config: remote${{ matrix.config.arch_suffix }} + container_image: gcr.io/google.com/absl-177019/gloop-linux-toolchain-latest@sha256:a820c79d3f356a64db121a4ee16c4408df18f9d2c8bf6c6abfae21666ac003ba + alternate_options: ${{ matrix.config.alternate_options || false }} + stds: ${{ matrix.config.stds || '["c++17", "c++20", "c++23"]' }} + compilation_modes: ${{ matrix.config.compilation_modes || '["opt", "fastbuild"]' }} + + # Provide a single predictably-named job for copybara to look for + all-blocking-tests: + name: All Blocking Tests + needs: [linux] + runs-on: ubuntu-latest + steps: + - name: Check for cancellations + if: ${{ contains(join(needs.*.result, ' '), 'cancelled') }} + run: echo "One or more tests were cancelled. If this was a presubmit job cancelled by a submit job, you can ignore this." && exit 1 + - name: Check for failures + if: ${{ contains(join(needs.*.result, ' '), 'failure') }} + run: echo "One or more tests failed. Please check the logs above and fix the issues." && exit 1 + # This workflow must run even if one or more of the dependent workflows + # failed. + if: always()
diff --git a/MODULE.bazel b/MODULE.bazel index 00ab4c3..d4096d2 100644 --- a/MODULE.bazel +++ b/MODULE.bazel
@@ -50,3 +50,9 @@ version = "20260708.rc1", repo_name = "do_not_use_for_gloop_visibility_only", ) + +bazel_dep(name = "docker_toolchain", version = "0.1", dev_dependency = True) +local_path_override( + module_name = "docker_toolchain", + path = "ci/docker_toolchain", +)
diff --git a/absl/base/attributes.h b/absl/base/attributes.h index 1e4c7c3..601be76 100644 --- a/absl/base/attributes.h +++ b/absl/base/attributes.h
@@ -85,16 +85,16 @@ // Note: As the GCC manual states, "[s]ince non-static C++ methods // have an implicit 'this' argument, the arguments of such methods // should be counted from two, not one." -#if ABSL_HAVE_ATTRIBUTE(format) || (defined(__GNUC__) && !defined(__clang__)) -#define ABSL_PRINTF_ATTRIBUTE(string_index, first_to_check) \ - __attribute__((__format__(__printf__, string_index, first_to_check))) #if ABSL_HAVE_CPP_ATTRIBUTE(gnu::format) +#define ABSL_PRINTF_ATTRIBUTE(string_index, first_to_check) \ + [[gnu::format(printf, string_index, first_to_check)]] #define ABSL_SCANF_ATTRIBUTE(string_index, first_to_check) \ [[gnu::format(scanf, string_index, first_to_check)]] -#else +#elif ABSL_HAVE_ATTRIBUTE(format) || (defined(__GNUC__) && !defined(__clang__)) +#define ABSL_PRINTF_ATTRIBUTE(string_index, first_to_check) \ + __attribute__((__format__(__printf__, string_index, first_to_check))) #define ABSL_SCANF_ATTRIBUTE(string_index, first_to_check) \ __attribute__((__format__(__scanf__, string_index, first_to_check))) -#endif #else #define ABSL_PRINTF_ATTRIBUTE(string_index, first_to_check) #define ABSL_SCANF_ATTRIBUTE(string_index, first_to_check) @@ -1117,4 +1117,52 @@ #define ABSL_ATTRIBUTE_WARN_UNUSED #endif +// ABSL_UNSAFE_BUFFERS() +// +// Wraps code that performs unsafe buffer accesses or pointer arithmetic. Used +// to suppress warnings when `-Wunsafe-buffer-usage` is enabled. +// +// **WARNING**: This macro should be used when unavoidable. Applying it means +// the compiler is unable to prove that your code avoids out-of-bounds reads or +// writes, forcing reliance on manual review. Over time, code evolves and +// undocumented assumptions can silently break, introducing vulnerabilities. +// Always prioritize refactoring to safer alternatives (e.g. std::array, +// absl::Span, or bounds-checked containers) over using this suppression. +// +// When using this macro is unavoidable, restrict its scope to the smallest +// possible expression or statement. You must also add a `// SAFETY: ...` +// comment explaining why the operation is actually safe. +// Justifications include: +// * Explicit bounds checks (e.g., `CHECK()`) immediately preceding the access. +// * Invariants strictly enforced by surrounding types or functions. +// * Preconditions required of the caller (though this is heavily discouraged). +// +// Example (expression): +// +// // SAFETY: `buf` is NUL-terminated so `strdup` won't read past the end. +// return ABSL_UNSAFE_BUFFERS(strdup(buf)); +// +// Example (single-line statement): +// +// // SAFETY: `offset` is bounds-checked above to be within `buffer`. +// ABSL_UNSAFE_BUFFERS(buffer += offset;) +// +// Example (multi-line statement): +// +// // SAFETY: `offset` is bounds-checked above to be within `buffer`. +// ABSL_UNSAFE_BUFFERS( +// char* p = buffer + offset; +// p[0] = 'a'; +// ) +// +// Note: When wrapping a multi-line statement, placing a semicolon after the +// closing parenthesis may produce an extra semicolon warning. +#if defined(__clang__) +#define ABSL_UNSAFE_BUFFERS(...) \ + _Pragma("clang unsafe_buffer_usage begin") \ + __VA_ARGS__ _Pragma("clang unsafe_buffer_usage end") +#else +#define ABSL_UNSAFE_BUFFERS(...) __VA_ARGS__ +#endif + #endif // ABSL_BASE_ATTRIBUTES_H_
diff --git a/absl/base/attributes_test.cc b/absl/base/attributes_test.cc index 058d1be..e87b66e 100644 --- a/absl/base/attributes_test.cc +++ b/absl/base/attributes_test.cc
@@ -54,4 +54,22 @@ Agg good1 [[maybe_unused]] = {ImplicitlyConstructible("hello")}; } +TEST(Attributes, UnsafeBuffers) { + int buffer[10] = {0}; + int offset = 1; + + // Expression example + int* p1 = ABSL_UNSAFE_BUFFERS(buffer + offset); + EXPECT_EQ(p1, &buffer[1]); + + // Single-line statement example + int* p2 = buffer; + ABSL_UNSAFE_BUFFERS(p2 += offset;) + EXPECT_EQ(p2, &buffer[1]); + + // Multi-line statement example + ABSL_UNSAFE_BUFFERS(int* p3 = buffer + offset; p3[0] = 1;) + EXPECT_EQ(buffer[1], 1); +} + } // namespace
diff --git a/absl/crc/internal/crc_x86_arm_combined.cc b/absl/crc/internal/crc_x86_arm_combined.cc index 95221e4..d6f42d9 100644 --- a/absl/crc/internal/crc_x86_arm_combined.cc +++ b/absl/crc/internal/crc_x86_arm_combined.cc
@@ -1084,6 +1084,7 @@ 3, 1, PclmulStreamType::PCLMUL>(); case CpuType::kAmdMilan: case CpuType::kAmdGenoa: + case CpuType::kAmdRyzenV3000: case CpuType::kAmdSiena: case CpuType::kAmdTurin: case CpuType::kAmdVenice:
diff --git a/absl/functional/BUILD.bazel b/absl/functional/BUILD.bazel index d1b46c7..0cad083 100644 --- a/absl/functional/BUILD.bazel +++ b/absl/functional/BUILD.bazel
@@ -124,6 +124,7 @@ linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":bind_front", + "//absl/base:config", "//absl/memory", "@googletest//:gtest", "@googletest//:gtest_main",
diff --git a/absl/functional/CMakeLists.txt b/absl/functional/CMakeLists.txt index 1ddfa29..231c9dd 100644 --- a/absl/functional/CMakeLists.txt +++ b/absl/functional/CMakeLists.txt
@@ -105,6 +105,7 @@ ${ABSL_DEFAULT_COPTS} DEPS absl::bind_front + absl::config absl::memory GTest::gmock_main )
diff --git a/absl/functional/bind_front.h b/absl/functional/bind_front.h index 45ca4fc..06b6e52 100644 --- a/absl/functional/bind_front.h +++ b/absl/functional/bind_front.h
@@ -21,10 +21,11 @@ // it does not require the use of argument placeholders. The simpler syntax of // `absl::bind_front()` allows you to avoid known misuses with `std::bind()`. // -// `absl::bind_front()` is meant as a drop-in replacement for C++20's upcoming -// `std::bind_front()`, which similarly resolves these issues with -// `std::bind()`. Both `bind_front()` alternatives, unlike `std::bind()`, allow -// partial function application. (See +// `absl::bind_front()` is meant as a drop-in replacement for C++20's +// `std::bind_front(func, args...)` and C++26's +// `std::bind_front<ConstFn>(args...)`, which similarly resolve these issues +// with `std::bind()`. Both `bind_front()` alternatives, unlike `std::bind()`, +// allow partial function application. (See // https://en.wikipedia.org/wiki/Partial_application). #ifndef ABSL_FUNCTIONAL_BIND_FRONT_H_ @@ -39,17 +40,40 @@ #include <functional> // For std::bind_front. #endif // defined(__cpp_lib_bind_front) && __cpp_lib_bind_front >= 201907L +#include <type_traits> #include <utility> #include "absl/functional/internal/front_binder.h" #include "absl/utility/utility.h" +// ABSL_INTERNAL_HAVE_STD_NTTP_BIND_FRONT +// +// Defined if the standard library implements C++26 `std::bind_front<ConstFn>`, +// which `absl::bind_front<ConstFn>` then aliases. +// +// libc++ 17 through 22 define `__cpp_lib_bind_front` as 202306L in C++26 mode, +// but `std::bind_front<ConstFn>` was first implemented in libc++ 23: +// https://github.com/llvm/llvm-project/pull/165096#issuecomment-4487517016 +#ifdef ABSL_INTERNAL_HAVE_STD_NTTP_BIND_FRONT +#error ABSL_INTERNAL_HAVE_STD_NTTP_BIND_FRONT cannot be directly set +#elif defined(__cpp_lib_bind_front) && __cpp_lib_bind_front >= 202306L && \ + (!defined(_LIBCPP_VERSION) || _LIBCPP_VERSION >= 230000) +#define ABSL_INTERNAL_HAVE_STD_NTTP_BIND_FRONT 1 +#endif + namespace absl { ABSL_NAMESPACE_BEGIN // bind_front() // // Binds the first N arguments of an invocable object and stores them by value. +// The target invocable can be passed either as a runtime argument +// `absl::bind_front(func, args...)` (C++20 `std::bind_front`) or as a +// compile-time non-type template parameter `absl::bind_front<ConstFn>(args...)` +// (C++26 `std::bind_front`). +// +// Passing `ConstFn` as a non-type template parameter avoids storing a function +// pointer or pointer-to-member at runtime. // // Like `std::bind()`, `absl::bind_front()` is implicitly convertible to // `std::function`. In particular, it may be used as a simpler replacement for @@ -57,7 +81,8 @@ // specified. More importantly, it provides more reliable correctness guarantees // than `std::bind()`; while `std::bind()` will silently ignore passing more // parameters than expected, for example, `absl::bind_front()` will report such -// mis-uses as errors. In C++20, `absl::bind_front` is replaced by +// mis-uses as errors. In C++20 (for runtime callables) and C++26 (for +// non-type template parameter callables), `absl::bind_front` aliases // `std::bind_front`. // // absl::bind_front(a...) can be seen as storing the results of @@ -70,6 +95,8 @@ // assert(absl::bind_front(Minus)(3, 2) == 3 - 2); // assert(absl::bind_front(Minus, 3)(2) == 3 - 2); // assert(absl::bind_front(Minus, 3, 2)() == 3 - 2); +// assert(absl::bind_front<&Minus>()(3, 2) == 3 - 2); +// assert(absl::bind_front<&Minus>(3)(2) == 3 - 2); // // Example: Binding a member function. // @@ -80,8 +107,10 @@ // Math math; // // assert(absl::bind_front(&Math::Double)(&math, 3) == 2 * 3); +// assert(absl::bind_front<&Math::Double>()(&math, 3) == 2 * 3); // // Stores a pointer to math inside the functor. // assert(absl::bind_front(&Math::Double, &math)(3) == 2 * 3); +// assert(absl::bind_front<&Math::Double>(&math)(3) == 2 * 3); // // Stores a copy of math inside the functor. // assert(absl::bind_front(&Math::Double, math)(3) == 2 * 3); // // Stores std::unique_ptr<Math> inside the functor. @@ -193,7 +222,23 @@ } #endif // defined(__cpp_lib_bind_front) && __cpp_lib_bind_front >= 201907L +#ifndef ABSL_INTERNAL_HAVE_STD_NTTP_BIND_FRONT +template <auto ConstFn, class... BoundArgs> +constexpr auto bind_front(BoundArgs&&... args) { + using F = decltype(ConstFn); + if constexpr (std::is_pointer_v<F> || std::is_member_pointer_v<F>) { + static_assert(ConstFn != nullptr, + "bind_front: ConstFn cannot be equal to nullptr"); + } + // The runtime `bind_front` checks the requirements on `BoundArgs`. + return absl::bind_front(functional_internal::ConstFnInvoker<ConstFn>{}, + std::forward<BoundArgs>(args)...); +} +#endif // ABSL_INTERNAL_HAVE_STD_NTTP_BIND_FRONT + ABSL_NAMESPACE_END } // namespace absl +#undef ABSL_INTERNAL_HAVE_STD_NTTP_BIND_FRONT + #endif // ABSL_FUNCTIONAL_BIND_FRONT_H_
diff --git a/absl/functional/bind_front_test.cc b/absl/functional/bind_front_test.cc index 6e87d43..300466c 100644 --- a/absl/functional/bind_front_test.cc +++ b/absl/functional/bind_front_test.cc
@@ -19,15 +19,17 @@ #include <functional> #include <memory> #include <string> +#include <type_traits> #include <utility> #include "gmock/gmock.h" #include "gtest/gtest.h" +#include "absl/base/config.h" #include "absl/memory/memory.h" namespace { -char CharAt(const char* s, size_t index) { return s[index]; } +constexpr char CharAt(const char* s, size_t index) { return s[index]; } TEST(BindTest, Basics) { EXPECT_EQ('C', absl::bind_front(CharAt)("ABC", 2)); @@ -200,7 +202,7 @@ EXPECT_EQ(42, std::move(f)()); } -int Plus(int a, int b) { return a + b; } +constexpr int Plus(int a, int b) { return a + b; } TEST(BindTest, ConstExpr) { constexpr auto f = absl::bind_front(CharAt); @@ -223,4 +225,224 @@ absl::bind_front(ManglingCall{}, 1, 3.3)("A"); } +struct Math { + constexpr int Add(int a, int b) const noexcept { return base + a + b; } + int base = 10; +}; + +TEST(BindConstFnTest, Basics) { + EXPECT_EQ('C', absl::bind_front<&CharAt>()("ABC", 2)); + EXPECT_EQ('C', absl::bind_front<&CharAt>("ABC")(2)); + EXPECT_EQ('C', absl::bind_front<&CharAt>("ABC", 2)()); +} + +TEST(BindConstFnTest, MemberPointers) { + Math m{10}; + auto bound_ptr = absl::bind_front<&Math::Add>(&m); + EXPECT_EQ(15, bound_ptr(2, 3)); + + auto bound_ptr_and_arg = absl::bind_front<&Math::Add>(&m, 2); + EXPECT_EQ(15, bound_ptr_and_arg(3)); + + auto bound_zero_args = absl::bind_front<&Math::Add>(); + EXPECT_EQ(15, bound_zero_args(&m, 2, 3)); + EXPECT_EQ(15, bound_zero_args(m, 2, 3)); +} + +TEST(BindConstFnTest, StoreByCopyRefAndPointer) { + Struct s = {"hello"}; + auto by_copy = absl::bind_front<&Struct::value>(s); + EXPECT_EQ("hello", by_copy()); + EXPECT_NE(&s.value, &by_copy()); + + NonCopyable nc("hello"); + auto by_ref = absl::bind_front<&GetNonCopyableValue>(std::ref(nc)); + EXPECT_EQ("hello", by_ref()); + EXPECT_EQ(&nc.value, &by_ref()); + + auto by_cref = absl::bind_front<&GetNonCopyableValue>(std::cref(nc)); + EXPECT_EQ("hello", by_cref()); + EXPECT_EQ(&nc.value, &by_cref()); + + auto by_ptr = absl::bind_front<&NonCopyable::value>(&nc); + EXPECT_EQ("hello", by_ptr()); + EXPECT_EQ(&nc.value, &by_ptr()); + nc.value = "goodbye"; + EXPECT_EQ("goodbye", by_ref()); + EXPECT_EQ("goodbye", by_cref()); + EXPECT_EQ("goodbye", by_ptr()); +} + +TEST(BindConstFnTest, MoveOnlyArgsAndResult) { + EXPECT_EQ(42, absl::bind_front<&Sink>()(std::make_unique<int>(42))); + EXPECT_EQ(42, absl::bind_front<&Sink>(std::make_unique<int>(42))()); + EXPECT_THAT(absl::bind_front<&Factory>()(42), ::testing::Pointee(42)); + EXPECT_THAT(absl::bind_front<&Factory>(42)(), ::testing::Pointee(42)); + + FalseCopyable<std::unique_ptr<int>> x; + x.m = std::make_unique<int>(42); + auto f = absl::bind_front<&GetMember>(std::move(x)); + EXPECT_EQ(42, std::move(f)()); +} + +TEST(BindConstFnTest, ConstExpr) { + constexpr auto f = absl::bind_front<&CharAt>(); + EXPECT_EQ(f("ABC", 1), 'B'); + + constexpr auto plus5 = absl::bind_front<&Plus>(5); + EXPECT_EQ(plus5(1), 6); + + constexpr auto plus5_1 = absl::bind_front<&Plus>(5, 1); + EXPECT_EQ(plus5_1(), 6); + + constexpr Math m{10}; + constexpr auto add_m = absl::bind_front<&Math::Add>(m, 2); + EXPECT_EQ(add_m(3), 15); + + // Invoking the results is `constexpr` only where the runtime `bind_front` + // that produces them is `std::bind_front` (C++20). The pre-C++20 fallback's + // call operators and `std::invoke` are not `constexpr`. +#if defined(__cpp_lib_bind_front) && __cpp_lib_bind_front >= 201907L + static_assert(f("ABC", 1) == 'B'); + static_assert(plus5(1) == 6); + static_assert(plus5_1() == 6); + static_assert(add_m(3) == 15); +#endif // __cpp_lib_bind_front >= 201907L +} + +// The results come from the runtime `bind_front`, which propagates `noexcept` +// where it is `std::bind_front` (C++20). +#if defined(__cpp_lib_bind_front) && __cpp_lib_bind_front >= 201907L +int NoexceptPlus(int a, int b) noexcept { return a + b; } +int PotentiallyThrowingPlus(int a, int b) { return a + b; } + +TEST(BindConstFnTest, NoexceptPropagation) { + auto noexcept_0 = absl::bind_front<&NoexceptPlus>(); + static_assert(noexcept(noexcept_0(1, 2))); + + auto throwing_0 = absl::bind_front<&PotentiallyThrowingPlus>(); + static_assert(!noexcept(throwing_0(1, 2))); + + Math m{10}; + auto math_0 = absl::bind_front<&Math::Add>(); + static_assert(noexcept(math_0(m, 1, 2))); +} +#endif // __cpp_lib_bind_front >= 201907L + +// Invalid calls must be substitution failures rather than hard errors, so that +// `std::is_invocable` works on the result. +TEST(BindConstFnTest, SfinaeFriendly) { + using PlusBinder = decltype(absl::bind_front<&Plus>()); + static_assert(std::is_invocable_v<PlusBinder, int, int>); + static_assert(!std::is_invocable_v<PlusBinder, std::string, int>); + static_assert(!std::is_invocable_v<PlusBinder, int>); + + using Plus1Binder = decltype(absl::bind_front<&Plus>(1)); + static_assert(std::is_invocable_v<Plus1Binder, int>); + static_assert(!std::is_invocable_v<Plus1Binder, std::string>); +} + +// These tests pass class-type objects as template arguments (P0732R2, +// P1907R1), and `StatelessLambdaNTTP` puts a lambda in a template argument +// (P0315R4, which has no feature-test macro). +// +// We check the language version rather than +// `__cpp_nontype_template_args >= 201911L` because Clang (through at least +// 24) still defines it as 201411L: its P1907R1 support is incomplete, though +// everything used here works. Checking the feature macro would silently skip +// these tests on Clang. If a toolchain claims C++20 but can't compile these +// tests, exclude it here. +#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L +TEST(BindConstFnTest, StatelessLambdaNTTP) { + constexpr auto f = absl::bind_front<[](int x, int y) { return x + y; }>(); + EXPECT_EQ(f(2, 3), 5); + + constexpr auto f2 = absl::bind_front<[](int x, int y) { return x + y; }>(2); + EXPECT_EQ(f2(3), 5); +} + +struct Adder { + constexpr int operator()(int x) const& { return base + x; } + // `ConstFn` names a template parameter object, which is a `const` lvalue + // ([temp.param]), so it must never be invoked as an rvalue. + int operator()(int) const&& = delete; + + int base; +}; + +TEST(BindConstFnTest, StatefulObjectNTTP) { + constexpr auto add5 = absl::bind_front<Adder{5}>(); + EXPECT_EQ(add5(1), 6); + + constexpr auto add5_1 = absl::bind_front<Adder{5}>(1); + EXPECT_EQ(add5_1(), 6); +} + +struct DescribeCall { + template <class... Args> + std::string operator()(Args&&...) & { + return "&"; + } + template <class... Args> + std::string operator()(Args&&...) const& { + return "const&"; + } + template <class... Args> + std::string operator()(Args&&...) && { + return "&&"; + } + template <class... Args> + std::string operator()(Args&&...) const&& { + return "const&&"; + } +}; + +// Since `ConstFn` is a `const` lvalue, it is invoked through its `const&` +// overload whatever the value category of the result. Both zero and nonzero +// bound arguments are tested, since the standard library's +// `std::bind_front<ConstFn>` may implement them differently. +TEST(BindConstFnTest, InvokesConstFnAsConstLvalue) { + auto f0 = absl::bind_front<DescribeCall{}>(); + const auto& cf0 = f0; + EXPECT_EQ("const&", f0()); + EXPECT_EQ("const&", cf0()); + EXPECT_EQ("const&", std::move(f0)()); + EXPECT_EQ("const&", std::move(cf0)()); + + auto f1 = absl::bind_front<DescribeCall{}>(1); + const auto& cf1 = f1; + EXPECT_EQ("const&", f1()); + EXPECT_EQ("const&", cf1()); + EXPECT_EQ("const&", std::move(f1)()); + EXPECT_EQ("const&", std::move(cf1)()); +} + +struct DeletedConstLvalueCall { + template <class... Args> + void operator()(Args&&...) &; + template <class... Args> + void operator()(Args&&...) const& = delete; + template <class... Args> + void operator()(Args&&...) &&; + template <class... Args> + void operator()(Args&&...) const&&; +}; + +// If `ConstFn`'s `const&` overload is deleted, the result isn't invocable, +// rather than falling back to another overload. +TEST(BindConstFnTest, NotInvocableIfConstLvalueCallDeleted) { + using F0 = decltype(absl::bind_front<DeletedConstLvalueCall{}>()); + static_assert(!std::is_invocable_v<F0&>); + static_assert(!std::is_invocable_v<const F0&>); + static_assert(!std::is_invocable_v<F0>); + static_assert(!std::is_invocable_v<const F0>); + + using F1 = decltype(absl::bind_front<DeletedConstLvalueCall{}>(1)); + static_assert(!std::is_invocable_v<F1&>); + static_assert(!std::is_invocable_v<const F1&>); + static_assert(!std::is_invocable_v<F1>); + static_assert(!std::is_invocable_v<const F1>); +} +#endif // ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L + } // namespace
diff --git a/absl/functional/internal/front_binder.h b/absl/functional/internal/front_binder.h index fcada9c..33d4fb4 100644 --- a/absl/functional/internal/front_binder.h +++ b/absl/functional/internal/front_binder.h
@@ -18,6 +18,7 @@ #define ABSL_FUNCTIONAL_INTERNAL_FRONT_BINDER_H_ #include <cstddef> +#include <functional> #include <type_traits> #include <utility> @@ -87,6 +88,34 @@ template <class F, class... BoundArgs> using bind_front_t = FrontBinder<std::decay_t<F>, std::decay_t<BoundArgs>...>; +// Stateless invoker for the compile-time constant target `ConstFn` of +// `absl::bind_front<ConstFn>(args...)` (C++26 [func.bind.front]). +// +// `operator()` has a trailing `decltype(std::invoke(ConstFn, ...))` return type +// rather than `decltype(auto)` so that an invalid call is a substitution +// failure instead of a hard error. Deducing a `decltype(auto)` return type +// instantiates the body, so merely checking whether a call is valid would fail +// to compile. That would break `std::is_invocable` and overload resolution, +// including among the call operators of a `bind_front` result wrapping the +// invoker: e.g. calling such a result as an rvalue fails if it holds a +// move-only bound argument, because its `const&` overload is also considered. +// +// The return type is `decltype(std::invoke(ConstFn, ...))` rather than +// `std::invoke_result_t<decltype(ConstFn), ...>` because for C++20 class-type +// non-type template parameters, `decltype(ConstFn)` is the unqualified type `F` +// (which `std::invoke_result_t` treats as an rvalue `F&&`), whereas the +// template parameter expression `ConstFn` is a `const F` lvalue as specified by +// [func.bind.front]. +template <auto ConstFn> +struct ConstFnInvoker { + template <class... FreeArgs> + constexpr auto operator()(FreeArgs&&... free_args) const noexcept( + noexcept(std::invoke(ConstFn, std::forward<FreeArgs>(free_args)...))) + -> decltype(std::invoke(ConstFn, std::forward<FreeArgs>(free_args)...)) { + return std::invoke(ConstFn, std::forward<FreeArgs>(free_args)...); + } +}; + } // namespace functional_internal ABSL_NAMESPACE_END } // namespace absl
diff --git a/absl/strings/BUILD.bazel b/absl/strings/BUILD.bazel index 5fe9b68..7711ad2 100644 --- a/absl/strings/BUILD.bazel +++ b/absl/strings/BUILD.bazel
@@ -710,10 +710,10 @@ ":cordz_info", ":cordz_update_scope", ":cordz_update_tracker", - ":internal", ":resize_and_overwrite", ":string_view", ":strings", + "//absl/algorithm:container", "//absl/base:config", "//absl/base:core_headers", "//absl/base:endian",
diff --git a/ci/BUILD.bazel b/ci/BUILD.bazel new file mode 100644 index 0000000..607a356 --- /dev/null +++ b/ci/BUILD.bazel
@@ -0,0 +1,70 @@ +# 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. + +package(default_visibility = ["//visibility:private"]) + +platform( + name = "rbe", + constraint_values = [ + "@platforms//os:linux", + "@platforms//cpu:x86_64", + ], + exec_properties = { + "OSArch": "amd64", + "cache-silo-key": "n2-highcpu-2-2026-10-14", + "container-image": "docker://gcr.io/google.com/absl-177019/gloop-linux-latest@sha256:e199623ad0b61b30d240e8bbddf473f65cb1a11da09462001d83b5066dbd9e98", + "dockerRunAsRoot": "true", + }, +) + +platform( + name = "rbe-arm", + constraint_values = [ + "@platforms//os:linux", + "@platforms//cpu:aarch64", + ], + exec_properties = { + "OSArch": "arm64", + "cache-silo-key": "t2a-standard-4-2026-10-14", + "container-image": "docker://gcr.io/google.com/absl-177019/gloop-linux-latest@sha256:e199623ad0b61b30d240e8bbddf473f65cb1a11da09462001d83b5066dbd9e98", + "dockerRunAsRoot": "true", + }, +) + +platform( + name = "linux_gcc", + constraint_values = [ + "@platforms//os:linux", + "@platforms//cpu:x86_64", + "@bazel_tools//tools/cpp:gcc", + ], +) + +platform( + name = "linux_clang", + constraint_values = [ + "@platforms//os:linux", + "@platforms//cpu:x86_64", + "@bazel_tools//tools/cpp:clang", + ], +) + +platform( + name = "linux_arm_clang", + constraint_values = [ + "@platforms//os:linux", + "@platforms//cpu:aarch64", + "@bazel_tools//tools/cpp:clang", + ], +)
diff --git a/ci/docker_toolchain/BUILD.bazel b/ci/docker_toolchain/BUILD.bazel new file mode 100644 index 0000000..ae482b0 --- /dev/null +++ b/ci/docker_toolchain/BUILD.bazel
@@ -0,0 +1,21 @@ +# 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. + +# Standalone fallback toolchain module when building outside the Docker container. +# Inside the container, this module is overridden via /etc/bazel.bazelrc to point to an actual +# toolchain. + +package(default_visibility = ["//visibility:public"]) + +filegroup(name = "empty")
diff --git a/ci/docker_toolchain/MODULE.bazel b/ci/docker_toolchain/MODULE.bazel new file mode 100644 index 0000000..10d8650 --- /dev/null +++ b/ci/docker_toolchain/MODULE.bazel
@@ -0,0 +1,18 @@ +# 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. + +module( + name = "docker_toolchain", + version = "0.1", +)