Export of internal Abseil changes.

--
5da9755667df37e38ccaf6938c9f408e294110bb by Shaindel Schwartz <shaindel@google.com>:

Import of CCTZ from GitHub.

PiperOrigin-RevId: 232942734

--
b6fb275769c66fdd2bd92b119198c59e9a7dd737 by Samuel Benzaquen <sbenza@google.com>:

Fix integral underflow when from-arg width is INT_MIN.

PiperOrigin-RevId: 232888037

--
4135dbba4a26c4642277fc2a7e2a833d593daa1c by Abseil Team <absl-team@google.com>:

Add the insert_return_type alias to raw_hash_set.

PiperOrigin-RevId: 232683892

--
0b120b7d3693800bbb886f6fc607ae54a9338cb1 by Abseil Team <absl-team@google.com>:

Macros to detect and disabled SafeStack
https://clang.llvm.org/docs/SafeStack.html

PiperOrigin-RevId: 232680114

--
a77b3fb533a9e37966d1d6ef5ccd09c73fff2ca1 by Abseil Team <absl-team@google.com>:

Avoid potential red zone clobber

Pushing on the stack on x86-64 may clobber local variables held below
%rsp in the red zone. Avoid this by using lea on x86-64.

PiperOrigin-RevId: 232592478

--
bf326a0eefa92f4e704287563df0c5a5b1873b6d by Eric Fiselier <ericwf@google.com>:

Add additional tests for AbslHashValue.

PiperOrigin-RevId: 232344325

--
816e4f98fd7632c944c779db87b7dac4e138afcf by Eric Fiselier <ericwf@google.com>:

Avoid upcoming GCC 9.0 warnings about base class init.

Currently, in trunk, GCC has a new warning under -Wextra
that diagnoses when a derived class fails to explicitly
initialize the base class in a constructor initializer list.

This patch avoids this warning.

PiperOrigin-RevId: 232327626

--
779c0f44b3c2b7a04d4bdf978641eb8180515bf6 by Eric Fiselier <ericwf@google.com>:

Guard against C++2a char8_t change.

PiperOrigin-RevId: 232326178

--
41e5395b85bbbfb5bf418cc21b04ad4ccb15a284 by Eric Fiselier <ericwf@google.com>:

Avoid Clang Warning

PiperOrigin-RevId: 232138866
GitOrigin-RevId: 5da9755667df37e38ccaf6938c9f408e294110bb
Change-Id: I49ee4f58db177b81b039d7d949f671c97c5a7933
This commit is contained in:
Abseil Team 2019-02-07 14:13:06 -08:00 committed by Shaindel Schwartz
parent d78310fe5a
commit 2901ec32a9
18 changed files with 287 additions and 145 deletions

View file

@ -287,6 +287,17 @@
#define ABSL_ATTRIBUTE_NO_SANITIZE_CFI
#endif
// ABSL_ATTRIBUTE_NO_SANITIZE_SAFESTACK
//
// Tells the SafeStack to not instrument a given function.
// See https://clang.llvm.org/docs/SafeStack.html for details.
#if defined(__GNUC__) && defined(SAFESTACK_SANITIZER)
#define ABSL_ATTRIBUTE_NO_SANITIZE_SAFESTACK \
__attribute__((no_sanitize("safe-stack")))
#else
#define ABSL_ATTRIBUTE_NO_SANITIZE_SAFESTACK
#endif
// ABSL_ATTRIBUTE_RETURNS_NONNULL
//
// Tells the compiler that a particular function never returns a null pointer.

View file

@ -542,6 +542,7 @@ cc_library(
copts = ABSL_DEFAULT_COPTS,
deps = [
"//absl/meta:type_traits",
"//absl/types:optional",
],
)
@ -565,7 +566,6 @@ cc_library(
"//absl/base:endian",
"//absl/memory",
"//absl/meta:type_traits",
"//absl/types:optional",
"//absl/utility",
],
)

View file

@ -869,4 +869,21 @@ TEST(FixedArrayTest, AddressSanitizerAnnotations4) {
}
#endif // ADDRESS_SANITIZER
TEST(FixedArrayTest, AbslHashValueWorks) {
using V = absl::FixedArray<int>;
std::vector<V> cases;
// Generate a variety of vectors some of these are small enough for the inline
// space but are stored out of line.
for (int i = 0; i < 10; ++i) {
V v(i);
for (int j = 0; j < i; ++j) {
v[j] = j;
}
cases.push_back(v);
}
EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(cases));
}
} // namespace

View file

@ -1762,4 +1762,23 @@ TEST(AllocatorSupportTest, SizeAllocConstructor) {
}
}
TEST(InlinedVectorTest, AbslHashValueWorks) {
using V = absl::InlinedVector<int, 4>;
std::vector<V> cases;
// Generate a variety of vectors some of these are small enough for the inline
// space but are stored out of line.
for (int i = 0; i < 10; ++i) {
V v;
for (int j = 0; j < i; ++j) {
v.push_back(j);
}
cases.push_back(v);
v.resize(i % 4);
cases.push_back(v);
}
EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(cases));
}
} // anonymous namespace

View file

@ -18,6 +18,7 @@
#include <type_traits>
#include "absl/meta/type_traits.h"
#include "absl/types/optional.h"
namespace absl {
namespace container_internal {
@ -42,7 +43,140 @@ struct KeyArg<false> {
using type = key_type;
};
// The node_handle concept from C++17.
// We specialize node_handle for sets and maps. node_handle_base holds the
// common API of both.
template <typename PolicyTraits, typename Alloc>
class node_handle_base {
protected:
using slot_type = typename PolicyTraits::slot_type;
public:
using allocator_type = Alloc;
constexpr node_handle_base() {}
node_handle_base(node_handle_base&& other) noexcept {
*this = std::move(other);
}
~node_handle_base() { destroy(); }
node_handle_base& operator=(node_handle_base&& other) noexcept {
destroy();
if (!other.empty()) {
alloc_ = other.alloc_;
PolicyTraits::transfer(alloc(), slot(), other.slot());
other.reset();
}
return *this;
}
bool empty() const noexcept { return !alloc_; }
explicit operator bool() const noexcept { return !empty(); }
allocator_type get_allocator() const { return *alloc_; }
protected:
friend struct CommonAccess;
node_handle_base(const allocator_type& a, slot_type* s) : alloc_(a) {
PolicyTraits::transfer(alloc(), slot(), s);
}
void destroy() {
if (!empty()) {
PolicyTraits::destroy(alloc(), slot());
reset();
}
}
void reset() {
assert(alloc_.has_value());
alloc_ = absl::nullopt;
}
slot_type* slot() const {
assert(!empty());
return reinterpret_cast<slot_type*>(std::addressof(slot_space_));
}
allocator_type* alloc() { return std::addressof(*alloc_); }
private:
absl::optional<allocator_type> alloc_;
mutable absl::aligned_storage_t<sizeof(slot_type), alignof(slot_type)>
slot_space_;
};
// For sets.
template <typename Policy, typename PolicyTraits, typename Alloc,
typename = void>
class node_handle : public node_handle_base<PolicyTraits, Alloc> {
using Base = typename node_handle::node_handle_base;
public:
using value_type = typename PolicyTraits::value_type;
constexpr node_handle() {}
value_type& value() const { return PolicyTraits::element(this->slot()); }
private:
friend struct CommonAccess;
node_handle(const Alloc& a, typename Base::slot_type* s) : Base(a, s) {}
};
// For maps.
template <typename Policy, typename PolicyTraits, typename Alloc>
class node_handle<Policy, PolicyTraits, Alloc,
absl::void_t<typename Policy::mapped_type>>
: public node_handle_base<PolicyTraits, Alloc> {
using Base = typename node_handle::node_handle_base;
public:
using key_type = typename Policy::key_type;
using mapped_type = typename Policy::mapped_type;
constexpr node_handle() {}
auto key() const -> decltype(PolicyTraits::key(this->slot())) {
return PolicyTraits::key(this->slot());
}
mapped_type& mapped() const {
return PolicyTraits::value(&PolicyTraits::element(this->slot()));
}
private:
friend struct CommonAccess;
node_handle(const Alloc& a, typename Base::slot_type* s) : Base(a, s) {}
};
// Provide access to non-public node-handle functions.
struct CommonAccess {
template <typename Node>
static auto GetSlot(const Node& node) -> decltype(node.slot()) {
return node.slot();
}
template <typename Node>
static void Reset(Node* node) {
node->reset();
}
template <typename T, typename... Args>
static T Make(Args&&... args) {
return T(std::forward<Args>(args)...);
}
};
// Implement the insert_return_type<> concept of C++17.
template <class Iterator, class NodeType>
struct InsertReturnType {
Iterator position;
bool inserted;
NodeType node;
};
} // namespace container_internal
} // namespace absl
} // namespace absl
#endif // ABSL_CONTAINER_INTERNAL_CONTAINER_H_

View file

@ -254,7 +254,7 @@ HashtablezInfo* SampleSlow(int64_t* next_sample) {
}
#if ABSL_PER_THREAD_TLS == 1
ABSL_PER_THREAD_TLS_KEYWORD int64_t next_sample = 0;
ABSL_PER_THREAD_TLS_KEYWORD int64_t global_next_sample = 0;
#endif // ABSL_PER_THREAD_TLS == 1
void UnsampleSlow(HashtablezInfo* info) {

View file

@ -146,23 +146,23 @@ class HashtablezInfoHandle {
HashtablezInfo* info_;
};
// Returns an RAII sampling handle that manages registration and unregistation
// with the global sampler.
#if ABSL_PER_THREAD_TLS == 1
extern ABSL_PER_THREAD_TLS_KEYWORD int64_t next_sample;
extern ABSL_PER_THREAD_TLS_KEYWORD int64_t global_next_sample;
#endif // ABSL_PER_THREAD_TLS
// Returns an RAII sampling handle that manages registration and unregistation
// with the global sampler.
inline HashtablezInfoHandle Sample() {
#if ABSL_PER_THREAD_TLS == 0
static auto* mu = new absl::Mutex;
static int64_t next_sample = 0;
static int64_t global_next_sample = 0;
absl::MutexLock l(mu);
#endif // !ABSL_HAVE_THREAD_LOCAL
if (ABSL_PREDICT_TRUE(--next_sample > 0)) {
if (ABSL_PREDICT_TRUE(--global_next_sample > 0)) {
return HashtablezInfoHandle(nullptr);
}
return HashtablezInfoHandle(SampleSlow(&next_sample));
return HashtablezInfoHandle(SampleSlow(&global_next_sample));
}
// Holds samples and their associated stack traces with a soft limit of

View file

@ -115,7 +115,6 @@
#include "absl/container/internal/layout.h"
#include "absl/memory/memory.h"
#include "absl/meta/type_traits.h"
#include "absl/types/optional.h"
#include "absl/utility/utility.h"
namespace absl {
@ -502,126 +501,6 @@ inline size_t GrowthToLowerboundCapacity(size_t growth) {
return growth + static_cast<size_t>((static_cast<int64_t>(growth) - 1) / 7);
}
// The node_handle concept from C++17.
// We specialize node_handle for sets and maps. node_handle_base holds the
// common API of both.
template <typename Policy, typename Alloc>
class node_handle_base {
protected:
using PolicyTraits = hash_policy_traits<Policy>;
using slot_type = typename PolicyTraits::slot_type;
public:
using allocator_type = Alloc;
constexpr node_handle_base() {}
node_handle_base(node_handle_base&& other) noexcept {
*this = std::move(other);
}
~node_handle_base() { destroy(); }
node_handle_base& operator=(node_handle_base&& other) {
destroy();
if (!other.empty()) {
alloc_ = other.alloc_;
PolicyTraits::transfer(alloc(), slot(), other.slot());
other.reset();
}
return *this;
}
bool empty() const noexcept { return !alloc_; }
explicit operator bool() const noexcept { return !empty(); }
allocator_type get_allocator() const { return *alloc_; }
protected:
template <typename, typename, typename, typename>
friend class raw_hash_set;
node_handle_base(const allocator_type& a, slot_type* s) : alloc_(a) {
PolicyTraits::transfer(alloc(), slot(), s);
}
void destroy() {
if (!empty()) {
PolicyTraits::destroy(alloc(), slot());
reset();
}
}
void reset() {
assert(alloc_.has_value());
alloc_ = absl::nullopt;
}
slot_type* slot() const {
assert(!empty());
return reinterpret_cast<slot_type*>(std::addressof(slot_space_));
}
allocator_type* alloc() { return std::addressof(*alloc_); }
private:
absl::optional<allocator_type> alloc_;
mutable absl::aligned_storage_t<sizeof(slot_type), alignof(slot_type)>
slot_space_;
};
// For sets.
template <typename Policy, typename Alloc, typename = void>
class node_handle : public node_handle_base<Policy, Alloc> {
using Base = typename node_handle::node_handle_base;
public:
using value_type = typename Base::PolicyTraits::value_type;
constexpr node_handle() {}
value_type& value() const {
return Base::PolicyTraits::element(this->slot());
}
private:
template <typename, typename, typename, typename>
friend class raw_hash_set;
node_handle(const Alloc& a, typename Base::slot_type* s) : Base(a, s) {}
};
// For maps.
template <typename Policy, typename Alloc>
class node_handle<Policy, Alloc, absl::void_t<typename Policy::mapped_type>>
: public node_handle_base<Policy, Alloc> {
using Base = typename node_handle::node_handle_base;
public:
using key_type = typename Policy::key_type;
using mapped_type = typename Policy::mapped_type;
constexpr node_handle() {}
auto key() const -> decltype(Base::PolicyTraits::key(this->slot())) {
return Base::PolicyTraits::key(this->slot());
}
mapped_type& mapped() const {
return Base::PolicyTraits::value(
&Base::PolicyTraits::element(this->slot()));
}
private:
template <typename, typename, typename, typename>
friend class raw_hash_set;
node_handle(const Alloc& a, typename Base::slot_type* s) : Base(a, s) {}
};
// Implement the insert_return_type<> concept of C++17.
template <class Iterator, class NodeType>
struct insert_return_type {
Iterator position;
bool inserted;
NodeType node;
};
// Policy: a policy defines how to perform different operations on
// the slots of the hashtable (see hash_policy_traits.h for the full interface
// of policy).
@ -828,7 +707,8 @@ class raw_hash_set {
iterator inner_;
};
using node_type = container_internal::node_handle<Policy, Alloc>;
using node_type = node_handle<Policy, hash_policy_traits<Policy>, Alloc>;
using insert_return_type = InsertReturnType<iterator, node_type>;
raw_hash_set() noexcept(
std::is_nothrow_default_constructible<hasher>::value&&
@ -1136,13 +1016,14 @@ class raw_hash_set {
insert(ilist.begin(), ilist.end());
}
insert_return_type<iterator, node_type> insert(node_type&& node) {
insert_return_type insert(node_type&& node) {
if (!node) return {end(), false, node_type()};
const auto& elem = PolicyTraits::element(node.slot());
const auto& elem = PolicyTraits::element(CommonAccess::GetSlot(node));
auto res = PolicyTraits::apply(
InsertSlot<false>{*this, std::move(*node.slot())}, elem);
InsertSlot<false>{*this, std::move(*CommonAccess::GetSlot(node))},
elem);
if (res.second) {
node.reset();
CommonAccess::Reset(&node);
return {res.first, true, node_type()};
} else {
return {res.first, false, std::move(node)};
@ -1306,7 +1187,8 @@ class raw_hash_set {
}
node_type extract(const_iterator position) {
node_type node(alloc_ref(), position.inner_.slot_);
auto node =
CommonAccess::Make<node_type>(alloc_ref(), position.inner_.slot_);
erase_meta_only(position);
return node;
}

View file

@ -1706,7 +1706,7 @@ TEST(Nodes, ExtractInsert) {
EXPECT_FALSE(node.empty());
StringTable t2;
auto res = t2.insert(std::move(node));
StringTable::insert_return_type res = t2.insert(std::move(node));
EXPECT_TRUE(res.inserted);
EXPECT_THAT(*res.position, Pair(k0, ""));
EXPECT_FALSE(res.node);

View file

@ -392,16 +392,20 @@ TEST(Symbolize, ForEachSection) {
extern "C" {
inline void *ABSL_ATTRIBUTE_ALWAYS_INLINE inline_func() {
void *pc = nullptr;
#if defined(__i386__) || defined(__x86_64__)
__asm__ __volatile__("call 1f; 1: pop %0" : "=r"(pc));
#if defined(__i386__)
__asm__ __volatile__("call 1f;\n 1: pop %[PC]" : [ PC ] "=r"(pc));
#elif defined(__x86_64__)
__asm__ __volatile__("leaq 0(%%rip),%[PC];\n" : [ PC ] "=r"(pc));
#endif
return pc;
}
void *ABSL_ATTRIBUTE_NOINLINE non_inline_func() {
void *pc = nullptr;
#if defined(__i386__) || defined(__x86_64__)
__asm__ __volatile__("call 1f; 1: pop %0" : "=r"(pc));
#if defined(__i386__)
__asm__ __volatile__("call 1f;\n 1: pop %[PC]" : [ PC ] "=r"(pc));
#elif defined(__x86_64__)
__asm__ __volatile__("leaq 0(%%rip),%[PC];\n" : [ PC ] "=r"(pc));
#endif
return pc;
}

View file

@ -440,4 +440,29 @@ TEST(Uint128, NumericLimitsTest) {
EXPECT_EQ(absl::Uint128Max(), std::numeric_limits<absl::uint128>::max());
}
TEST(Uint128, Hash) {
EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly({
// Some simple values
absl::uint128{0},
absl::uint128{1},
~absl::uint128{},
// 64 bit limits
absl::uint128{std::numeric_limits<int64_t>::max()},
absl::uint128{std::numeric_limits<uint64_t>::max()} + 0,
absl::uint128{std::numeric_limits<uint64_t>::max()} + 1,
absl::uint128{std::numeric_limits<uint64_t>::max()} + 2,
// Keeping high same
absl::uint128{1} << 62,
absl::uint128{1} << 63,
// Keeping low same
absl::uint128{1} << 64,
absl::uint128{1} << 65,
// 128 bit limits
std::numeric_limits<absl::uint128>::max(),
std::numeric_limits<absl::uint128>::max() - 1,
std::numeric_limits<absl::uint128>::min() + 1,
std::numeric_limits<absl::uint128>::min(),
}));
}
} // namespace

View file

@ -53,7 +53,8 @@ inline bool ArgContext::Bind(const UnboundConversion* unbound,
// "A negative field width is taken as a '-' flag followed by a
// positive field width."
force_left = true;
width = -width;
// Make sure we don't overflow the width when negating it.
width = -std::max(width, -std::numeric_limits<int>::max());
}
}

View file

@ -1,6 +1,7 @@
#include "absl/strings/internal/str_format/bind.h"
#include <string.h>
#include <limits>
#include "gtest/gtest.h"
@ -91,6 +92,20 @@ TEST_F(FormatBindTest, BindSingle) {
}
}
TEST_F(FormatBindTest, WidthUnderflowRegression) {
UnboundConversion props;
BoundConversion bound;
int next = 0;
const int args_i[] = {std::numeric_limits<int>::min(), 17};
const FormatArgImpl args[] = {FormatArgImpl(args_i[0]),
FormatArgImpl(args_i[1])};
ASSERT_TRUE(Extract("*d", &props, &next));
ASSERT_TRUE(BindWithPack(&props, args, &bound));
EXPECT_EQ(bound.width(), std::numeric_limits<int>::max());
EXPECT_EQ(bound.arg(), args + 1);
}
TEST_F(FormatBindTest, FormatPack) {
struct Expectation {
int line;

View file

@ -22,6 +22,11 @@
namespace {
#if !defined(__cpp_char8_t)
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wc++2a-compat"
#endif
TEST(EncodeUTF8Char, BasicFunction) {
std::pair<char32_t, std::string> tests[] = {{0x0030, u8"\u0030"},
{0x00A3, u8"\u00A3"},
@ -53,5 +58,9 @@ TEST(EncodeUTF8Char, BasicFunction) {
EXPECT_LE(absl::strings_internal::EncodeUTF8Char(buf2, -1),
absl::strings_internal::kMaxEncodedUTF8Size);
}
#if defined(__clang__)
#pragma clang diagnostic pop
#endif
#endif // !defined(__cpp_char8_t)
} // namespace

View file

@ -647,6 +647,11 @@ TEST(Split, StringDelimiter) {
}
}
#if !defined(__cpp_char8_t)
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wc++2a-compat"
#endif
TEST(Split, UTF8) {
// Tests splitting utf8 strings and utf8 delimiters.
std::string utf8_string = u8"\u03BA\u1F79\u03C3\u03BC\u03B5";
@ -673,6 +678,10 @@ TEST(Split, UTF8) {
EXPECT_THAT(v, ElementsAre("Foo", u8"h\u00E4llo", u8"th\u4E1Ere"));
}
}
#if defined(__clang__)
#pragma clang diagnostic pop
#endif
#endif // !defined(__cpp_char8_t)
TEST(Split, EmptyStringDelimiter) {
{

View file

@ -54,7 +54,7 @@ ZoneInfoSourceFactory default_factory = DefaultFactory;
#pragma comment( \
linker, \
"/alternatename:?zone_info_source_factory@cctz_extension@time_internal@absl@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@absl@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@5@ABV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@absl@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@5@@ZA=?default_factory@cctz_extension@time_internal@absl@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@absl@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@5@ABV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@absl@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@5@@ZA")
#elif defined(_M_IA_64) || defined(_M_AMD64)
#elif defined(_M_IA_64) || defined(_M_AMD64) || defined(_M_ARM64)
#pragma comment( \
linker, \
"/alternatename:?zone_info_source_factory@cctz_extension@time_internal@absl@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@absl@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@5@AEBV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@absl@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@5@@ZEA=?default_factory@cctz_extension@time_internal@absl@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@absl@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@5@AEBV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@absl@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@5@@ZEA")

View file

@ -295,7 +295,7 @@ class optional_data<T, false> : public optional_data_base<T> {
optional_data() = default;
optional_data(const optional_data& rhs) {
optional_data(const optional_data& rhs) : optional_data_base<T>() {
if (rhs.engaged_) {
this->construct(rhs.data_);
}
@ -303,7 +303,8 @@ class optional_data<T, false> : public optional_data_base<T> {
optional_data(optional_data&& rhs) noexcept(
absl::default_allocator_is_nothrow::value ||
std::is_nothrow_move_constructible<T>::value) {
std::is_nothrow_move_constructible<T>::value)
: optional_data_base<T>() {
if (rhs.engaged_) {
this->construct(std::move(rhs.data_));
}

View file

@ -779,4 +779,19 @@ TEST(Span, SpanSize) {
EXPECT_LE(sizeof(absl::Span<BigStruct>), 2 * sizeof(void*));
}
TEST(Span, Hash) {
int array[] = {1, 2, 3, 4};
int array2[] = {1, 2, 3};
using T = absl::Span<const int>;
EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(
{// Empties
T(), T(nullptr, 0), T(array, 0), T(array2, 0),
// Different array with same value
T(array, 3), T(array2), T({1, 2, 3}),
// Same array, but different length
T(array, 1), T(array, 2),
// Same length, but different array
T(array + 1, 2), T(array + 2, 2)}));
}
} // namespace