summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMake/AbseilHelpers.cmake83
-rw-r--r--absl/algorithm/container.h78
-rw-r--r--absl/base/call_once.h8
-rw-r--r--absl/base/config.h24
-rw-r--r--absl/container/BUILD.bazel4
-rw-r--r--absl/container/flat_hash_map.h12
-rw-r--r--absl/container/flat_hash_set.h12
-rw-r--r--absl/container/inlined_vector.h41
-rw-r--r--absl/container/internal/hash_generator_testing.cc8
-rw-r--r--absl/container/internal/hash_generator_testing.h8
-rw-r--r--absl/container/node_hash_map.h14
-rw-r--r--absl/container/node_hash_set.h11
-rw-r--r--absl/numeric/int128.h11
-rw-r--r--absl/synchronization/lifetime_test.cc15
-rw-r--r--absl/synchronization/notification.cc1
-rw-r--r--absl/time/duration_test.cc11
-rw-r--r--absl/time/internal/cctz/src/time_zone_libc.cc200
-rw-r--r--absl/time/internal/cctz/src/time_zone_lookup_test.cc100
-rw-r--r--absl/time/time.h18
-rw-r--r--absl/types/BUILD.bazel1
-rw-r--r--absl/types/variant_exception_safety_test.cc6
-rw-r--r--absl/types/variant_test.cc9
22 files changed, 564 insertions, 111 deletions
diff --git a/CMake/AbseilHelpers.cmake b/CMake/AbseilHelpers.cmake
index d4870365..3b9148ea 100644
--- a/CMake/AbseilHelpers.cmake
+++ b/CMake/AbseilHelpers.cmake
@@ -62,7 +62,6 @@ function(absl_library)
endif()
endfunction()
-#
# CMake function to imitate Bazel's cc_library rule.
#
# Parameters:
@@ -77,14 +76,13 @@ endfunction()
# TESTONLY: When added, this target will only be built if user passes -DABSL_RUN_TESTS=ON to CMake.
#
# Note:
-#
# By default, absl_cc_library will always create a library named absl_internal_${NAME},
# which means other targets can only depend this library as absl_internal_${NAME}, not ${NAME}.
# This is to reduce namespace pollution.
#
# absl_cc_library(
# NAME
-# awesome_lib
+# awesome
# HDRS
# "a.h"
# SRCS
@@ -96,7 +94,7 @@ endfunction()
# SRCS
# "b.cc"
# DEPS
-# absl_internal_awesome_lib # not "awesome_lib"!
+# absl_internal_awesome # not "awesome"!
# )
#
# If PUBLIC is set, absl_cc_library will instead create a target named
@@ -112,7 +110,6 @@ endfunction()
# User can then use the library as absl::main_lib (although absl_main_lib is defined too).
#
# TODO: Implement "ALWAYSLINK"
-
function(absl_cc_library)
cmake_parse_arguments(ABSL_CC_LIB
"DISABLE_INSTALL;PUBLIC;TESTONLY"
@@ -153,7 +150,8 @@ function(absl_cc_library)
else()
# Generating header-only library
add_library(${_NAME} INTERFACE)
- target_include_directories(${_NAME} INTERFACE ${ABSL_COMMON_INCLUDE_DIRS})
+ target_include_directories(${_NAME}
+ INTERFACE ${ABSL_COMMON_INCLUDE_DIRS})
target_link_libraries(${_NAME}
INTERFACE ${ABSL_CC_LIB_DEPS} ${ABSL_CC_LIB_LINKOPTS}
)
@@ -166,6 +164,78 @@ function(absl_cc_library)
endif()
endfunction()
+# absl_cc_test()
+#
+# CMake function to imitate Bazel's cc_test rule.
+#
+# Parameters:
+# NAME: name of target (see Usage below)
+# SRCS: List of source files for the binary
+# DEPS: List of other libraries to be linked in to the binary targets
+# COPTS: List of private compile options
+# DEFINES: List of public defines
+# LINKOPTS: List of link options
+#
+# Note:
+# By default, absl_cc_test will always create a binary named absl_${NAME}.
+# This will also add it to ctest list as absl_${NAME}.
+#
+# Usage:
+# absl_cc_library(
+# NAME
+# awesome
+# HDRS
+# "a.h"
+# SRCS
+# "a.cc"
+# PUBLIC
+# )
+#
+# absl_cc_test(
+# NAME
+# awesome_test
+# SRCS
+# "awesome_test.cc"
+# DEPS
+# absl::awesome
+# gmock
+# gtest_main
+# )
+function(absl_cc_test)
+ if(NOT ABSL_RUN_TESTS)
+ return()
+ endif()
+
+ cmake_parse_arguments(ABSL_CC_TEST
+ ""
+ "NAME"
+ "SRCS;COPTS;DEFINES;LINKOPTS;DEPS"
+ ${ARGN}
+ )
+
+ set(_NAME "absl_${ABSL_CC_TEST_NAME}")
+ add_executable(${_NAME} "")
+ target_sources(${_NAME} PRIVATE ${ABSL_CC_TEST_SRCS})
+ target_include_directories(${_NAME}
+ PUBLIC ${ABSL_COMMON_INCLUDE_DIRS}
+ PRIVATE ${GMOCK_INCLUDE_DIRS} ${GTEST_INCLUDE_DIRS}
+ )
+ target_compile_definitions(${_NAME}
+ PUBLIC ${ABSL_CC_TEST_DEFINES}
+ )
+ target_compile_options(${_NAME}
+ PRIVATE ${ABSL_CC_TEST_COPTS}
+ )
+ target_link_libraries(${_NAME}
+ PUBLIC ${ABSL_CC_TEST_DEPS}
+ PRIVATE ${ABSL_CC_TEST_LINKOPTS}
+ )
+ # Add all Abseil targets to a a folder in the IDE for organization.
+ set_property(TARGET ${_NAME} PROPERTY FOLDER ${ABSL_IDE_FOLDER})
+
+ add_test(NAME ${_NAME} COMMAND ${_NAME})
+endfunction()
+
#
# header only virtual target creation
#
@@ -211,7 +281,6 @@ function(absl_header_library)
endfunction()
-
#
# create an abseil unit_test and add it to the executed test list
#
diff --git a/absl/algorithm/container.h b/absl/algorithm/container.h
index 53ab1568..6d5f6630 100644
--- a/absl/algorithm/container.h
+++ b/absl/algorithm/container.h
@@ -46,6 +46,8 @@
#include <iterator>
#include <numeric>
#include <type_traits>
+#include <unordered_map>
+#include <unordered_set>
#include <utility>
#include <vector>
@@ -54,7 +56,6 @@
#include "absl/meta/type_traits.h"
namespace absl {
-
namespace container_algorithm_internal {
// NOTE: it is important to defer to ADL lookup for building with C++ modules,
@@ -101,6 +102,17 @@ ContainerIter<C> c_begin(C& c) { return begin(c); }
template <typename C>
ContainerIter<C> c_end(C& c) { return end(c); }
+template <typename T>
+struct IsUnorderedContainer : std::false_type {};
+
+template <class Key, class T, class Hash, class KeyEqual, class Allocator>
+struct IsUnorderedContainer<
+ std::unordered_map<Key, T, Hash, KeyEqual, Allocator>> : std::true_type {};
+
+template <class Key, class Hash, class KeyEqual, class Allocator>
+struct IsUnorderedContainer<std::unordered_set<Key, Hash, KeyEqual, Allocator>>
+ : std::true_type {};
+
} // namespace container_algorithm_internal
// PUBLIC API
@@ -1154,7 +1166,13 @@ bool c_includes(const C1& c1, const C2& c2, Compare&& comp) {
// Container-based version of the <algorithm> `std::set_union()` function
// to return an iterator containing the union of two containers; duplicate
// values are not copied into the output.
-template <typename C1, typename C2, typename OutputIterator>
+template <typename C1, typename C2, typename OutputIterator,
+ typename = typename std::enable_if<
+ !container_algorithm_internal::IsUnorderedContainer<C1>::value,
+ void>::type,
+ typename = typename std::enable_if<
+ !container_algorithm_internal::IsUnorderedContainer<C2>::value,
+ void>::type>
OutputIterator c_set_union(const C1& c1, const C2& c2, OutputIterator output) {
return std::set_union(container_algorithm_internal::c_begin(c1),
container_algorithm_internal::c_end(c1),
@@ -1164,7 +1182,13 @@ OutputIterator c_set_union(const C1& c1, const C2& c2, OutputIterator output) {
// Overload of c_set_union() for performing a merge using a `comp` other than
// `operator<`.
-template <typename C1, typename C2, typename OutputIterator, typename Compare>
+template <typename C1, typename C2, typename OutputIterator, typename Compare,
+ typename = typename std::enable_if<
+ !container_algorithm_internal::IsUnorderedContainer<C1>::value,
+ void>::type,
+ typename = typename std::enable_if<
+ !container_algorithm_internal::IsUnorderedContainer<C2>::value,
+ void>::type>
OutputIterator c_set_union(const C1& c1, const C2& c2, OutputIterator output,
Compare&& comp) {
return std::set_union(container_algorithm_internal::c_begin(c1),
@@ -1178,7 +1202,13 @@ OutputIterator c_set_union(const C1& c1, const C2& c2, OutputIterator output,
//
// Container-based version of the <algorithm> `std::set_intersection()` function
// to return an iterator containing the intersection of two containers.
-template <typename C1, typename C2, typename OutputIterator>
+template <typename C1, typename C2, typename OutputIterator,
+ typename = typename std::enable_if<
+ !container_algorithm_internal::IsUnorderedContainer<C1>::value,
+ void>::type,
+ typename = typename std::enable_if<
+ !container_algorithm_internal::IsUnorderedContainer<C2>::value,
+ void>::type>
OutputIterator c_set_intersection(const C1& c1, const C2& c2,
OutputIterator output) {
return std::set_intersection(container_algorithm_internal::c_begin(c1),
@@ -1189,7 +1219,13 @@ OutputIterator c_set_intersection(const C1& c1, const C2& c2,
// Overload of c_set_intersection() for performing a merge using a `comp` other
// than `operator<`.
-template <typename C1, typename C2, typename OutputIterator, typename Compare>
+template <typename C1, typename C2, typename OutputIterator, typename Compare,
+ typename = typename std::enable_if<
+ !container_algorithm_internal::IsUnorderedContainer<C1>::value,
+ void>::type,
+ typename = typename std::enable_if<
+ !container_algorithm_internal::IsUnorderedContainer<C2>::value,
+ void>::type>
OutputIterator c_set_intersection(const C1& c1, const C2& c2,
OutputIterator output, Compare&& comp) {
return std::set_intersection(container_algorithm_internal::c_begin(c1),
@@ -1204,7 +1240,13 @@ OutputIterator c_set_intersection(const C1& c1, const C2& c2,
// Container-based version of the <algorithm> `std::set_difference()` function
// to return an iterator containing elements present in the first container but
// not in the second.
-template <typename C1, typename C2, typename OutputIterator>
+template <typename C1, typename C2, typename OutputIterator,
+ typename = typename std::enable_if<
+ !container_algorithm_internal::IsUnorderedContainer<C1>::value,
+ void>::type,
+ typename = typename std::enable_if<
+ !container_algorithm_internal::IsUnorderedContainer<C2>::value,
+ void>::type>
OutputIterator c_set_difference(const C1& c1, const C2& c2,
OutputIterator output) {
return std::set_difference(container_algorithm_internal::c_begin(c1),
@@ -1215,7 +1257,13 @@ OutputIterator c_set_difference(const C1& c1, const C2& c2,
// Overload of c_set_difference() for performing a merge using a `comp` other
// than `operator<`.
-template <typename C1, typename C2, typename OutputIterator, typename Compare>
+template <typename C1, typename C2, typename OutputIterator, typename Compare,
+ typename = typename std::enable_if<
+ !container_algorithm_internal::IsUnorderedContainer<C1>::value,
+ void>::type,
+ typename = typename std::enable_if<
+ !container_algorithm_internal::IsUnorderedContainer<C2>::value,
+ void>::type>
OutputIterator c_set_difference(const C1& c1, const C2& c2,
OutputIterator output, Compare&& comp) {
return std::set_difference(container_algorithm_internal::c_begin(c1),
@@ -1230,7 +1278,13 @@ OutputIterator c_set_difference(const C1& c1, const C2& c2,
// Container-based version of the <algorithm> `std::set_symmetric_difference()`
// function to return an iterator containing elements present in either one
// container or the other, but not both.
-template <typename C1, typename C2, typename OutputIterator>
+template <typename C1, typename C2, typename OutputIterator,
+ typename = typename std::enable_if<
+ !container_algorithm_internal::IsUnorderedContainer<C1>::value,
+ void>::type,
+ typename = typename std::enable_if<
+ !container_algorithm_internal::IsUnorderedContainer<C2>::value,
+ void>::type>
OutputIterator c_set_symmetric_difference(const C1& c1, const C2& c2,
OutputIterator output) {
return std::set_symmetric_difference(
@@ -1242,7 +1296,13 @@ OutputIterator c_set_symmetric_difference(const C1& c1, const C2& c2,
// Overload of c_set_symmetric_difference() for performing a merge using a
// `comp` other than `operator<`.
-template <typename C1, typename C2, typename OutputIterator, typename Compare>
+template <typename C1, typename C2, typename OutputIterator, typename Compare,
+ typename = typename std::enable_if<
+ !container_algorithm_internal::IsUnorderedContainer<C1>::value,
+ void>::type,
+ typename = typename std::enable_if<
+ !container_algorithm_internal::IsUnorderedContainer<C2>::value,
+ void>::type>
OutputIterator c_set_symmetric_difference(const C1& c1, const C2& c2,
OutputIterator output,
Compare&& comp) {
diff --git a/absl/base/call_once.h b/absl/base/call_once.h
index 532ee2e3..f6c8ebb2 100644
--- a/absl/base/call_once.h
+++ b/absl/base/call_once.h
@@ -150,12 +150,8 @@ void CallOnceImpl(std::atomic<uint32_t>* control,
old_control != kOnceRunning &&
old_control != kOnceWaiter &&
old_control != kOnceDone) {
- ABSL_RAW_LOG(
- FATAL,
- "Unexpected value for control word: %lx. Either the control word "
- "has non-static storage duration (where GoogleOnceDynamic might "
- "be appropriate), or there's been a memory corruption.",
- static_cast<unsigned long>(old_control)); // NOLINT
+ ABSL_RAW_LOG(FATAL, "Unexpected value for control word: 0x%lx",
+ static_cast<unsigned long>(old_control)); // NOLINT
}
}
#endif // NDEBUG
diff --git a/absl/base/config.h b/absl/base/config.h
index d4eb7d0c..e3edb2bf 100644
--- a/absl/base/config.h
+++ b/absl/base/config.h
@@ -139,12 +139,18 @@
#ifdef ABSL_HAVE_THREAD_LOCAL
#error ABSL_HAVE_THREAD_LOCAL cannot be directly set
#elif defined(__APPLE__)
-// Notes: Xcode's clang did not support `thread_local` until version
-// 8, and even then not for all iOS < 9.0. Also, Xcode 9.3 started disallowing
-// `thread_local` for 32-bit iOS simulator targeting iOS 9.x.
-// `__has_feature` is only supported by Clang so it has be inside
+// Notes:
+// * Xcode's clang did not support `thread_local` until version 8, and
+// even then not for all iOS < 9.0.
+// * Xcode 9.3 started disallowing `thread_local` for 32-bit iOS simulator
+// targeting iOS 9.x.
+// * Xcode 10 moves the deployment target check for iOS < 9.0 to link time
+// making __has_feature unreliable there.
+//
+// Otherwise, `__has_feature` is only supported by Clang so it has be inside
// `defined(__APPLE__)` check.
-#if __has_feature(cxx_thread_local)
+#if __has_feature(cxx_thread_local) && \
+ !(TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_9_0)
#define ABSL_HAVE_THREAD_LOCAL 1
#endif
#else // !defined(__APPLE__)
@@ -423,4 +429,12 @@
#define ABSL_HAVE_STD_STRING_VIEW 1
#endif
+// In debug mode, MSVC 2017's std::variant throws a EXCEPTION_ACCESS_VIOLATION
+// SEH exception from emplace for variant<SomeStruct> when constructing the
+// struct can throw. This defeats some of variant_test and
+// variant_exception_safety_test.
+#if defined(_MSC_VER) && _MSC_VER >= 1700 && defined(_DEBUG)
+#define ABSL_INTERNAL_MSVC_2017_DBG_MODE
+#endif
+
#endif // ABSL_BASE_CONFIG_H_
diff --git a/absl/container/BUILD.bazel b/absl/container/BUILD.bazel
index d75f8916..afc869f4 100644
--- a/absl/container/BUILD.bazel
+++ b/absl/container/BUILD.bazel
@@ -212,6 +212,7 @@ cc_library(
":container_memory",
":hash_function_defaults",
":raw_hash_map",
+ "//absl/algorithm:container",
"//absl/memory",
],
)
@@ -240,6 +241,7 @@ cc_library(
":container_memory",
":hash_function_defaults",
":raw_hash_set",
+ "//absl/algorithm:container",
"//absl/base:core_headers",
"//absl/memory",
],
@@ -271,6 +273,7 @@ cc_library(
":hash_function_defaults",
":node_hash_policy",
":raw_hash_map",
+ "//absl/algorithm:container",
"//absl/memory",
],
)
@@ -299,6 +302,7 @@ cc_library(
":hash_function_defaults",
":node_hash_policy",
":raw_hash_set",
+ "//absl/algorithm:container",
"//absl/memory",
],
)
diff --git a/absl/container/flat_hash_map.h b/absl/container/flat_hash_map.h
index de632be2..7bc21380 100644
--- a/absl/container/flat_hash_map.h
+++ b/absl/container/flat_hash_map.h
@@ -35,6 +35,7 @@
#include <type_traits>
#include <utility>
+#include "absl/algorithm/container.h"
#include "absl/container/internal/container_memory.h"
#include "absl/container/internal/hash_function_defaults.h" // IWYU pragma: export
#include "absl/container/internal/raw_hash_map.h" // IWYU pragma: export
@@ -564,5 +565,16 @@ struct FlatHashMapPolicy {
};
} // namespace container_internal
+
+namespace container_algorithm_internal {
+
+// Specialization of trait in absl/algorithm/container.h
+template <class Key, class T, class Hash, class KeyEqual, class Allocator>
+struct IsUnorderedContainer<
+ absl::flat_hash_map<Key, T, Hash, KeyEqual, Allocator>> : std::true_type {};
+
+} // namespace container_algorithm_internal
+
} // namespace absl
+
#endif // ABSL_CONTAINER_FLAT_HASH_MAP_H_
diff --git a/absl/container/flat_hash_set.h b/absl/container/flat_hash_set.h
index a2584d66..f7c1acaa 100644
--- a/absl/container/flat_hash_set.h
+++ b/absl/container/flat_hash_set.h
@@ -32,6 +32,7 @@
#include <type_traits>
#include <utility>
+#include "absl/algorithm/container.h"
#include "absl/base/macros.h"
#include "absl/container/internal/container_memory.h"
#include "absl/container/internal/hash_function_defaults.h" // IWYU pragma: export
@@ -475,5 +476,16 @@ struct FlatHashSetPolicy {
static size_t space_used(const T*) { return 0; }
};
} // namespace container_internal
+
+namespace container_algorithm_internal {
+
+// Specialization of trait in absl/algorithm/container.h
+template <class Key, class Hash, class KeyEqual, class Allocator>
+struct IsUnorderedContainer<absl::flat_hash_set<Key, Hash, KeyEqual, Allocator>>
+ : std::true_type {};
+
+} // namespace container_algorithm_internal
+
} // namespace absl
+
#endif // ABSL_CONTAINER_FLAT_HASH_SET_H_
diff --git a/absl/container/inlined_vector.h b/absl/container/inlined_vector.h
index ea8cb02b..642dae6c 100644
--- a/absl/container/inlined_vector.h
+++ b/absl/container/inlined_vector.h
@@ -20,17 +20,17 @@
// vector" which behaves in an equivalent fashion to a `std::vector`, except
// that storage for small sequences of the vector are provided inline without
// requiring any heap allocation.
-
-// An `absl::InlinedVector<T,N>` specifies the size N at which to inline as one
-// of its template parameters. Vectors of length <= N are provided inline.
-// Typically N is very small (e.g., 4) so that sequences that are expected to be
-// short do not require allocations.
-
-// An `absl::InlinedVector` does not usually require a specific allocator; if
+//
+// An `absl::InlinedVector<T, N>` specifies the default capacity `N` as one of
+// its template parameters. Instances where `size() <= N` hold contained
+// elements in inline space. Typically `N` is very small so that sequences that
+// are expected to be short do not require allocations.
+//
+// An `absl::InlinedVector` does not usually require a specific allocator. If
// the inlined vector grows beyond its initial constraints, it will need to
-// allocate (as any normal `std::vector` would) and it will generally use the
-// default allocator in that case; optionally, a custom allocator may be
-// specified using an `absl::InlinedVector<T,N,A>` construction.
+// allocate (as any normal `std::vector` would). This is usually performed with
+// the default allocator (defined as `std::allocator<T>`). Optionally, a custom
+// allocator type may be specified as `A` in `absl::InlinedVector<T, N, A>`.
#ifndef ABSL_CONTAINER_INLINED_VECTOR_H_
#define ABSL_CONTAINER_INLINED_VECTOR_H_
@@ -61,8 +61,8 @@ namespace absl {
// An `absl::InlinedVector` is designed to be a drop-in replacement for
// `std::vector` for use cases where the vector's size is sufficiently small
// that it can be inlined. If the inlined vector does grow beyond its estimated
-// size, it will trigger an initial allocation on the heap, and will behave as a
-// `std:vector`. The API of the `absl::InlinedVector` within this file is
+// capacity, it will trigger an initial allocation on the heap, and will behave
+// as a `std:vector`. The API of the `absl::InlinedVector` within this file is
// designed to cover the same API footprint as covered by `std::vector`.
template <typename T, size_t N, typename A = std::allocator<T>>
class InlinedVector {
@@ -101,7 +101,6 @@ class InlinedVector {
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
-
// ---------------------------------------------------------------------------
// InlinedVector Constructors and Destructor
// ---------------------------------------------------------------------------
@@ -135,11 +134,12 @@ class InlinedVector {
AppendRange(init_list.begin(), init_list.end());
}
- // Creates and initialize with the elements [`first`, `last`).
+ // Creates an inlined vector with elements constructed from the provided
+ // Iterator range [`first`, `last`).
//
// NOTE: The `enable_if` prevents ambiguous interpretation between a call to
- // this constructor with two integral arguments and a call to the preceding
- // `InlinedVector(n, v)` constructor.
+ // this constructor with two integral arguments and a call to the above
+ // `InlinedVector(size_type, const_reference)` constructor.
template <typename InputIterator, DisableIfIntegral<InputIterator>* = nullptr>
InlinedVector(InputIterator first, InputIterator last,
const allocator_type& alloc = allocator_type())
@@ -153,11 +153,11 @@ class InlinedVector {
// Creates a copy of `other` but with a specified allocator.
InlinedVector(const InlinedVector& other, const allocator_type& alloc);
- // Creates an inlined vector with the contents of `other`.
+ // Creates an inlined vector by moving in the contents of `other`.
//
// NOTE: This move constructor does not allocate and only moves the underlying
// objects, so its `noexcept` specification depends on whether moving the
- // underlying objects can throw or not. We assume
+ // underlying objects can throw or not. We assume:
// a) move constructors should only throw due to allocation failure and
// b) if `value_type`'s move constructor allocates, it uses the same
// allocation function as the `InlinedVector`'s allocator, so the move
@@ -167,9 +167,9 @@ class InlinedVector {
absl::allocator_is_nothrow<allocator_type>::value ||
std::is_nothrow_move_constructible<value_type>::value);
- // Creates an inlined vector with the contents of `other`.
+ // Creates an inlined vector by moving in the contents of `other`.
//
- // NOTE: This move constructor allocates and also moves the underlying
+ // NOTE: This move constructor allocates and subsequently moves the underlying
// objects, so its `noexcept` specification depends on whether the allocation
// can throw and whether moving the underlying objects can throw. Based on the
// same assumptions as above, the `noexcept` specification is dominated by
@@ -180,7 +180,6 @@ class InlinedVector {
~InlinedVector() { clear(); }
-
// ---------------------------------------------------------------------------
// InlinedVector Member Accessors
// ---------------------------------------------------------------------------
diff --git a/absl/container/internal/hash_generator_testing.cc b/absl/container/internal/hash_generator_testing.cc
index 0d6a9df1..e0fefbff 100644
--- a/absl/container/internal/hash_generator_testing.cc
+++ b/absl/container/internal/hash_generator_testing.cc
@@ -39,9 +39,9 @@ class RandomDeviceSeedSeq {
} // namespace
-std::mt19937_64* GetThreadLocalRng() {
+std::mt19937_64* GetSharedRng() {
RandomDeviceSeedSeq seed_seq;
- thread_local auto* rng = new std::mt19937_64(seed_seq);
+ static auto* rng = new std::mt19937_64(seed_seq);
return rng;
}
@@ -51,7 +51,7 @@ std::string Generator<std::string>::operator()() const {
std::string res;
res.resize(32);
std::generate(res.begin(), res.end(),
- [&]() { return chars(*GetThreadLocalRng()); });
+ [&]() { return chars(*GetSharedRng()); });
return res;
}
@@ -63,7 +63,7 @@ absl::string_view Generator<absl::string_view>::operator()() const {
auto& res = arena->back();
res.resize(32);
std::generate(res.begin(), res.end(),
- [&]() { return chars(*GetThreadLocalRng()); });
+ [&]() { return chars(*GetSharedRng()); });
return res;
}
diff --git a/absl/container/internal/hash_generator_testing.h b/absl/container/internal/hash_generator_testing.h
index 50d77102..6521efe8 100644
--- a/absl/container/internal/hash_generator_testing.h
+++ b/absl/container/internal/hash_generator_testing.h
@@ -43,7 +43,7 @@ struct IsMap<Map, absl::void_t<typename Map::mapped_type>> : std::true_type {};
} // namespace generator_internal
-std::mt19937_64* GetThreadLocalRng();
+std::mt19937_64* GetSharedRng();
enum Enum {
kEnumEmpty,
@@ -66,7 +66,7 @@ template <class T>
struct Generator<T, typename std::enable_if<std::is_integral<T>::value>::type> {
T operator()() const {
std::uniform_int_distribution<T> dist;
- return dist(*GetThreadLocalRng());
+ return dist(*GetSharedRng());
}
};
@@ -76,7 +76,7 @@ struct Generator<Enum> {
std::uniform_int_distribution<typename std::underlying_type<Enum>::type>
dist;
while (true) {
- auto variate = dist(*GetThreadLocalRng());
+ auto variate = dist(*GetSharedRng());
if (variate != kEnumEmpty && variate != kEnumDeleted)
return static_cast<Enum>(variate);
}
@@ -90,7 +90,7 @@ struct Generator<EnumClass> {
typename std::underlying_type<EnumClass>::type>
dist;
while (true) {
- EnumClass variate = static_cast<EnumClass>(dist(*GetThreadLocalRng()));
+ EnumClass variate = static_cast<EnumClass>(dist(*GetSharedRng()));
if (variate != EnumClass::kEmpty && variate != EnumClass::kDeleted)
return static_cast<EnumClass>(variate);
}
diff --git a/absl/container/node_hash_map.h b/absl/container/node_hash_map.h
index 00710e52..18d3f28f 100644
--- a/absl/container/node_hash_map.h
+++ b/absl/container/node_hash_map.h
@@ -40,6 +40,7 @@
#include <type_traits>
#include <utility>
+#include "absl/algorithm/container.h"
#include "absl/container/internal/container_memory.h"
#include "absl/container/internal/hash_function_defaults.h" // IWYU pragma: export
#include "absl/container/internal/node_hash_policy.h"
@@ -91,7 +92,7 @@ class NodeHashMapPolicy;
// std::string search_key = "b";
// auto result = ducks.find(search_key);
// if (result != ducks.end()) {
-// std::cout << "Result: " << search_key->second << std::endl;
+// std::cout << "Result: " << result->second << std::endl;
// }
template <class Key, class Value,
class Hash = absl::container_internal::hash_default_hash<Key>,
@@ -566,5 +567,16 @@ class NodeHashMapPolicy
static const Value& value(const value_type* elem) { return elem->second; }
};
} // namespace container_internal
+
+namespace container_algorithm_internal {
+
+// Specialization of trait in absl/algorithm/container.h
+template <class Key, class T, class Hash, class KeyEqual, class Allocator>
+struct IsUnorderedContainer<
+ absl::node_hash_map<Key, T, Hash, KeyEqual, Allocator>> : std::true_type {};
+
+} // namespace container_algorithm_internal
+
} // namespace absl
+
#endif // ABSL_CONTAINER_NODE_HASH_MAP_H_
diff --git a/absl/container/node_hash_set.h b/absl/container/node_hash_set.h
index 813fdeff..e0897c99 100644
--- a/absl/container/node_hash_set.h
+++ b/absl/container/node_hash_set.h
@@ -37,6 +37,7 @@
#include <type_traits>
+#include "absl/algorithm/container.h"
#include "absl/container/internal/hash_function_defaults.h" // IWYU pragma: export
#include "absl/container/internal/node_hash_policy.h"
#include "absl/container/internal/raw_hash_set.h" // IWYU pragma: export
@@ -475,5 +476,15 @@ struct NodeHashSetPolicy
static size_t element_space_used(const T*) { return sizeof(T); }
};
} // namespace container_internal
+
+namespace container_algorithm_internal {
+
+// Specialization of trait in absl/algorithm/container.h
+template <class Key, class Hash, class KeyEqual, class Allocator>
+struct IsUnorderedContainer<absl::node_hash_set<Key, Hash, KeyEqual, Allocator>>
+ : std::true_type {};
+
+} // namespace container_algorithm_internal
} // namespace absl
+
#endif // ABSL_CONTAINER_NODE_HASH_SET_H_
diff --git a/absl/numeric/int128.h b/absl/numeric/int128.h
index 5d14a4a8..9c2e00f6 100644
--- a/absl/numeric/int128.h
+++ b/absl/numeric/int128.h
@@ -37,6 +37,11 @@
#include "absl/base/macros.h"
#include "absl/base/port.h"
+#if defined(_MSC_VER) && defined(_WIN64)
+#include <intrin.h>
+#pragma intrinsic(_umul128)
+#endif // defined(_MSC_VER) && defined(_WIN64)
+
namespace absl {
@@ -661,6 +666,12 @@ inline uint128 operator*(uint128 lhs, uint128 rhs) {
// can be used for uint128 storage.
return static_cast<unsigned __int128>(lhs) *
static_cast<unsigned __int128>(rhs);
+#elif defined(_MSC_VER) && defined(_WIN64)
+ uint64_t carry;
+ uint64_t low = _umul128(Uint128Low64(lhs), Uint128Low64(rhs), &carry);
+ return MakeUint128(Uint128Low64(lhs) * Uint128High64(rhs) +
+ Uint128High64(lhs) * Uint128Low64(rhs) + carry,
+ low);
#else // ABSL_HAVE_INTRINSIC128
uint64_t a32 = Uint128Low64(lhs) >> 32;
uint64_t a00 = Uint128Low64(lhs) & 0xffffffff;
diff --git a/absl/synchronization/lifetime_test.cc b/absl/synchronization/lifetime_test.cc
index 90c9009b..b7360c29 100644
--- a/absl/synchronization/lifetime_test.cc
+++ b/absl/synchronization/lifetime_test.cc
@@ -72,23 +72,19 @@ void ThreadTwo(absl::Mutex* mutex, absl::CondVar* condvar,
// Launch thread 1 and thread 2, and block on their completion.
// If any of 'mutex', 'condvar', or 'notification' is nullptr, use a locally
// constructed instance instead.
-void RunTests(absl::Mutex* mutex, absl::CondVar* condvar,
- absl::Notification* notification) {
+void RunTests(absl::Mutex* mutex, absl::CondVar* condvar) {
absl::Mutex default_mutex;
absl::CondVar default_condvar;
- absl::Notification default_notification;
+ absl::Notification notification;
if (!mutex) {
mutex = &default_mutex;
}
if (!condvar) {
condvar = &default_condvar;
}
- if (!notification) {
- notification = &default_notification;
- }
bool state = false;
- std::thread thread_one(ThreadOne, mutex, condvar, notification, &state);
- std::thread thread_two(ThreadTwo, mutex, condvar, notification, &state);
+ std::thread thread_one(ThreadOne, mutex, condvar, &notification, &state);
+ std::thread thread_two(ThreadTwo, mutex, condvar, &notification, &state);
thread_one.join();
thread_two.join();
}
@@ -96,8 +92,7 @@ void RunTests(absl::Mutex* mutex, absl::CondVar* condvar,
void TestLocals() {
absl::Mutex mutex;
absl::CondVar condvar;
- absl::Notification notification;
- RunTests(&mutex, &condvar, &notification);
+ RunTests(&mutex, &condvar);
}
// Global variables during start and termination
diff --git a/absl/synchronization/notification.cc b/absl/synchronization/notification.cc
index ed8cc906..cdcbc134 100644
--- a/absl/synchronization/notification.cc
+++ b/absl/synchronization/notification.cc
@@ -22,6 +22,7 @@
#include "absl/time/time.h"
namespace absl {
+
void Notification::Notify() {
MutexLock l(&this->mutex_);
diff --git a/absl/time/duration_test.cc b/absl/time/duration_test.cc
index 7ae25dc6..775da91e 100644
--- a/absl/time/duration_test.cc
+++ b/absl/time/duration_test.cc
@@ -56,6 +56,17 @@ MATCHER_P(TimevalMatcher, tv, "") {
return false;
}
+TEST(Duration, ConstExpr) {
+ constexpr absl::Duration d0 = absl::ZeroDuration();
+ static_assert(d0 == absl::ZeroDuration(), "ZeroDuration()");
+ constexpr absl::Duration d1 = absl::Seconds(1);
+ static_assert(d1 == absl::Seconds(1), "Seconds(1)");
+ static_assert(d1 != absl::ZeroDuration(), "Seconds(1)");
+ constexpr absl::Duration d2 = absl::InfiniteDuration();
+ static_assert(d2 == absl::InfiniteDuration(), "InfiniteDuration()");
+ static_assert(d2 != absl::ZeroDuration(), "InfiniteDuration()");
+}
+
TEST(Duration, ValueSemantics) {
// If this compiles, the test passes.
constexpr absl::Duration a; // Default construction
diff --git a/absl/time/internal/cctz/src/time_zone_libc.cc b/absl/time/internal/cctz/src/time_zone_libc.cc
index e35fa18b..6db519e1 100644
--- a/absl/time/internal/cctz/src/time_zone_libc.cc
+++ b/absl/time/internal/cctz/src/time_zone_libc.cc
@@ -20,6 +20,7 @@
#include <chrono>
#include <ctime>
+#include <limits>
#include <tuple>
#include <utility>
@@ -85,6 +86,76 @@ OffsetAbbr get_offset_abbr(const T& tm, decltype(&T::__tm_gmtoff) = nullptr,
#endif // !defined(__tm_gmtoff) && !defined(__tm_zone)
#endif
+inline std::tm* gm_time(const std::time_t *timep, std::tm *result) {
+#if defined(_WIN32) || defined(_WIN64)
+ return gmtime_s(result, timep) ? nullptr : result;
+#else
+ return gmtime_r(timep, result);
+#endif
+}
+
+inline std::tm* local_time(const std::time_t *timep, std::tm *result) {
+#if defined(_WIN32) || defined(_WIN64)
+ return localtime_s(result, timep) ? nullptr : result;
+#else
+ return localtime_r(timep, result);
+#endif
+}
+
+// Converts a civil second and "dst" flag into a time_t and UTC offset.
+// Returns false if time_t cannot represent the requested civil second.
+// Caller must have already checked that cs.year() will fit into a tm_year.
+bool make_time(const civil_second& cs, int is_dst, std::time_t* t, int* off) {
+ std::tm tm;
+ tm.tm_year = static_cast<int>(cs.year() - year_t{1900});
+ tm.tm_mon = cs.month() - 1;
+ tm.tm_mday = cs.day();
+ tm.tm_hour = cs.hour();
+ tm.tm_min = cs.minute();
+ tm.tm_sec = cs.second();
+ tm.tm_isdst = is_dst;
+ *t = std::mktime(&tm);
+ if (*t == std::time_t{-1}) {
+ std::tm tm2;
+ const std::tm* tmp = local_time(t, &tm2);
+ if (tmp == nullptr || tmp->tm_year != tm.tm_year ||
+ tmp->tm_mon != tm.tm_mon || tmp->tm_mday != tm.tm_mday ||
+ tmp->tm_hour != tm.tm_hour || tmp->tm_min != tm.tm_min ||
+ tmp->tm_sec != tm.tm_sec) {
+ // A true error (not just one second before the epoch).
+ return false;
+ }
+ }
+ *off = get_offset_abbr(tm).first;
+ return true;
+}
+
+// Find the least time_t in [lo:hi] where local time matches offset, given:
+// (1) lo doesn't match, (2) hi does, and (3) there is only one transition.
+std::time_t find_trans(std::time_t lo, std::time_t hi, int offset) {
+ std::tm tm;
+ while (lo + 1 != hi) {
+ const std::time_t mid = lo + (hi - lo) / 2;
+ if (std::tm* tmp = local_time(&mid, &tm)) {
+ if (get_offset_abbr(*tmp).first == offset) {
+ hi = mid;
+ } else {
+ lo = mid;
+ }
+ } else {
+ // If std::tm cannot hold some result we resort to a linear search,
+ // ignoring all failed conversions. Slow, but never really happens.
+ while (++lo != hi) {
+ if (std::tm* tmp = local_time(&lo, &tm)) {
+ if (get_offset_abbr(*tmp).first == offset) break;
+ }
+ }
+ return lo;
+ }
+ }
+ return hi;
+}
+
} // namespace
TimeZoneLibC::TimeZoneLibC(const std::string& name)
@@ -93,50 +164,107 @@ TimeZoneLibC::TimeZoneLibC(const std::string& name)
time_zone::absolute_lookup TimeZoneLibC::BreakTime(
const time_point<seconds>& tp) const {
time_zone::absolute_lookup al;
- std::time_t t = ToUnixSeconds(tp);
+ al.offset = 0;
+ al.is_dst = false;
+ al.abbr = "-00";
+
+ const std::int_fast64_t s = ToUnixSeconds(tp);
+
+ // If std::time_t cannot hold the input we saturate the output.
+ if (s < std::numeric_limits<std::time_t>::min()) {
+ al.cs = civil_second::min();
+ return al;
+ }
+ if (s > std::numeric_limits<std::time_t>::max()) {
+ al.cs = civil_second::max();
+ return al;
+ }
+
+ const std::time_t t = static_cast<std::time_t>(s);
std::tm tm;
- if (local_) {
-#if defined(_WIN32) || defined(_WIN64)
- localtime_s(&tm, &t);
-#else
- localtime_r(&t, &tm);
-#endif
- std::tie(al.offset, al.abbr) = get_offset_abbr(tm);
- } else {
-#if defined(_WIN32) || defined(_WIN64)
- gmtime_s(&tm, &t);
-#else
- gmtime_r(&t, &tm);
-#endif
- al.offset = 0;
- al.abbr = "UTC";
+ std::tm* tmp = local_ ? local_time(&t, &tm) : gm_time(&t, &tm);
+
+ // If std::tm cannot hold the result we saturate the output.
+ if (tmp == nullptr) {
+ al.cs = (s < 0) ? civil_second::min() : civil_second::max();
+ return al;
}
- al.cs = civil_second(tm.tm_year + year_t{1900}, tm.tm_mon + 1, tm.tm_mday,
- tm.tm_hour, tm.tm_min, tm.tm_sec);
- al.is_dst = tm.tm_isdst > 0;
+
+ const year_t year = tmp->tm_year + year_t{1900};
+ al.cs = civil_second(year, tmp->tm_mon + 1, tmp->tm_mday,
+ tmp->tm_hour, tmp->tm_min, tmp->tm_sec);
+ std::tie(al.offset, al.abbr) = get_offset_abbr(*tmp);
+ if (!local_) al.abbr = "UTC"; // as expected by cctz
+ al.is_dst = tmp->tm_isdst > 0;
return al;
}
time_zone::civil_lookup TimeZoneLibC::MakeTime(const civil_second& cs) const {
- time_zone::civil_lookup cl;
- std::time_t t;
- if (local_) {
- // Does not handle SKIPPED/AMBIGUOUS or huge years.
- std::tm tm;
- tm.tm_year = static_cast<int>(cs.year() - 1900);
- tm.tm_mon = cs.month() - 1;
- tm.tm_mday = cs.day();
- tm.tm_hour = cs.hour();
- tm.tm_min = cs.minute();
- tm.tm_sec = cs.second();
- tm.tm_isdst = -1;
- t = std::mktime(&tm);
+ if (!local_) {
+ // If time_point<seconds> cannot hold the result we saturate.
+ static const civil_second min_tp_cs =
+ civil_second() + ToUnixSeconds(time_point<seconds>::min());
+ static const civil_second max_tp_cs =
+ civil_second() + ToUnixSeconds(time_point<seconds>::max());
+ const time_point<seconds> tp =
+ (cs < min_tp_cs)
+ ? time_point<seconds>::min()
+ : (cs > max_tp_cs) ? time_point<seconds>::max()
+ : FromUnixSeconds(cs - civil_second());
+ return {time_zone::civil_lookup::UNIQUE, tp, tp, tp};
+ }
+
+ // If tm_year cannot hold the requested year we saturate the result.
+ if (cs.year() < 0) {
+ if (cs.year() < std::numeric_limits<int>::min() + year_t{1900}) {
+ const time_point<seconds> tp = time_point<seconds>::min();
+ return {time_zone::civil_lookup::UNIQUE, tp, tp, tp};
+ }
} else {
- t = cs - civil_second();
+ if (cs.year() - year_t{1900} > std::numeric_limits<int>::max()) {
+ const time_point<seconds> tp = time_point<seconds>::max();
+ return {time_zone::civil_lookup::UNIQUE, tp, tp, tp};
+ }
+ }
+
+ // We probe with "is_dst" values of 0 and 1 to try to distinguish unique
+ // civil seconds from skipped or repeated ones. This is not always possible
+ // however, as the "dst" flag does not change over some offset transitions.
+ // We are also subject to the vagaries of mktime() implementations.
+ std::time_t t0, t1;
+ int offset0, offset1;
+ if (make_time(cs, 0, &t0, &offset0) && make_time(cs, 1, &t1, &offset1)) {
+ if (t0 == t1) {
+ // The civil time was singular (pre == trans == post).
+ const time_point<seconds> tp = FromUnixSeconds(t0);
+ return {time_zone::civil_lookup::UNIQUE, tp, tp, tp};
+ }
+
+ if (t0 > t1) {
+ std::swap(t0, t1);
+ std::swap(offset0, offset1);
+ }
+ const std::time_t tt = find_trans(t0, t1, offset1);
+ const time_point<seconds> trans = FromUnixSeconds(tt);
+
+ if (offset0 < offset1) {
+ // The civil time did not exist (pre >= trans > post).
+ const time_point<seconds> pre = FromUnixSeconds(t1);
+ const time_point<seconds> post = FromUnixSeconds(t0);
+ return {time_zone::civil_lookup::SKIPPED, pre, trans, post};
+ }
+
+ // The civil time was ambiguous (pre < trans <= post).
+ const time_point<seconds> pre = FromUnixSeconds(t0);
+ const time_point<seconds> post = FromUnixSeconds(t1);
+ return {time_zone::civil_lookup::REPEATED, pre, trans, post};
}
- cl.kind = time_zone::civil_lookup::UNIQUE;
- cl.pre = cl.trans = cl.post = FromUnixSeconds(t);
- return cl;
+
+ // make_time() failed somehow so we saturate the result.
+ const time_point<seconds> tp = (cs < civil_second())
+ ? time_point<seconds>::min()
+ : time_point<seconds>::max();
+ return {time_zone::civil_lookup::UNIQUE, tp, tp, tp};
}
bool TimeZoneLibC::NextTransition(const time_point<seconds>& tp,
diff --git a/absl/time/internal/cctz/src/time_zone_lookup_test.cc b/absl/time/internal/cctz/src/time_zone_lookup_test.cc
index f28e7f85..e84b9469 100644
--- a/absl/time/internal/cctz/src/time_zone_lookup_test.cc
+++ b/absl/time/internal/cctz/src/time_zone_lookup_test.cc
@@ -16,7 +16,9 @@
#include <chrono>
#include <cstddef>
+#include <cstdlib>
#include <future>
+#include <limits>
#include <string>
#include <thread>
#include <vector>
@@ -925,7 +927,7 @@ TEST(MakeTime, Normalization) {
EXPECT_EQ(tp, convert(civil_second(2009, 2, 13, 18, 30, 90), tz)); // second
}
-// NOTE: Run this with --copt=-ftrapv to detect overflow problems.
+// NOTE: Run this with -ftrapv to detect overflow problems.
TEST(MakeTime, SysSecondsLimits) {
const char RFC3339[] = "%Y-%m-%dT%H:%M:%S%Ez";
const time_zone utc = utc_time_zone();
@@ -991,19 +993,107 @@ TEST(MakeTime, SysSecondsLimits) {
tp = convert(civil_second::min(), west);
EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::min(), tp);
+ // Some similar checks for the "libc" time-zone implementation.
if (sizeof(std::time_t) >= 8) {
// Checks that "tm_year + 1900", as used by the "libc" implementation,
// can produce year values beyond the range on an int without overflow.
#if defined(_WIN32) || defined(_WIN64)
- // localtime_s() and gmtime_s() don't believe in years past 3000.
+ // localtime_s() and gmtime_s() don't believe in years outside [1970:3000].
#else
- const time_zone libc_utc = LoadZone("libc:UTC");
- tp = convert(civil_year(year_t{2147483648}), libc_utc);
- EXPECT_EQ("2147483648-01-01T00:00:00+00:00", format(RFC3339, tp, libc_utc));
+ const time_zone utc = LoadZone("libc:UTC");
+ const year_t max_tm_year = year_t{std::numeric_limits<int>::max()} + 1900;
+ tp = convert(civil_second(max_tm_year, 12, 31, 23, 59, 59), utc);
+ EXPECT_EQ("2147485547-12-31T23:59:59+00:00", format(RFC3339, tp, utc));
+ const year_t min_tm_year = year_t{std::numeric_limits<int>::min()} + 1900;
+ tp = convert(civil_second(min_tm_year, 1, 1, 0, 0, 0), utc);
+ EXPECT_EQ("-2147481748-01-01T00:00:00+00:00", format(RFC3339, tp, utc));
#endif
}
}
+TEST(MakeTime, LocalTimeLibC) {
+ // Checks that cctz and libc agree on transition points in [1970:2037].
+ //
+ // We limit this test case to environments where:
+ // 1) we know how to change the time zone used by localtime()/mktime(),
+ // 2) cctz and localtime()/mktime() will use similar-enough tzdata, and
+ // 3) we have some idea about how mktime() behaves during transitions.
+#if defined(__linux__)
+ const char* const ep = getenv("TZ");
+ std::string tz_name = (ep != nullptr) ? ep : "";
+ for (const char* const* np = kTimeZoneNames; *np != nullptr; ++np) {
+ ASSERT_EQ(0, setenv("TZ", *np, 1)); // change what "localtime" means
+ const auto zi = local_time_zone();
+ const auto lc = LoadZone("libc:localtime");
+ time_zone::civil_transition trans;
+ for (auto tp = zi.lookup(civil_second()).trans;
+ zi.next_transition(tp, &trans);
+ tp = zi.lookup(trans.to).trans) {
+ const auto fcl = zi.lookup(trans.from);
+ const auto tcl = zi.lookup(trans.to);
+ civil_second cs; // compare cs in zi and lc
+ if (fcl.kind == time_zone::civil_lookup::UNIQUE) {
+ if (tcl.kind == time_zone::civil_lookup::UNIQUE) {
+ // Both unique; must be an is_dst or abbr change.
+ ASSERT_EQ(trans.from, trans.to);
+ const auto trans = fcl.trans;
+ const auto tal = zi.lookup(trans);
+ const auto tprev = trans - absl::time_internal::cctz::seconds(1);
+ const auto pal = zi.lookup(tprev);
+ if (pal.is_dst == tal.is_dst) {
+ ASSERT_STRNE(pal.abbr, tal.abbr);
+ }
+ continue;
+ }
+ ASSERT_EQ(time_zone::civil_lookup::REPEATED, tcl.kind);
+ cs = trans.to;
+ } else {
+ ASSERT_EQ(time_zone::civil_lookup::UNIQUE, tcl.kind);
+ ASSERT_EQ(time_zone::civil_lookup::SKIPPED, fcl.kind);
+ cs = trans.from;
+ }
+ if (cs.year() > 2037) break; // limit test time (and to 32-bit time_t)
+ const auto cl_zi = zi.lookup(cs);
+ if (zi.lookup(cl_zi.pre).is_dst == zi.lookup(cl_zi.post).is_dst) {
+ // The "libc" implementation cannot correctly classify transitions
+ // that don't change the "tm_isdst" flag. In Europe/Volgograd, for
+ // example, there is a SKIPPED transition from +03 to +04 with dst=F
+ // on both sides ...
+ // 1540681199 = 2018-10-28 01:59:59 +03:00:00 [dst=F off=10800]
+ // 1540681200 = 2018-10-28 03:00:00 +04:00:00 [dst=F off=14400]
+ // but std::mktime(2018-10-28 02:00:00, tm_isdst=0) fails, unlike,
+ // say, the similar Europe/Chisinau transition from +02 to +03 ...
+ // 1521935999 = 2018-03-25 01:59:59 +02:00:00 [dst=F off=7200]
+ // 1521936000 = 2018-03-25 03:00:00 +03:00:00 [dst=T off=10800]
+ // where std::mktime(2018-03-25 02:00:00, tm_isdst=0) succeeds and
+ // returns 1521936000.
+ continue;
+ }
+ if (cs == civil_second(2037, 10, 4, 2, 0, 0)) {
+ const std::string tzname = *np;
+ if (tzname == "Africa/Casablanca" || tzname == "Africa/El_Aaiun") {
+ // The "libc" implementation gets this transition wrong (at least
+ // until 2018g when it was removed), returning an offset of 3600
+ // instead of 0. TODO: Revert this when 2018g is ubiquitous.
+ continue;
+ }
+ }
+ const auto cl_lc = lc.lookup(cs);
+ SCOPED_TRACE(testing::Message() << "For " << cs << " in " << *np);
+ EXPECT_EQ(cl_zi.kind, cl_lc.kind);
+ EXPECT_EQ(cl_zi.pre, cl_lc.pre);
+ EXPECT_EQ(cl_zi.trans, cl_lc.trans);
+ EXPECT_EQ(cl_zi.post, cl_lc.post);
+ }
+ }
+ if (ep == nullptr) {
+ ASSERT_EQ(0, unsetenv("TZ"));
+ } else {
+ ASSERT_EQ(0, setenv("TZ", tz_name.c_str(), 1));
+ }
+#endif
+}
+
TEST(NextTransition, UTC) {
const auto tz = utc_time_zone();
time_zone::civil_transition trans;
diff --git a/absl/time/time.h b/absl/time/time.h
index e9e989cb..de12e560 100644
--- a/absl/time/time.h
+++ b/absl/time/time.h
@@ -153,6 +153,16 @@ class Duration {
// Value semantics.
constexpr Duration() : rep_hi_(0), rep_lo_(0) {} // zero-length duration
+ // Copyable.
+#if !defined(__clang__) && defined(_MSC_VER) && _MSC_VER < 1910
+ // Explicitly defining the constexpr copy constructor avoids an MSVC bug.
+ constexpr Duration(const Duration& d)
+ : rep_hi_(d.rep_hi_), rep_lo_(d.rep_lo_) {}
+#else
+ constexpr Duration(const Duration& d) = default;
+#endif
+ Duration& operator=(const Duration& d) = default;
+
// Compound assignment operators.
Duration& operator+=(Duration d);
Duration& operator-=(Duration d);
@@ -584,7 +594,11 @@ class Time {
// absl::Time t = absl::Now();
// absl::Time t = absl::TimeFromTimeval(tv);
// absl::Time t = absl::InfinitePast();
- constexpr Time() {}
+ constexpr Time() = default;
+
+ // Copyable.
+ constexpr Time(const Time& t) = default;
+ Time& operator=(const Time& t) = default;
// Assignment operators.
Time& operator+=(Duration d) {
@@ -826,6 +840,8 @@ class TimeZone {
public:
explicit TimeZone(time_internal::cctz::time_zone tz) : cz_(tz) {}
TimeZone() = default; // UTC, but prefer UTCTimeZone() to be explicit.
+
+ // Copyable.
TimeZone(const TimeZone&) = default;
TimeZone& operator=(const TimeZone&) = default;
diff --git a/absl/types/BUILD.bazel b/absl/types/BUILD.bazel
index 43899ca7..d56fea6e 100644
--- a/absl/types/BUILD.bazel
+++ b/absl/types/BUILD.bazel
@@ -287,6 +287,7 @@ cc_test(
linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS,
deps = [
":variant",
+ "//absl/base:config",
"//absl/base:exception_safety_testing",
"//absl/memory",
"@com_google_googletest//:gtest_main",
diff --git a/absl/types/variant_exception_safety_test.cc b/absl/types/variant_exception_safety_test.cc
index 58436f07..82425dbd 100644
--- a/absl/types/variant_exception_safety_test.cc
+++ b/absl/types/variant_exception_safety_test.cc
@@ -11,6 +11,7 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
+
#include "absl/types/variant.h"
#include <iostream>
@@ -20,8 +21,11 @@
#include "gmock/gmock.h"
#include "gtest/gtest.h"
+#include "absl/base/config.h"
#include "absl/base/internal/exception_safety_testing.h"
#include "absl/memory/memory.h"
+// See comment in absl/base/config.h
+#if !defined(ABSL_INTERNAL_MSVC_2017_DBG_MODE)
namespace absl {
namespace {
@@ -506,3 +510,5 @@ TEST(VariantExceptionSafetyTest, Swap) {
} // namespace
} // namespace absl
+
+#endif // !defined(ABSL_INTERNAL_MSVC_2017_DBG_MODE)
diff --git a/absl/types/variant_test.cc b/absl/types/variant_test.cc
index 626d5e61..f47f3a72 100644
--- a/absl/types/variant_test.cc
+++ b/absl/types/variant_test.cc
@@ -559,9 +559,14 @@ TEST(VariantTest, TestDtor) {
}
#ifdef ABSL_HAVE_EXCEPTIONS
-
+// See comment in absl/base/config.h
+#if defined(ABSL_INTERNAL_MSVC_2017_DBG_MODE)
+TEST(VariantTest, DISABLED_TestDtorValuelessByException)
+#else
// Test destruction when in the valueless_by_exception state.
-TEST(VariantTest, TestDtorValuelessByException) {
+TEST(VariantTest, TestDtorValuelessByException)
+#endif
+{
int counter = 0;
IncrementInDtor counter_adjuster(&counter);