diff --git a/absl/algorithm/BUILD.bazel b/absl/algorithm/BUILD.bazel index cdb0af1..f00d5d0 100644 --- a/absl/algorithm/BUILD.bazel +++ b/absl/algorithm/BUILD.bazel
@@ -86,6 +86,7 @@ "//absl/base", "//absl/base:config", "//absl/base:core_headers", + "//absl/base:hardening", "//absl/memory", "//absl/meta:type_traits", "//absl/random",
diff --git a/absl/algorithm/CMakeLists.txt b/absl/algorithm/CMakeLists.txt index cd7fa22..eb20132 100644 --- a/absl/algorithm/CMakeLists.txt +++ b/absl/algorithm/CMakeLists.txt
@@ -37,6 +37,7 @@ DEPS absl::algorithm absl::config + absl::hardening GTest::gmock_main )
diff --git a/absl/algorithm/container.h b/absl/algorithm/container.h index 6928623..a6b60cb 100644 --- a/absl/algorithm/container.h +++ b/absl/algorithm/container.h
@@ -126,14 +126,14 @@ using OutputIter = ContainerIter<OutputRange>; if constexpr (base_internal::IsAtLeastForwardIterator<InputIter>::value) { - base_internal::HardeningAssert( - n <= std::distance(container_algorithm_internal::c_begin(input), - container_algorithm_internal::c_end(input))); + base_internal::HardeningAssertLE( + n, std::distance(container_algorithm_internal::c_begin(input), + container_algorithm_internal::c_end(input))); } if constexpr (base_internal::IsAtLeastForwardIterator<OutputIter>::value) { - base_internal::HardeningAssert( - n <= std::distance(container_algorithm_internal::c_begin(output), - container_algorithm_internal::c_end(output))); + base_internal::HardeningAssertLE( + n, std::distance(container_algorithm_internal::c_begin(output), + container_algorithm_internal::c_end(output))); } } @@ -143,9 +143,9 @@ using OutputIter = ContainerIter<OutputRange>; if constexpr (base_internal::IsAtLeastForwardIterator<InputIter>::value && base_internal::IsAtLeastForwardIterator<OutputIter>::value) { - base_internal::HardeningAssert( + base_internal::HardeningAssertLE( std::distance(container_algorithm_internal::c_begin(input), - container_algorithm_internal::c_end(input)) <= + container_algorithm_internal::c_end(input)), std::distance(container_algorithm_internal::c_begin(output), container_algorithm_internal::c_end(output))); } @@ -192,16 +192,6 @@ std::remove_reference_t<C>>::value, std::decay_t<OutputIterator>>; -template <typename C, typename OutputRange> -using ResultOfRangeToRangeTransfer = - std::enable_if_t<container_algorithm_internal::HasBeginEnd< - std::add_lvalue_reference_t<OutputRange>>::value && - !container_algorithm_internal::IsMultidimensionalArray< - std::remove_reference_t<OutputRange>>::value && - !container_algorithm_internal::IsMultidimensionalArray< - std::remove_reference_t<C>>::value, - void>; - // Similar to std::is_pointer, but for testing if a type is a span. // // Note that subclasses of spans do not automatically qualify as spans, as they @@ -231,6 +221,18 @@ std::conditional_t<std::is_lvalue_reference<C>::value, std::true_type, IsSpan<C>>; +template <typename C, typename OutputRange> +using ResultOfRangeToRangeTransfer = + std::enable_if_t<container_algorithm_internal::HasBeginEnd< + std::add_lvalue_reference_t<OutputRange>>::value && + !container_algorithm_internal::IsMultidimensionalArray< + std::remove_reference_t<OutputRange>>::value && + !container_algorithm_internal::IsMultidimensionalArray< + std::remove_reference_t<C>>::value && + container_algorithm_internal:: + IsPermissibleDestinationRange<OutputRange>::value, + void>; + } // namespace container_algorithm_internal // PUBLIC API @@ -632,8 +634,7 @@ InputSequence, OutputRange> c_copy(const InputSequence& input, OutputRange&& output) { container_algorithm_internal::AssertCopySize(input, output); - absl::c_copy(input, container_algorithm_internal::c_begin( - std::forward<OutputRange>(output))); + absl::c_copy(input, container_algorithm_internal::c_begin(output)); } // c_copy_n() @@ -664,9 +665,7 @@ C, OutputRange> 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<OutputRange>(output))); + absl::c_copy_n(input, n, container_algorithm_internal::c_begin(output)); } // c_copy_if() @@ -715,8 +714,8 @@ C, OutputRange> c_move(C&& src, OutputRange&& dest) { container_algorithm_internal::AssertCopySize(src, dest); - absl::c_move(std::forward<C>(src), container_algorithm_internal::c_begin( - std::forward<OutputRange>(dest))); + absl::c_move(std::forward<C>(src), + container_algorithm_internal::c_begin(dest)); } // c_move_backward() @@ -757,33 +756,109 @@ // result in an iterator pointing to the last transformed element in the output // range. template <typename InputSequence, typename OutputIterator, typename UnaryOp> -constexpr OutputIterator c_transform(const InputSequence& input, - OutputIterator output, - UnaryOp&& unary_op) { +constexpr container_algorithm_internal::ResultOfRangeToIteratorTransfer< + InputSequence, 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, + container_algorithm_internal::c_end(input), + std::forward<OutputIterator>(output), std::forward<UnaryOp>(unary_op)); } +// Performs a transformation using a unary predicate. Stores the result in +// `output`. `absl::c_transform(input, output, unary_op)` is equivalent to +// `std::transform(std::begin(input), std::end(input), std::begin(output), +// unary_op)`. +// +// The `output` container must be large enough to hold all elements of `input`; +// this function does not resize `output`. +template <typename InputSequence, typename OutputRange, typename UnaryOp> +constexpr container_algorithm_internal::ResultOfRangeToRangeTransfer< + InputSequence, OutputRange> +c_transform(const InputSequence& input, OutputRange&& output, + UnaryOp&& unary_op) { + container_algorithm_internal::AssertCopySize(input, output); + absl::c_transform( + input, + container_algorithm_internal::c_begin(std::forward<OutputRange>(output)), + std::forward<UnaryOp>(unary_op)); +} + // Overload of c_transform() for performing a transformation using a binary // predicate. Applies `binary_op` to the first N elements of `c1` and `c2`, // where N = min(size(c1), size(c2)). template <typename InputSequence1, typename InputSequence2, typename OutputIterator, typename BinaryOp> -constexpr OutputIterator c_transform(const InputSequence1& input1, - const InputSequence2& input2, - OutputIterator output, - BinaryOp&& binary_op) { +constexpr container_algorithm_internal::ResultOfRangeToIteratorTransfer< + InputSequence1, 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); auto last2 = container_algorithm_internal::c_end(input2); - for (; first1 != last1 && first2 != last2; - ++first1, (void)++first2, ++output) { - *output = binary_op(*first1, *first2); + std::decay_t<OutputIterator> out = std::forward<OutputIterator>(output); + for (; first1 != last1 && first2 != last2; ++first1, (void)++first2, ++out) { + *out = binary_op(*first1, *first2); } + return out; +} - return output; +// Performs a transformation using a binary predicate. Stores the result in +// `output`. Applies `binary_op` to the first N elements of `input1` and +// `input2`, where N = min(size(input1), size(input2)). +// +// The `output` container must be large enough to hold all N elements; +// this function does not resize `output`. +template <typename InputSequence1, typename InputSequence2, + typename OutputRange, typename BinaryOp> +constexpr std::common_type_t< + container_algorithm_internal::ResultOfRangeToRangeTransfer<InputSequence1, + OutputRange>, + container_algorithm_internal::ResultOfRangeToRangeTransfer<InputSequence2, + OutputRange>> +c_transform(const InputSequence1& input1, const InputSequence2& input2, + OutputRange&& output, BinaryOp&& binary_op) { + using InputIter1 = + container_algorithm_internal::ContainerIter<InputSequence1>; + using InputIter2 = + container_algorithm_internal::ContainerIter<InputSequence2>; + using OutputIter = container_algorithm_internal::ContainerIter<OutputRange>; + if constexpr (base_internal::IsAtLeastForwardIterator<OutputIter>::value) { + constexpr bool input1_has_size = + base_internal::IsAtLeastForwardIterator<InputIter1>::value; + constexpr bool input2_has_size = + base_internal::IsAtLeastForwardIterator<InputIter2>::value; + auto output_size = + std::distance(container_algorithm_internal::c_begin(output), + container_algorithm_internal::c_end(output)); + + if constexpr (input1_has_size && input2_has_size) { + base_internal::HardeningAssertLE( + (std::min)(std::distance( + container_algorithm_internal::c_begin(input1), + container_algorithm_internal::c_end(input1)), + std::distance( + container_algorithm_internal::c_begin(input2), + container_algorithm_internal::c_end(input2))), + output_size); + } else if constexpr (input1_has_size) { + base_internal::HardeningAssertLE( + std::distance(container_algorithm_internal::c_begin(input1), + container_algorithm_internal::c_end(input1)), + output_size); + } else if constexpr (input2_has_size) { + base_internal::HardeningAssertLE( + std::distance(container_algorithm_internal::c_begin(input2), + container_algorithm_internal::c_end(input2)), + output_size); + } + } + absl::c_transform( + input1, input2, + container_algorithm_internal::c_begin(std::forward<OutputRange>(output)), + std::forward<BinaryOp>(binary_op)); } // c_replace()
diff --git a/absl/algorithm/container_test.cc b/absl/algorithm/container_test.cc index 6249e20..2d2fec1 100644 --- a/absl/algorithm/container_test.cc +++ b/absl/algorithm/container_test.cc
@@ -36,6 +36,7 @@ #include "gtest/gtest.h" #include "absl/base/casts.h" #include "absl/base/config.h" +#include "absl/base/internal/hardening.h" #include "absl/base/macros.h" #include "absl/memory/memory.h" #include "absl/meta/type_traits.h" @@ -762,6 +763,13 @@ EXPECT_THAT(actual, ElementsAre(1, 2, 3, 4, 5)); } +TEST(MutatingTest, CopyToSpanRvalue) { + const std::vector<int> input = {1, 2, 3}; + std::vector<int> actual = {0, 0, 0, 4, 5}; + absl::c_copy(input, absl::MakeSpan(actual)); + EXPECT_THAT(actual, ElementsAre(1, 2, 3, 4, 5)); +} + TEST(MutatingTest, CopyNToContainer) { const std::vector<int> input = {1, 2, 3, 4, 5}; std::vector<int> actual = {0, 0, 0, 0, 0}; @@ -769,6 +777,13 @@ EXPECT_THAT(actual, ElementsAre(1, 2, 0, 0, 0)); } +TEST(MutatingTest, CopyNToSpanRvalue) { + const std::vector<int> input = {1, 2, 3, 4, 5}; + std::vector<int> actual = {0, 0, 0, 0, 0}; + absl::c_copy_n(input, 2, absl::MakeSpan(actual)); + EXPECT_THAT(actual, ElementsAre(1, 2, 0, 0, 0)); +} + TEST(MutatingTest, CopyNToContainerWithZeroN) { const std::vector<int> input = {1, 2, 3, 4, 5}; std::vector<int> actual = {0, 0, 0, 0, 0}; @@ -843,7 +858,6 @@ EXPECT_THAT(actual, ElementsAre(1, 2, 0, 0, 0)); } -#if GTEST_HAS_DEATH_TEST bool IsHardened() { bool hardened = false; @@ -857,77 +871,105 @@ TEST(MutatingTest, CopyToCArrayInvalidSize) { const std::vector<int> input = {1, 2, 3}; int actual[2] = {0, 0}; + (void)actual; +#if GTEST_HAS_DEATH_TEST if (IsHardened()) { + absl::base_internal::ScopedSetAbslHardeningForTesting hardener(true); EXPECT_DEATH(absl::c_copy(input, actual), ""); } +#endif } TEST(MutatingTest, CopyNToCArrayInvalidSize) { const std::vector<int> input = {1, 2, 3}; int actual[2] = {0, 0}; + (void)actual; +#if GTEST_HAS_DEATH_TEST if (IsHardened()) { + absl::base_internal::ScopedSetAbslHardeningForTesting hardener(true); EXPECT_DEATH(absl::c_copy_n(input, 3, actual), ""); } +#endif } TEST(MutatingTest, CopyNToCArrayNGreaterThanInput) { const std::vector<int> input = {1, 2, 3}; int actual[4] = {0, 0, 0, 0}; + (void)actual; +#if GTEST_HAS_DEATH_TEST if (IsHardened()) { + absl::base_internal::ScopedSetAbslHardeningForTesting hardener(true); EXPECT_DEATH(absl::c_copy_n(input, 4, actual), ""); } +#endif } TEST(MutatingTest, CopyToContainerInvalidSize) { const std::list<int> input = {1, 2, 3, 4, 5}; std::list<int> actual = {0, 0, 0}; +#if GTEST_HAS_DEATH_TEST if (IsHardened()) { + absl::base_internal::ScopedSetAbslHardeningForTesting hardener(true); EXPECT_DEATH(absl::c_copy(input, actual), ""); } +#endif } TEST(MutatingTest, CopyNToContainerNGreaterThanInput) { const std::vector<int> input = {1, 2, 3}; std::vector<int> actual = {0, 0, 0, 0}; +#if GTEST_HAS_DEATH_TEST if (IsHardened()) { + absl::base_internal::ScopedSetAbslHardeningForTesting hardener(true); EXPECT_DEATH(absl::c_copy_n(input, 4, actual), ""); } +#endif } TEST(MutatingTest, CopyNToContainerNGreaterThanOutput) { const std::vector<int> input = {1, 2, 3}; std::vector<int> actual = {0, 0}; +#if GTEST_HAS_DEATH_TEST if (IsHardened()) { + absl::base_internal::ScopedSetAbslHardeningForTesting hardener(true); EXPECT_DEATH(absl::c_copy_n(input, 3, actual), ""); } +#endif } TEST(MutatingTest, CopyToForwardListInvalidSize) { const std::forward_list<int> input = {1, 2, 3, 4, 5}; std::forward_list<int> actual = {0, 0, 0}; +#if GTEST_HAS_DEATH_TEST if (IsHardened()) { + absl::base_internal::ScopedSetAbslHardeningForTesting hardener(true); EXPECT_DEATH(absl::c_copy(input, actual), ""); } +#endif } TEST(MutatingTest, CopyNToForwardListNGreaterThanInput) { const std::forward_list<int> input = {1, 2, 3}; std::forward_list<int> actual = {0, 0, 0, 0}; +#if GTEST_HAS_DEATH_TEST if (IsHardened()) { + absl::base_internal::ScopedSetAbslHardeningForTesting hardener(true); EXPECT_DEATH(absl::c_copy_n(input, 4, actual), ""); } +#endif } TEST(MutatingTest, CopyNToForwardListNGreaterThanOutput) { const std::forward_list<int> input = {1, 2, 3}; std::forward_list<int> actual = {0, 0}; +#if GTEST_HAS_DEATH_TEST if (IsHardened()) { + absl::base_internal::ScopedSetAbslHardeningForTesting hardener(true); EXPECT_DEATH(absl::c_copy_n(input, 3, actual), ""); } +#endif } -#endif // GTEST_HAS_DEATH_TEST - TEST(MutatingTest, CopyIf) { const std::list<int> input = {1, 2, 3}; std::vector<int> output; @@ -1008,6 +1050,28 @@ EXPECT_EQ(actual[3], nullptr); } +TEST(MutatingTest, MoveToSpanRvalue) { + std::vector<std::unique_ptr<int>> input; + input.push_back(std::make_unique<int>(1)); + input.push_back(std::make_unique<int>(2)); + input.push_back(std::make_unique<int>(3)); + + std::vector<std::unique_ptr<int>> actual(5); + absl::c_move(input, absl::MakeSpan(actual)); + + EXPECT_EQ(input[0], nullptr); + EXPECT_EQ(input[1], nullptr); + EXPECT_EQ(input[2], nullptr); + + ASSERT_NE(actual[0], nullptr); + EXPECT_EQ(*actual[0], 1); + ASSERT_NE(actual[1], nullptr); + EXPECT_EQ(*actual[1], 2); + ASSERT_NE(actual[2], nullptr); + EXPECT_EQ(*actual[2], 3); + EXPECT_EQ(actual[3], nullptr); +} + TEST(MutatingTest, MoveToDifferentContainerType) { std::list<std::unique_ptr<int>> input; input.push_back(std::make_unique<int>(1)); @@ -1047,34 +1111,41 @@ EXPECT_EQ(*actual[0], 1); } -#if GTEST_HAS_DEATH_TEST TEST(MutatingTest, MoveToCArrayInvalidSize) { std::vector<int> input = {1, 2, 3}; int actual[2] = {0, 0}; + (void)actual; +#if GTEST_HAS_DEATH_TEST if (IsHardened()) { + absl::base_internal::ScopedSetAbslHardeningForTesting hardener(true); EXPECT_DEATH(absl::c_move(input, actual), ""); } +#endif } TEST(MutatingTest, MoveToContainerInvalidSize) { std::list<int> input = {1, 2, 3, 4, 5}; std::list<int> actual = {0, 0, 0}; +#if GTEST_HAS_DEATH_TEST if (IsHardened()) { + absl::base_internal::ScopedSetAbslHardeningForTesting hardener(true); EXPECT_DEATH(absl::c_move(input, actual), ""); } +#endif } TEST(MutatingTest, MoveToForwardListInvalidSize) { std::forward_list<int> input = {1, 2, 3, 4, 5}; std::forward_list<int> actual = {0, 0, 0}; +#if GTEST_HAS_DEATH_TEST if (IsHardened()) { + absl::base_internal::ScopedSetAbslHardeningForTesting hardener(true); EXPECT_DEATH(absl::c_move(input, actual), ""); } +#endif } -#endif // GTEST_HAS_DEATH_TEST - TEST(MutatingTest, SwapRanges) { std::vector<int> odds = {2, 4, 6}; std::vector<int> evens = {1, 3, 5}; @@ -1105,19 +1176,144 @@ *end = 7; EXPECT_EQ(std::vector<int>({1, 5, 4, 7}), z); - z.clear(); - y.pop_back(); - end = absl::c_transform(x, y, std::back_inserter(z), std::plus<int>()); - EXPECT_EQ(std::vector<int>({1, 5}), z); - *end = 7; - EXPECT_EQ(std::vector<int>({1, 5, 7}), z); + std::vector<int> x2{1, 2, 3}; + std::vector<int> y2{10, 20, 30}; + std::vector<int> z2(3); + auto out_it = z2.begin(); + absl::c_transform(x2, y2, out_it, std::plus<int>()); + // The caller's iterator should not be modified. + EXPECT_EQ(out_it, z2.begin()); + EXPECT_EQ(z2, std::vector<int>({11, 22, 33})); +} - z.clear(); - std::swap(x, y); - end = absl::c_transform(x, y, std::back_inserter(z), std::plus<int>()); - EXPECT_EQ(std::vector<int>({1, 5}), z); - *end = 7; - EXPECT_EQ(std::vector<int>({1, 5, 7}), z); +TEST(MutatingTest, TransformToContainer) { + const std::vector<int> input = {1, 2, 3}; + std::vector<int> actual = {0, 0, 0, 4, 5}; + absl::c_transform(input, actual, [](int x) { return x * 2; }); + EXPECT_THAT(actual, ElementsAre(2, 4, 6, 4, 5)); +} + +TEST(MutatingTest, BinaryTransformToContainer) { + { + const std::vector<int> input1 = {1, 2, 3}; + const std::vector<int> input2 = {10, 20, 30}; + std::vector<int> actual = {0, 0, 0, 4, 5}; + absl::c_transform(input1, input2, actual, std::plus<int>()); + EXPECT_THAT(actual, ElementsAre(11, 22, 33, 4, 5)); + } + { + const std::vector<int> input1 = {1, 2, 3, 4}; + const std::vector<int> input2 = {10, 20}; + std::vector<int> actual = {0, 0, 0, 4, 5}; + absl::c_transform(input1, input2, actual, std::plus<int>()); + EXPECT_THAT(actual, ElementsAre(11, 22, 0, 4, 5)); + } + { + const std::vector<int> input1 = {1, 2}; + const std::vector<int> input2 = {10, 20, 30, 40}; + std::vector<int> actual = {0, 0, 0, 4, 5}; + absl::c_transform(input1, input2, actual, std::plus<int>()); + EXPECT_THAT(actual, ElementsAre(11, 22, 0, 4, 5)); + } +} + +TEST(MutatingTest, TransformToDifferentContainerType) { + const std::list<int> input = {1, 2, 3}; + std::array<int, 5> actual = {0, 0, 0, 4, 5}; + absl::c_transform(input, actual, [](int x) { return x * 2; }); + EXPECT_THAT(actual, ElementsAre(2, 4, 6, 4, 5)); +} + +TEST(MutatingTest, TransformToCArray) { + const std::vector<int> input = {1, 2, 3}; + int actual[5] = {0, 0, 0, 4, 5}; + absl::c_transform(input, actual, [](int x) { return x * 2; }); + EXPECT_THAT(actual, ElementsAre(2, 4, 6, 4, 5)); +} + +TEST(MutatingTest, TransformFromCArray) { + const int input[5] = {1, 2, 3, 4, 5}; + std::vector<int> actual = {0, 0, 0, 0, 0}; + absl::c_transform(input, actual, [](int x) { return x * 2; }); + EXPECT_THAT(actual, ElementsAre(2, 4, 6, 8, 10)); +} + +TEST(MutatingTest, TransformToCArrayInvalidSize) { + const std::vector<int> input = {1, 2, 3}; + int actual[2] = {0, 0}; + (void)actual; +#if GTEST_HAS_DEATH_TEST + if (IsHardened()) { + absl::base_internal::ScopedSetAbslHardeningForTesting hardener(true); + EXPECT_DEATH(absl::c_transform(input, actual, [](int x) { return x * 2; }), + ""); + } +#endif +} + +TEST(MutatingTest, BinaryTransformToCArrayInvalidSize) { + const std::vector<int> input1 = {1, 2, 3}; + const std::vector<int> input2 = {10, 20, 30}; + int actual[2] = {0, 0}; + (void)actual; +#if GTEST_HAS_DEATH_TEST + if (IsHardened()) { + absl::base_internal::ScopedSetAbslHardeningForTesting hardener(true); + EXPECT_DEATH(absl::c_transform(input1, input2, actual, std::plus<int>()), + ""); + } +#endif +} + +TEST(MutatingTest, TransformToContainerInvalidSize) { + const std::list<int> input = {1, 2, 3, 4, 5}; + std::list<int> actual = {0, 0, 0}; +#if GTEST_HAS_DEATH_TEST + if (IsHardened()) { + absl::base_internal::ScopedSetAbslHardeningForTesting hardener(true); + EXPECT_DEATH(absl::c_transform(input, actual, [](int x) { return x * 2; }), + ""); + } +#endif +} + +TEST(MutatingTest, BinaryTransformToContainerInvalidSize) { + const std::vector<int> input1 = {1, 2, 3, 4, 5}; + const std::vector<int> input2 = {10, 20, 30, 40, 50}; + std::vector<int> actual = {0, 0, 0}; +#if GTEST_HAS_DEATH_TEST + if (IsHardened()) { + absl::base_internal::ScopedSetAbslHardeningForTesting hardener(true); + EXPECT_DEATH(absl::c_transform(input1, input2, actual, std::plus<int>()), + ""); + } +#endif +} + +TEST(MutatingTest, BinaryTransformInput2InvalidSize) { + const std::vector<int> input1 = {1, 2, 3}; + const std::vector<int> input2 = {10, 20}; + std::vector<int> actual = {0}; +#if GTEST_HAS_DEATH_TEST + if (IsHardened()) { + absl::base_internal::ScopedSetAbslHardeningForTesting hardener(true); + EXPECT_DEATH(absl::c_transform(input1, input2, actual, std::plus<int>()), + ""); + } +#endif +} + +TEST(MutatingTest, BinaryTransformInput1InvalidSize) { + const std::vector<int> input1 = {1, 2}; + const std::vector<int> input2 = {10, 20, 30}; + std::vector<int> actual = {0}; +#if GTEST_HAS_DEATH_TEST + if (IsHardened()) { + absl::base_internal::ScopedSetAbslHardeningForTesting hardener(true); + EXPECT_DEATH(absl::c_transform(input1, input2, actual, std::plus<int>()), + ""); + } +#endif } TEST(MutatingTest, Replace) { @@ -2597,6 +2793,27 @@ std::declval<Output>()))>> : std::true_type {}; +template <typename Container, typename Output, typename UnaryOp, + typename = void> +struct CanTransformUnary : std::false_type {}; +template <typename Container, typename Output, typename UnaryOp> +struct CanTransformUnary<Container, Output, UnaryOp, + absl::void_t<decltype(absl::c_transform( + std::declval<Container>(), std::declval<Output>(), + std::declval<UnaryOp>()))>> : std::true_type {}; + +template <typename Container1, typename Container2, typename Output, + typename BinaryOp, typename = void> +struct CanTransformBinary : std::false_type {}; +template <typename Container1, typename Container2, typename Output, + typename BinaryOp> +struct CanTransformBinary< + Container1, Container2, Output, BinaryOp, + absl::void_t<decltype(absl::c_transform( + std::declval<Container1>(), std::declval<Container2>(), + std::declval<Output>(), std::declval<BinaryOp>()))>> : std::true_type { +}; + TEST(CanCopyTest, CopyToMultiDimArray) { static_assert(CanCopy<std::vector<int>, int (&)[10]>::value); static_assert(!CanCopy<std::vector<int>, int (&)[2][2]>::value); @@ -2624,12 +2841,25 @@ using Vec = std::vector<int>; // Because AmbiguousType is both an iterator and a container, // the compiler should fail to resolve the c_copy overload. - static_assert(!CanCopy<Vec, AmbiguousType>::value, + static_assert(!CanCopy<Vec, AmbiguousType&>::value, "Ambiguous types should not compile!"); - static_assert(!CanCopyN<Vec, AmbiguousType>::value, + static_assert(!CanCopyN<Vec, AmbiguousType&>::value, "Ambiguous types should not compile!"); } +TEST(CanCopyTest, CopyToRValue) { + using Vec = std::vector<int>; + using Span = absl::Span<int>; + static_assert(CanCopy<Vec, Span>::value, + "Should be able to copy to rvalue Span"); + static_assert(!CanCopy<Vec, Vec>::value, + "Should not be able to copy to rvalue vector"); + static_assert(CanCopyN<Vec, Span>::value, + "Should be able to copy_n to rvalue Span"); + static_assert(!CanCopyN<Vec, Vec>::value, + "Should not be able to copy_n to rvalue vector"); +} + TEST(CanMoveTest, MoveToMultiDimArray) { static_assert(CanMove<std::vector<int>, int (&)[10]>::value); static_assert(!CanMove<std::vector<int>, int (&)[2][2]>::value); @@ -2644,7 +2874,57 @@ using Vec = std::vector<int>; // Because AmbiguousType is both an iterator and a container, // the compiler should fail to resolve the c_move overload. - static_assert(!CanMove<Vec, AmbiguousType>::value, + static_assert(!CanMove<Vec, AmbiguousType&>::value, + "Ambiguous types should not compile!"); +} + +TEST(CanMoveTest, MoveToRValue) { + using Vec = std::vector<std::unique_ptr<int>>; + using Span = absl::Span<std::unique_ptr<int>>; + static_assert(CanMove<Vec, Span>::value, + "Should be able to move to rvalue Span"); + static_assert(!CanMove<Vec, Vec>::value, + "Should not be able to move to rvalue vector"); +} + +TEST(CanTransformTest, TransformToMultiDimArray) { + using Negate = std::negate<int>; + using Plus = std::plus<int>; + + static_assert( + CanTransformUnary<std::vector<int>, int (&)[10], Negate>::value); + static_assert( + !CanTransformUnary<std::vector<int>, int (&)[2][2], Negate>::value); + + static_assert(CanTransformUnary<int[10], int (&)[10], Negate>::value); + static_assert(!CanTransformUnary<int[10], int (&)[2][2], Negate>::value); + static_assert(!CanTransformUnary<int[2][2], int (&)[4], Negate>::value); + static_assert(!CanTransformUnary<int[2][2], int (&)[2][2], Negate>::value); + + static_assert(CanTransformBinary<std::vector<int>, std::vector<int>, + int (&)[10], Plus>::value); + static_assert(!CanTransformBinary<std::vector<int>, std::vector<int>, + int (&)[2][2], Plus>::value); + + static_assert(CanTransformBinary<int[10], int[10], int (&)[10], Plus>::value); + static_assert( + !CanTransformBinary<int[10], int[10], int (&)[2][2], Plus>::value); + static_assert( + !CanTransformBinary<int[2][2], int[4], int (&)[4], Plus>::value); + static_assert( + !CanTransformBinary<int[2][2], int[2][2], int (&)[2][2], Plus>::value); +} + +TEST(CanTransformTest, AmbiguousTypeFailsToCompile) { + using Vec = std::vector<int>; + using Negate = std::negate<int>; + using Plus = std::plus<int>; + + // Because AmbiguousType is both an iterator and a container, + // the compiler should fail to resolve the c_transform overload. + static_assert(!CanTransformUnary<Vec, AmbiguousType&, Negate>::value, + "Ambiguous types should not compile!"); + static_assert(!CanTransformBinary<Vec, Vec, AmbiguousType&, Plus>::value, "Ambiguous types should not compile!"); }
diff --git a/absl/base/internal/hardening.h b/absl/base/internal/hardening.h index fc86621..ef6d327 100644 --- a/absl/base/internal/hardening.h +++ b/absl/base/internal/hardening.h
@@ -23,6 +23,8 @@ #ifndef ABSL_BASE_INTERNAL_HARDENING_H_ #define ABSL_BASE_INTERNAL_HARDENING_H_ +#include <cstddef> + #include "absl/base/config.h" #include "absl/base/macros.h" #include "absl/base/options.h" @@ -66,8 +68,8 @@ #endif } -template <typename T> -constexpr void HardeningAssertGT(T val1, T val2) { +template <typename T1, typename T2> +constexpr void HardeningAssertGT(T1 val1, T2 val2) { ABSL_ASSERT(val1 > val2); #if (ABSL_OPTION_HARDENED == 1 || ABSL_OPTION_HARDENED == 2) && defined(NDEBUG) if (!ABSL_PREDICT_TRUE(val1 > val2)) { @@ -76,8 +78,8 @@ #endif } -template <typename T> -constexpr void HardeningAssertGE(T val1, T val2) { +template <typename T1, typename T2> +constexpr void HardeningAssertGE(T1 val1, T2 val2) { ABSL_ASSERT(val1 >= val2); #if (ABSL_OPTION_HARDENED == 1 || ABSL_OPTION_HARDENED == 2) && defined(NDEBUG) if (!ABSL_PREDICT_TRUE(val1 >= val2)) { @@ -86,8 +88,8 @@ #endif } -template <typename T> -constexpr void HardeningAssertLT(T val1, T val2) { +template <typename T1, typename T2> +constexpr void HardeningAssertLT(T1 val1, T2 val2) { ABSL_ASSERT(val1 < val2); #if (ABSL_OPTION_HARDENED == 1 || ABSL_OPTION_HARDENED == 2) && defined(NDEBUG) if (!ABSL_PREDICT_TRUE(val1 < val2)) { @@ -96,8 +98,8 @@ #endif } -template <typename T> -constexpr void HardeningAssertLE(T val1, T val2) { +template <typename T1, typename T2> +constexpr void HardeningAssertLE(T1 val1, T2 val2) { ABSL_ASSERT(val1 <= val2); #if (ABSL_OPTION_HARDENED == 1 || ABSL_OPTION_HARDENED == 2) && defined(NDEBUG) if (!ABSL_PREDICT_TRUE(val1 <= val2)) { @@ -130,6 +132,20 @@ #endif } +class ScopedSetAbslHardeningForTesting { + private: + bool prev_state_; + + public: + explicit ScopedSetAbslHardeningForTesting([[maybe_unused]] bool enabled) { + prev_state_ = false; + SetAbslHardeningEnabled(enabled); + } + ~ScopedSetAbslHardeningForTesting() { + absl::base_internal::SetAbslHardeningEnabled(prev_state_); + } +}; + } // namespace base_internal ABSL_NAMESPACE_END
diff --git a/absl/base/macros.h b/absl/base/macros.h index bfb6402..46432f0 100644 --- a/absl/base/macros.h +++ b/absl/base/macros.h
@@ -224,13 +224,30 @@ // `OldType` with `NewType`. Once all replacements have been completed, the old // function or type can be deleted. // +// Internal note: Clang also allows `ABSL_REFACTOR_INLINE` to be used on +// using-declarations, but attributes on using-declarations are invalid in C++. +// (NOTE: This note refers to `using a::b ABSL_REFACTOR_INLINE;` and not +// `using b ABSL_REFACTOR_INLINE = a::b;`, which is OK.) Therefore: +// +// 1. In OSS: Do not use this on using-declarations. Such usage is invalid and +// unsupported usage, and may break at any time. +// 2. In Google: Avoid such usage except as a last resort. Instead, prefer other +// inlining approaches (such as type aliases or forwarding functions, +// illustrated above) whenever possible. This is because Clang (currently) +// does not honor the [[deprecated]] attribute on using-declarations, and +// therefore cannot surface the deprecation to users in the middle of a +// migration. +// // 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. #if ABSL_HAVE_CPP_ATTRIBUTE(clang::annotate) -#define ABSL_REFACTOR_INLINE [[clang::annotate("inline-me")]] +#define ABSL_REFACTOR_INLINE \ + _Pragma("clang diagnostic push") /* Avoid errors on using-declarations */ \ + _Pragma("clang diagnostic ignored \"-Wcxx-attribute-extension\"") \ + [[clang::annotate("inline-me")]] _Pragma("clang diagnostic pop") #else #define ABSL_REFACTOR_INLINE #endif
diff --git a/absl/container/BUILD.bazel b/absl/container/BUILD.bazel index e748f04..a386a53 100644 --- a/absl/container/BUILD.bazel +++ b/absl/container/BUILD.bazel
@@ -89,6 +89,7 @@ ":test_allocator", "//absl/base:config", "//absl/base:exception_testing", + "//absl/base:hardening", "//absl/base:iterator_traits_test_helper", "//absl/hash:hash_testing", "//absl/memory", @@ -180,6 +181,7 @@ "//absl/base:config", "//absl/base:core_headers", "//absl/base:exception_testing", + "//absl/base:hardening", "//absl/base:iterator_traits_test_helper", "//absl/hash:hash_testing", "//absl/log:check", @@ -1382,6 +1384,7 @@ ":chunked_queue", ":test_allocator", "//absl/base:core_headers", + "//absl/base:hardening", "//absl/strings", "@googletest//:gtest", "@googletest//:gtest_main",
diff --git a/absl/container/CMakeLists.txt b/absl/container/CMakeLists.txt index 396b944..005548f 100644 --- a/absl/container/CMakeLists.txt +++ b/absl/container/CMakeLists.txt
@@ -149,6 +149,7 @@ absl::fixed_array absl::config absl::exception_testing + absl::hardening absl::hash_testing absl::iterator_traits_test_helper_internal absl::memory @@ -235,6 +236,7 @@ absl::config absl::core_headers absl::exception_testing + absl::hardening absl::hash_testing absl::inlined_vector absl::memory @@ -1234,6 +1236,7 @@ absl::chunked_queue absl::config absl::core_headers + absl::hardening absl::strings absl::test_allocator GTest::gmock_main
diff --git a/absl/container/chunked_queue_test.cc b/absl/container/chunked_queue_test.cc index bee7e03..4ba13c9 100644 --- a/absl/container/chunked_queue_test.cc +++ b/absl/container/chunked_queue_test.cc
@@ -29,6 +29,7 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" +#include "absl/base/internal/hardening.h" #include "absl/base/macros.h" #include "absl/container/internal/test_allocator.h" #include "absl/strings/str_cat.h" @@ -755,6 +756,7 @@ GTEST_SKIP() << "Not a hardened build"; } + absl::base_internal::ScopedSetAbslHardeningForTesting hardener(true); absl::chunked_queue<int> q; EXPECT_DEATH_IF_SUPPORTED(q.front(), ""); EXPECT_DEATH_IF_SUPPORTED(q.back(), "");
diff --git a/absl/container/fixed_array_test.cc b/absl/container/fixed_array_test.cc index 54e5fd6..81fb0e5 100644 --- a/absl/container/fixed_array_test.cc +++ b/absl/container/fixed_array_test.cc
@@ -31,6 +31,7 @@ #include "gtest/gtest.h" #include "absl/base/config.h" #include "absl/base/internal/exception_testing.h" +#include "absl/base/internal/hardening.h" #include "absl/base/internal/iterator_traits_test_helper.h" #include "absl/base/options.h" #include "absl/container/internal/test_allocator.h" @@ -196,6 +197,7 @@ TEST(FixedArrayTest, Hardened) { #if !defined(NDEBUG) || ABSL_OPTION_HARDENED + absl::base_internal::ScopedSetAbslHardeningForTesting hardener(true); absl::FixedArray<int> a = {1, 2, 3}; EXPECT_EQ(a[2], 3); EXPECT_DEATH_IF_SUPPORTED(a[3], "");
diff --git a/absl/container/inlined_vector_test.cc b/absl/container/inlined_vector_test.cc index 56edf09..d6cc175 100644 --- a/absl/container/inlined_vector_test.cc +++ b/absl/container/inlined_vector_test.cc
@@ -32,6 +32,7 @@ #include "gtest/gtest.h" #include "absl/base/attributes.h" #include "absl/base/internal/exception_testing.h" +#include "absl/base/internal/hardening.h" #include "absl/base/internal/iterator_traits_test_helper.h" #include "absl/base/macros.h" #include "absl/base/options.h" @@ -258,6 +259,7 @@ Fill(&v, 10); EXPECT_EQ(v[9], 9); #if !defined(NDEBUG) || ABSL_OPTION_HARDENED + absl::base_internal::ScopedSetAbslHardeningForTesting hardener(true); EXPECT_DEATH_IF_SUPPORTED(v[10], ""); EXPECT_DEATH_IF_SUPPORTED(v[static_cast<size_t>(-1)], ""); EXPECT_DEATH_IF_SUPPORTED(v.resize(v.max_size() + 1), "");
diff --git a/absl/container/internal/raw_hash_set.cc b/absl/container/internal/raw_hash_set.cc index dc58245..80efa1e 100644 --- a/absl/container/internal/raw_hash_set.cc +++ b/absl/container/internal/raw_hash_set.cc
@@ -386,7 +386,8 @@ if (capacity <= GrowthInfoLowerBound::kMaxGrowthLeftLowerBound) { return GetGrowthLeftLowerBound(); } else { - return GetGrowthLeftTotalBigCapacity(full_growth_info_ptr()); + return static_cast<size_t>( + GetGrowthLeftTotalBigCapacity(full_growth_info_ptr())); } }
diff --git a/absl/container/internal/raw_hash_set.h b/absl/container/internal/raw_hash_set.h index b99d987..7119e10 100644 --- a/absl/container/internal/raw_hash_set.h +++ b/absl/container/internal/raw_hash_set.h
@@ -230,9 +230,9 @@ #include <ranges> // NOLINT(build/c++20) #endif -#ifdef __BMI2__ -#include <bmi2intrin.h> -#endif // __BMI2__ +#if defined(__i386__) || defined(__x86_64__) +#include <immintrin.h> +#endif namespace absl { ABSL_NAMESPACE_BEGIN
diff --git a/absl/memory/memory.h b/absl/memory/memory.h index 4719364..d364df8 100644 --- a/absl/memory/memory.h +++ b/absl/memory/memory.h
@@ -94,7 +94,7 @@ // the C++14's `std::make_unique`. Now that C++11 support has been sunsetted, // `absl::make_unique` simply uses the STL-provided implementation. New code // should use `std::make_unique`. -using std::make_unique; +using std::make_unique ABSL_REFACTOR_INLINE; #if defined(__cpp_lib_smart_ptr_for_overwrite) && \ __cpp_lib_smart_ptr_for_overwrite >= 202002L
diff --git a/absl/meta/type_traits.h b/absl/meta/type_traits.h index 9f52202..be55e52 100644 --- a/absl/meta/type_traits.h +++ b/absl/meta/type_traits.h
@@ -151,6 +151,7 @@ template <class T> using decay_t ABSL_DEPRECATE_AND_INLINE() = std::decay_t<T>; +// Avoid inlining since the inliner cannot handle default arguments well. template <bool C, class T = void> using enable_if_t [[deprecated("Use std::enable_if_t instead.")]] = std::enable_if_t<C, T>;
diff --git a/absl/random/distributions.h b/absl/random/distributions.h index cfe731e..8a032df 100644 --- a/absl/random/distributions.h +++ b/absl/random/distributions.h
@@ -283,8 +283,11 @@ // // `absl::Exponential` produces a floating point number representing the // distance (time) between two consecutive events in a point process of events -// occurring continuously and independently at a constant average rate. `T` must -// be a floating point type, but may be inferred from the type of `lambda`. +// occurring continuously and independently at a constant average rate `lambda`. +// `T` must be a floating point type, but may be inferred from the type of +// `lambda`. +// +// The mean of the distribution is 1/`lambda`. // // See https://en.wikipedia.org/wiki/Exponential_distribution. //
diff --git a/absl/status/status.h b/absl/status/status.h index 88486d3..55802cd 100644 --- a/absl/status/status.h +++ b/absl/status/status.h
@@ -644,7 +644,7 @@ absl::SourceLocation loc = absl::SourceLocation::current()) { if (ok()) return; rep_ = AddSourceLocationImpl(rep_, loc); - ABSL_ATTRIBUTE_UNUSED bool okay = ok(); + [[maybe_unused]] bool okay = ok(); // This hint tells the optimizer that the status is still not ok after the // AddSourceLocation() call. This is useful when passing a known !ok status // to StatusOr. StatusOr checks for ok() on its constructor and this assume @@ -1091,7 +1091,7 @@ Status out = MakeErrorImpl<static_cast<int>(error_code)>(message, loc); // -Wassume warning complains about potential side effects of `ok()`, so use a // local to avoid that. - ABSL_ATTRIBUTE_UNUSED bool ok = out.ok(); + [[maybe_unused]] bool ok = out.ok(); ABSL_ASSUME(!ok); return out; }
diff --git a/absl/strings/BUILD.bazel b/absl/strings/BUILD.bazel index 4c974ce..def78d5 100644 --- a/absl/strings/BUILD.bazel +++ b/absl/strings/BUILD.bazel
@@ -1005,6 +1005,7 @@ "//absl/base:config", "//absl/base:core_headers", "//absl/base:endian", + "//absl/base:hardening", "//absl/base:no_destructor", "//absl/container:fixed_array", "//absl/functional:function_ref",
diff --git a/absl/strings/CMakeLists.txt b/absl/strings/CMakeLists.txt index 3041e19..e6b5da3 100644 --- a/absl/strings/CMakeLists.txt +++ b/absl/strings/CMakeLists.txt
@@ -1164,6 +1164,7 @@ absl::endian absl::fixed_array absl::function_ref + absl::hardening absl::hash absl::hash_testing absl::no_destructor
diff --git a/absl/strings/cord_test.cc b/absl/strings/cord_test.cc index 5c773e7..56db8f4 100644 --- a/absl/strings/cord_test.cc +++ b/absl/strings/cord_test.cc
@@ -38,6 +38,7 @@ #include "absl/base/attributes.h" #include "absl/base/config.h" #include "absl/base/internal/endian.h" +#include "absl/base/internal/hardening.h" #include "absl/base/macros.h" #include "absl/base/no_destructor.h" #include "absl/base/options.h" @@ -2828,6 +2829,7 @@ }()); if (!test_hardening) return; + absl::base_internal::ScopedSetAbslHardeningForTesting hardener(true); EXPECT_DEATH_IF_SUPPORTED(cord[5], ""); EXPECT_DEATH_IF_SUPPORTED(*cord.chunk_end(), ""); EXPECT_DEATH_IF_SUPPORTED(static_cast<void>(cord.chunk_end()->empty()), "");
diff --git a/absl/strings/internal/stl_type_traits.h b/absl/strings/internal/stl_type_traits.h index ae8454d..6298921 100644 --- a/absl/strings/internal/stl_type_traits.h +++ b/absl/strings/internal/stl_type_traits.h
@@ -25,6 +25,8 @@ #ifndef ABSL_STRINGS_INTERNAL_STL_TYPE_TRAITS_H_ #define ABSL_STRINGS_INTERNAL_STL_TYPE_TRAITS_H_ +#include <stddef.h> + #include <array> #include <bitset> #include <deque> @@ -43,212 +45,97 @@ ABSL_NAMESPACE_BEGIN namespace strings_internal { -template <typename C, template <typename...> class T> -struct IsSpecializationImpl : std::false_type {}; -template <template <typename...> class T, typename... Args> -struct IsSpecializationImpl<T<Args...>, T> : std::true_type {}; -template <typename C, template <typename...> class T> -using IsSpecialization = IsSpecializationImpl<std::decay_t<C>, T>; +template <typename To, typename From> +using IsCastableToDerivedSTLContainer = + std::enable_if_t<!std::is_same_v<From, To>, std::is_convertible<From, To>>; template <typename C> -struct IsArrayImpl : std::false_type {}; -template <template <typename, size_t> class A, typename T, size_t N> -struct IsArrayImpl<A<T, N>> : std::is_same<A<T, N>, std::array<T, N>> {}; -template <typename C> -using IsArray = IsArrayImpl<std::decay_t<C>>; +std::false_type CastableToDerivedSTLContainer(C*); -template <typename C> -struct IsBitsetImpl : std::false_type {}; -template <template <size_t> class B, size_t N> -struct IsBitsetImpl<B<N>> : std::is_same<B<N>, std::bitset<N>> {}; -template <typename C> -using IsBitset = IsBitsetImpl<std::decay_t<C>>; +template <typename C, typename V, size_t N> +IsCastableToDerivedSTLContainer<C, std::array<typename C::value_type, N>> +CastableToDerivedSTLContainer(std::array<V, N>*); -template <typename C> -struct IsSTLContainer - : std::disjunction< - IsArray<C>, IsBitset<C>, IsSpecialization<C, std::deque>, - IsSpecialization<C, std::forward_list>, - IsSpecialization<C, std::list>, IsSpecialization<C, std::map>, - IsSpecialization<C, std::multimap>, IsSpecialization<C, std::set>, - IsSpecialization<C, std::multiset>, - IsSpecialization<C, std::unordered_map>, - IsSpecialization<C, std::unordered_multimap>, - IsSpecialization<C, std::unordered_set>, - IsSpecialization<C, std::unordered_multiset>, - IsSpecialization<C, std::vector>> {}; +template <typename C, size_t N> +IsCastableToDerivedSTLContainer<C, std::bitset<N>> +CastableToDerivedSTLContainer(std::bitset<N>*); -template <typename C, template <typename...> class T, typename = void> -struct IsBaseOfSpecializationImpl : std::false_type {}; -// IsBaseOfSpecializationImpl needs multiple partial specializations to SFINAE -// on the existence of container dependent types and plug them into the STL -// template. -template <typename C, template <typename, typename> class T> -struct IsBaseOfSpecializationImpl< - C, T, std::void_t<typename C::value_type, typename C::allocator_type>> - : std::is_base_of<C, - T<typename C::value_type, typename C::allocator_type>> {}; -template <typename C, template <typename, typename, typename> class T> -struct IsBaseOfSpecializationImpl< - C, T, - std::void_t<typename C::key_type, typename C::key_compare, - typename C::allocator_type>> - : std::is_base_of<C, T<typename C::key_type, typename C::key_compare, - typename C::allocator_type>> {}; -template <typename C, template <typename, typename, typename, typename> class T> -struct IsBaseOfSpecializationImpl< - C, T, - std::void_t<typename C::key_type, typename C::mapped_type, +template <typename C, typename... U> +IsCastableToDerivedSTLContainer< + C, std::deque<typename C::value_type, typename C::allocator_type>> +CastableToDerivedSTLContainer(std::deque<U...>*); + +template <typename C, typename... U> +IsCastableToDerivedSTLContainer< + C, std::forward_list<typename C::value_type, typename C::allocator_type>> +CastableToDerivedSTLContainer(std::forward_list<U...>*); + +template <typename C, typename... U> +IsCastableToDerivedSTLContainer< + C, std::list<typename C::value_type, typename C::allocator_type>> +CastableToDerivedSTLContainer(std::list<U...>*); + +template <typename C, typename... U> +IsCastableToDerivedSTLContainer< + C, std::map<typename C::key_type, typename C::mapped_type, typename C::key_compare, typename C::allocator_type>> - : std::is_base_of<C, - T<typename C::key_type, typename C::mapped_type, - typename C::key_compare, typename C::allocator_type>> { -}; -template <typename C, template <typename, typename, typename, typename> class T> -struct IsBaseOfSpecializationImpl< - C, T, - // Roundabout equivalent of std::void_t that works around a template - // redefinition error from Clang. - std::conditional_t< - true, void, - std::common_type<typename C::key_type, typename C::hasher, - typename C::key_equal, typename C::allocator_type>>> - : std::is_base_of<C, T<typename C::key_type, typename C::hasher, - typename C::key_equal, typename C::allocator_type>> { -}; -template <typename C, - template <typename, typename, typename, typename, typename> class T> -struct IsBaseOfSpecializationImpl< - C, T, - std::void_t<typename C::key_type, typename C::mapped_type, - typename C::hasher, typename C::key_equal, +CastableToDerivedSTLContainer(std::map<U...>*); + +template <typename C, typename... U> +IsCastableToDerivedSTLContainer< + C, std::multimap<typename C::key_type, typename C::mapped_type, + typename C::key_compare, typename C::allocator_type>> +CastableToDerivedSTLContainer(std::multimap<U...>*); + +template <typename C, typename... U> +IsCastableToDerivedSTLContainer< + C, std::set<typename C::value_type, typename C::key_compare, typename C::allocator_type>> - : std::is_base_of<C, T<typename C::key_type, typename C::mapped_type, - typename C::hasher, typename C::key_equal, - typename C::allocator_type>> {}; -template <typename C, template <typename...> class T> -using IsBaseOfSpecialization = IsBaseOfSpecializationImpl<std::decay_t<C>, T>; +CastableToDerivedSTLContainer(std::set<U...>*); -template <typename C> -struct IsBaseOfArrayImpl : std::false_type {}; -template <template <typename, size_t> class A, typename T, size_t N> -struct IsBaseOfArrayImpl<A<T, N>> : std::is_base_of<A<T, N>, std::array<T, N>> { -}; -template <typename C> -using IsBaseOfArray = IsBaseOfArrayImpl<std::decay_t<C>>; +template <typename C, typename... U> +IsCastableToDerivedSTLContainer< + C, std::multiset<typename C::value_type, typename C::key_compare, + typename C::allocator_type>> +CastableToDerivedSTLContainer(std::multiset<U...>*); -template <typename C> -struct IsBaseOfBitsetImpl : std::false_type {}; -template <template <size_t> class B, size_t N> -struct IsBaseOfBitsetImpl<B<N>> : std::is_base_of<B<N>, std::bitset<N>> {}; -template <typename C> -using IsBaseOfBitset = IsBaseOfBitsetImpl<std::decay_t<C>>; +template <typename C, typename... U> +IsCastableToDerivedSTLContainer< + C, std::unordered_map<typename C::key_type, typename C::mapped_type, + typename C::hasher, typename C::key_equal, + typename C::allocator_type>> +CastableToDerivedSTLContainer(std::unordered_map<U...>*); -template <typename C> -struct IsBaseOfSTLContainer - : std::disjunction<IsBaseOfArray<C>, IsBaseOfBitset<C>, - IsBaseOfSpecialization<C, std::deque>, - IsBaseOfSpecialization<C, std::forward_list>, - IsBaseOfSpecialization<C, std::list>, - IsBaseOfSpecialization<C, std::map>, - IsBaseOfSpecialization<C, std::multimap>, - IsBaseOfSpecialization<C, std::set>, - IsBaseOfSpecialization<C, std::multiset>, - IsBaseOfSpecialization<C, std::unordered_map>, - IsBaseOfSpecialization<C, std::unordered_multimap>, - IsBaseOfSpecialization<C, std::unordered_set>, - IsBaseOfSpecialization<C, std::unordered_multiset>, - IsBaseOfSpecialization<C, std::vector>> {}; - -template <typename C, template <typename...> class T, typename = void> -struct IsConvertibleToSpecializationImpl : std::false_type {}; -// IsConvertibleToSpecializationImpl needs multiple partial specializations to -// SFINAE on the existence of container dependent types and plug them into the -// STL template. -template <typename C, template <typename, typename> class T> -struct IsConvertibleToSpecializationImpl< - C, T, std::void_t<typename C::value_type, typename C::allocator_type>> - : std::is_convertible< - C, T<typename C::value_type, typename C::allocator_type>> {}; -template <typename C, template <typename, typename, typename> class T> -struct IsConvertibleToSpecializationImpl< - C, T, - std::void_t<typename C::key_type, typename C::key_compare, - typename C::allocator_type>> - : std::is_convertible<C, T<typename C::key_type, typename C::key_compare, - typename C::allocator_type>> {}; -template <typename C, template <typename, typename, typename, typename> class T> -struct IsConvertibleToSpecializationImpl< - C, T, - std::void_t<typename C::key_type, typename C::mapped_type, - typename C::key_compare, typename C::allocator_type>> - : std::is_convertible< - C, T<typename C::key_type, typename C::mapped_type, - typename C::key_compare, typename C::allocator_type>> {}; -template <typename C, template <typename, typename, typename, typename> class T> -struct IsConvertibleToSpecializationImpl< - C, T, - // Roundabout equivalent of std::void_t that works around a template - // redefinition error from Clang. - std::conditional_t< - true, void, - std::common_type<typename C::key_type, typename C::hasher, - typename C::key_equal, typename C::allocator_type>>> - : std::is_convertible< - C, T<typename C::key_type, typename C::hasher, typename C::key_equal, - typename C::allocator_type>> {}; -template <typename C, - template <typename, typename, typename, typename, typename> class T> -struct IsConvertibleToSpecializationImpl< - C, T, - std::void_t<typename C::key_type, typename C::mapped_type, - typename C::hasher, typename C::key_equal, - typename C::allocator_type>> - : std::is_convertible<C, T<typename C::key_type, typename C::mapped_type, +template <typename C, typename... U> +IsCastableToDerivedSTLContainer< + C, std::unordered_multimap<typename C::key_type, typename C::mapped_type, typename C::hasher, typename C::key_equal, - typename C::allocator_type>> {}; -template <typename C, template <typename...> class T> -using IsConvertibleToSpecialization = - IsConvertibleToSpecializationImpl<std::decay_t<C>, T>; + typename C::allocator_type>> +CastableToDerivedSTLContainer(std::unordered_multimap<U...>*); -template <typename C> -struct IsConvertibleToArrayImpl : std::false_type {}; -template <template <typename, size_t> class A, typename T, size_t N> -struct IsConvertibleToArrayImpl<A<T, N>> - : std::is_convertible<A<T, N>, std::array<T, N>> {}; -template <typename C> -using IsConvertibleToArray = IsConvertibleToArrayImpl<std::decay_t<C>>; +template <typename C, typename... U> +IsCastableToDerivedSTLContainer< + C, std::unordered_set<typename C::key_type, typename C::hasher, + typename C::key_equal, typename C::allocator_type>> +CastableToDerivedSTLContainer(std::unordered_set<U...>*); -template <typename C> -struct IsConvertibleToBitsetImpl : std::false_type {}; -template <template <size_t> class B, size_t N> -struct IsConvertibleToBitsetImpl<B<N>> - : std::is_convertible<B<N>, std::bitset<N>> {}; -template <typename C> -using IsConvertibleToBitset = IsConvertibleToBitsetImpl<std::decay_t<C>>; +template <typename C, typename... U> +IsCastableToDerivedSTLContainer< + C, + std::unordered_multiset<typename C::key_type, typename C::hasher, + typename C::key_equal, typename C::allocator_type>> +CastableToDerivedSTLContainer(std::unordered_multiset<U...>*); -template <typename C> -struct IsConvertibleToSTLContainer - : std::disjunction< - IsConvertibleToArray<C>, IsConvertibleToBitset<C>, - IsConvertibleToSpecialization<C, std::deque>, - IsConvertibleToSpecialization<C, std::forward_list>, - IsConvertibleToSpecialization<C, std::list>, - IsConvertibleToSpecialization<C, std::map>, - IsConvertibleToSpecialization<C, std::multimap>, - IsConvertibleToSpecialization<C, std::set>, - IsConvertibleToSpecialization<C, std::multiset>, - IsConvertibleToSpecialization<C, std::unordered_map>, - IsConvertibleToSpecialization<C, std::unordered_multimap>, - IsConvertibleToSpecialization<C, std::unordered_set>, - IsConvertibleToSpecialization<C, std::unordered_multiset>, - IsConvertibleToSpecialization<C, std::vector>> {}; +template <typename C, typename... U> +IsCastableToDerivedSTLContainer< + C, std::vector<typename C::value_type, typename C::allocator_type>> +CastableToDerivedSTLContainer(std::vector<U...>*); template <typename C> struct IsStrictlyBaseOfAndConvertibleToSTLContainer - : std::conjunction<std::negation<IsSTLContainer<C>>, - IsBaseOfSTLContainer<C>, - IsConvertibleToSTLContainer<C>> {}; + : decltype(strings_internal::CastableToDerivedSTLContainer< + absl::remove_cvref_t<C>>( + std::declval<absl::remove_cvref_t<C>*>())) {}; } // namespace strings_internal ABSL_NAMESPACE_END
diff --git a/absl/time/time.h b/absl/time/time.h index 03cf67a..fc1456b 100644 --- a/absl/time/time.h +++ b/absl/time/time.h
@@ -1064,11 +1064,6 @@ // the format specified by `absl::ParseTime()`. std::string AbslUnparseFlag(Time t); -ABSL_DEPRECATED("Use AbslParseFlag() instead.") -bool ParseFlag(const std::string& text, Time* t, std::string* error); -ABSL_DEPRECATED("Use AbslUnparseFlag() instead.") -std::string UnparseFlag(Time t); - // TimeZone // // The `absl::TimeZone` is an opaque, small, value-type class representing a
diff --git a/absl/types/BUILD.bazel b/absl/types/BUILD.bazel index 2670c0c..1d937e7 100644 --- a/absl/types/BUILD.bazel +++ b/absl/types/BUILD.bazel
@@ -107,6 +107,7 @@ "//absl/base:config", "//absl/base:core_headers", "//absl/base:exception_testing", + "//absl/base:hardening", "//absl/container:fixed_array", "//absl/container:inlined_vector", "//absl/hash", @@ -149,6 +150,7 @@ "//absl/base:config", "//absl/base:core_headers", "//absl/base:exception_testing", + "//absl/base:hardening", "//absl/base:raw_logging_internal", "//absl/hash:hash_testing", "//absl/meta:type_traits",
diff --git a/absl/types/CMakeLists.txt b/absl/types/CMakeLists.txt index 79a4c19..28e1fe8 100644 --- a/absl/types/CMakeLists.txt +++ b/absl/types/CMakeLists.txt
@@ -58,6 +58,7 @@ absl::config absl::core_headers absl::exception_testing + absl::hardening absl::hash_testing absl::raw_logging_internal absl::span @@ -101,6 +102,7 @@ absl::core_headers absl::exception_testing absl::fixed_array + absl::hardening absl::hash absl::inlined_vector absl::hash_testing
diff --git a/absl/types/any_span_test.cc b/absl/types/any_span_test.cc index e5960c6..fe12b74 100644 --- a/absl/types/any_span_test.cc +++ b/absl/types/any_span_test.cc
@@ -30,6 +30,7 @@ #include "gtest/gtest.h" #include "absl/base/config.h" #include "absl/base/internal/exception_testing.h" +#include "absl/base/internal/hardening.h" #include "absl/base/internal/raw_logging.h" #include "absl/base/macros.h" #include "absl/hash/hash_testing.h" @@ -1041,6 +1042,7 @@ EXPECT_THAT(span.subspan(3, AnySpan<int>::npos), ElementsAre()); #if GTEST_HAS_DEATH_TEST if (IsHardened()) { + auto hardener = absl::base_internal::ScopedSetAbslHardeningForTesting(true); EXPECT_DEATH(span.subspan(4, AnySpan<int>::npos), ""); EXPECT_DEATH(span.subspan(AnySpan<int>::npos, AnySpan<int>::npos), ""); } @@ -1052,6 +1054,7 @@ AnySpan<int> span(v); #if GTEST_HAS_DEATH_TEST if (IsHardened()) { + auto hardener = absl::base_internal::ScopedSetAbslHardeningForTesting(true); EXPECT_DEATH(span.subspan(5, 0), ""); EXPECT_DEATH(span.subspan(5, 1), ""); EXPECT_DEATH(span.subspan(AnySpan<int>::npos, 0), "");
diff --git a/absl/types/optional.h b/absl/types/optional.h index c70efb1..a5b15b8 100644 --- a/absl/types/optional.h +++ b/absl/types/optional.h
@@ -56,12 +56,13 @@ return std::make_optional<T>(il, std::forward<Args>(args)...); } -using std::nullopt; +using std::nullopt ABSL_REFACTOR_INLINE; using nullopt_t ABSL_REFACTOR_INLINE = std::nullopt_t; -using std::optional; +using std::optional ABSL_REFACTOR_INLINE; + ABSL_NAMESPACE_END } // namespace absl
diff --git a/absl/types/span.h b/absl/types/span.h index 3b291a9..c33e294 100644 --- a/absl/types/span.h +++ b/absl/types/span.h
@@ -719,6 +719,9 @@ // return absl::MakeSpan(&array[0], num_elements_); // } // +// NOTE: To avoid undefined behavior if the container is empty, use `.data()` +// or pass the container directly instead of using `&v[0]` or `&v[v.size()]`. +// template <int&... ExplicitArgumentBarrier, typename T> constexpr Span<T> MakeSpan(T* absl_nullable ptr ABSL_ATTRIBUTE_LIFETIME_BOUND, size_t size) noexcept {
diff --git a/absl/types/span_test.cc b/absl/types/span_test.cc index 5a8b82e..051e673 100644 --- a/absl/types/span_test.cc +++ b/absl/types/span_test.cc
@@ -27,6 +27,7 @@ #include "absl/base/attributes.h" #include "absl/base/config.h" #include "absl/base/internal/exception_testing.h" +#include "absl/base/internal/hardening.h" #include "absl/base/options.h" #include "absl/container/fixed_array.h" #include "absl/container/inlined_vector.h" @@ -250,6 +251,7 @@ EXPECT_EQ(s.back(), s[9]); #if !defined(NDEBUG) || ABSL_OPTION_HARDENED + absl::base_internal::ScopedSetAbslHardeningForTesting hardener(true); EXPECT_DEATH_IF_SUPPORTED(s[-1], ""); EXPECT_DEATH_IF_SUPPORTED(s[10], ""); #endif @@ -291,6 +293,7 @@ EXPECT_EQ(v, MakeRamp(20, 1)); #if !defined(NDEBUG) || ABSL_OPTION_HARDENED + absl::base_internal::ScopedSetAbslHardeningForTesting hardener(true); absl::Span<int> prefix_death(v); EXPECT_DEATH_IF_SUPPORTED(prefix_death.remove_prefix(21), ""); absl::Span<int> suffix_death(v);