blob: bcf90d73877ac10900f7aab9a59709b749fa4fdc [file] [log] [blame]
// Copyright 2017 The Abseil Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "absl/meta/type_traits.h"
#include <cstdint>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>
#include "gtest/gtest.h"
#include "absl/base/attributes.h"
#include "absl/base/config.h"
#include "absl/time/clock.h"
#include "absl/time/time.h"
#ifdef ABSL_HAVE_STD_STRING_VIEW
#include <string_view>
#endif
namespace {
using ::testing::StaticAssertTypeEq;
template <typename T>
using IsOwnerAndNotView =
absl::conjunction<absl::type_traits_internal::IsOwner<T>,
absl::negation<absl::type_traits_internal::IsView<T>>>;
static_assert(IsOwnerAndNotView<std::vector<int>>::value,
"vector is an owner, not a view");
static_assert(IsOwnerAndNotView<std::string>::value,
"string is an owner, not a view");
static_assert(IsOwnerAndNotView<std::wstring>::value,
"wstring is an owner, not a view");
#ifdef ABSL_HAVE_STD_STRING_VIEW
static_assert(!IsOwnerAndNotView<std::string_view>::value,
"string_view is a view, not an owner");
static_assert(!IsOwnerAndNotView<std::wstring_view>::value,
"wstring_view is a view, not an owner");
#endif
template <class T, class U>
struct simple_pair {
T first;
U second;
};
struct Dummy {};
struct ReturnType {};
struct ConvertibleToReturnType {
operator ReturnType() const; // NOLINT
};
// Unique types used as parameter types for testing the detection idiom.
struct StructA {};
struct StructB {};
struct StructC {};
struct TypeWithBarFunction {
template <class T,
absl::enable_if_t<std::is_same<T&&, StructA&>::value, int> = 0>
ReturnType bar(T&&, const StructB&, StructC&&) &&; // NOLINT
};
struct TypeWithBarFunctionAndConvertibleReturnType {
template <class T,
absl::enable_if_t<std::is_same<T&&, StructA&>::value, int> = 0>
ConvertibleToReturnType bar(T&&, const StructB&, StructC&&) &&; // NOLINT
};
template <class Class, class... Ts>
using BarIsCallableImpl =
decltype(std::declval<Class>().bar(std::declval<Ts>()...));
template <class Class, class... T>
using BarIsCallable =
absl::type_traits_internal::is_detected<BarIsCallableImpl, Class, T...>;
// NOTE: Test of detail type_traits_internal::is_detected.
TEST(IsDetectedTest, BasicUsage) {
EXPECT_TRUE((BarIsCallable<TypeWithBarFunction, StructA&, const StructB&,
StructC>::value));
EXPECT_TRUE(
(BarIsCallable<TypeWithBarFunction, StructA&, StructB&, StructC>::value));
EXPECT_TRUE(
(BarIsCallable<TypeWithBarFunction, StructA&, StructB, StructC>::value));
EXPECT_FALSE((BarIsCallable<int, StructA&, const StructB&, StructC>::value));
EXPECT_FALSE((BarIsCallable<TypeWithBarFunction&, StructA&, const StructB&,
StructC>::value));
EXPECT_FALSE((BarIsCallable<TypeWithBarFunction, StructA, const StructB&,
StructC>::value));
}
TEST(VoidTTest, BasicUsage) {
StaticAssertTypeEq<void, absl::void_t<Dummy>>();
StaticAssertTypeEq<void, absl::void_t<Dummy, Dummy, Dummy>>();
}
TEST(TypeTraitsTest, TestRemoveCVRef) {
EXPECT_TRUE(
(std::is_same<typename absl::remove_cvref<int>::type, int>::value));
EXPECT_TRUE(
(std::is_same<typename absl::remove_cvref<int&>::type, int>::value));
EXPECT_TRUE(
(std::is_same<typename absl::remove_cvref<int&&>::type, int>::value));
EXPECT_TRUE((
std::is_same<typename absl::remove_cvref<const int&>::type, int>::value));
EXPECT_TRUE(
(std::is_same<typename absl::remove_cvref<int*>::type, int*>::value));
// Does not remove const in this case.
EXPECT_TRUE((std::is_same<typename absl::remove_cvref<const int*>::type,
const int*>::value));
EXPECT_TRUE(
(std::is_same<typename absl::remove_cvref<int[2]>::type, int[2]>::value));
EXPECT_TRUE((std::is_same<typename absl::remove_cvref<int(&)[2]>::type,
int[2]>::value));
EXPECT_TRUE((std::is_same<typename absl::remove_cvref<int(&&)[2]>::type,
int[2]>::value));
EXPECT_TRUE((std::is_same<typename absl::remove_cvref<const int[2]>::type,
int[2]>::value));
EXPECT_TRUE((std::is_same<typename absl::remove_cvref<const int(&)[2]>::type,
int[2]>::value));
EXPECT_TRUE((std::is_same<typename absl::remove_cvref<const int(&&)[2]>::type,
int[2]>::value));
}
struct TypeA {};
struct TypeB {};
struct TypeC {};
struct TypeD {};
template <typename T>
struct Wrap {};
enum class TypeEnum { A, B, C, D };
struct GetTypeT {
template <typename T,
absl::enable_if_t<std::is_same<T, TypeA>::value, int> = 0>
TypeEnum operator()(Wrap<T>) const {
return TypeEnum::A;
}
template <typename T,
absl::enable_if_t<std::is_same<T, TypeB>::value, int> = 0>
TypeEnum operator()(Wrap<T>) const {
return TypeEnum::B;
}
template <typename T,
absl::enable_if_t<std::is_same<T, TypeC>::value, int> = 0>
TypeEnum operator()(Wrap<T>) const {
return TypeEnum::C;
}
// NOTE: TypeD is intentionally not handled
} constexpr GetType = {};
struct GetTypeExtT {
template <typename T>
absl::result_of_t<const GetTypeT&(T)> operator()(T&& arg) const {
return GetType(std::forward<T>(arg));
}
TypeEnum operator()(Wrap<TypeD>) const { return TypeEnum::D; }
} constexpr GetTypeExt = {};
TEST(TypeTraitsTest, TestResultOf) {
EXPECT_EQ(TypeEnum::A, GetTypeExt(Wrap<TypeA>()));
EXPECT_EQ(TypeEnum::B, GetTypeExt(Wrap<TypeB>()));
EXPECT_EQ(TypeEnum::C, GetTypeExt(Wrap<TypeC>()));
EXPECT_EQ(TypeEnum::D, GetTypeExt(Wrap<TypeD>()));
}
namespace adl_namespace {
struct DeletedSwap {};
void swap(DeletedSwap&, DeletedSwap&) = delete;
struct SpecialNoexceptSwap {
SpecialNoexceptSwap(SpecialNoexceptSwap&&) {}
SpecialNoexceptSwap& operator=(SpecialNoexceptSwap&&) { return *this; }
~SpecialNoexceptSwap() = default;
};
void swap(SpecialNoexceptSwap&, SpecialNoexceptSwap&) noexcept {}
} // namespace adl_namespace
TEST(TypeTraitsTest, IsSwappable) {
using absl::type_traits_internal::IsSwappable;
using absl::type_traits_internal::StdSwapIsUnconstrained;
EXPECT_TRUE(IsSwappable<int>::value);
struct S {};
EXPECT_TRUE(IsSwappable<S>::value);
struct NoConstruct {
NoConstruct(NoConstruct&&) = delete;
NoConstruct& operator=(NoConstruct&&) { return *this; }
~NoConstruct() = default;
};
EXPECT_EQ(IsSwappable<NoConstruct>::value, StdSwapIsUnconstrained::value);
struct NoAssign {
NoAssign(NoAssign&&) {}
NoAssign& operator=(NoAssign&&) = delete;
~NoAssign() = default;
};
EXPECT_EQ(IsSwappable<NoAssign>::value, StdSwapIsUnconstrained::value);
EXPECT_FALSE(IsSwappable<adl_namespace::DeletedSwap>::value);
EXPECT_TRUE(IsSwappable<adl_namespace::SpecialNoexceptSwap>::value);
}
TEST(TypeTraitsTest, IsNothrowSwappable) {
using absl::type_traits_internal::IsNothrowSwappable;
using absl::type_traits_internal::StdSwapIsUnconstrained;
EXPECT_TRUE(IsNothrowSwappable<int>::value);
struct NonNoexceptMoves {
NonNoexceptMoves(NonNoexceptMoves&&) {}
NonNoexceptMoves& operator=(NonNoexceptMoves&&) { return *this; }
~NonNoexceptMoves() = default;
};
EXPECT_FALSE(IsNothrowSwappable<NonNoexceptMoves>::value);
struct NoConstruct {
NoConstruct(NoConstruct&&) = delete;
NoConstruct& operator=(NoConstruct&&) { return *this; }
~NoConstruct() = default;
};
EXPECT_FALSE(IsNothrowSwappable<NoConstruct>::value);
struct NoAssign {
NoAssign(NoAssign&&) {}
NoAssign& operator=(NoAssign&&) = delete;
~NoAssign() = default;
};
EXPECT_FALSE(IsNothrowSwappable<NoAssign>::value);
EXPECT_FALSE(IsNothrowSwappable<adl_namespace::DeletedSwap>::value);
EXPECT_TRUE(IsNothrowSwappable<adl_namespace::SpecialNoexceptSwap>::value);
}
TEST(TriviallyRelocatable, PrimitiveTypes) {
static_assert(absl::is_trivially_relocatable<int>::value, "");
static_assert(absl::is_trivially_relocatable<char>::value, "");
static_assert(absl::is_trivially_relocatable<void*>::value, "");
}
// User-defined types can be trivially relocatable as long as they don't have a
// user-provided move constructor or destructor.
TEST(TriviallyRelocatable, UserDefinedTriviallyRelocatable) {
struct S {
int x;
int y;
};
static_assert(absl::is_trivially_relocatable<S>::value, "");
}
// A user-provided move constructor disqualifies a type from being trivially
// relocatable.
TEST(TriviallyRelocatable, UserProvidedMoveConstructor) {
struct S {
S(S&&) {} // NOLINT(modernize-use-equals-default)
};
static_assert(!absl::is_trivially_relocatable<S>::value, "");
}
// A user-provided copy constructor disqualifies a type from being trivially
// relocatable.
TEST(TriviallyRelocatable, UserProvidedCopyConstructor) {
struct S {
S(const S&) {} // NOLINT(modernize-use-equals-default)
};
static_assert(!absl::is_trivially_relocatable<S>::value, "");
}
// A user-provided copy assignment operator disqualifies a type from
// being trivially relocatable.
TEST(TriviallyRelocatable, UserProvidedCopyAssignment) {
struct S {
S(const S&) = default;
S& operator=(const S&) { // NOLINT(modernize-use-equals-default)
return *this;
}
};
static_assert(!absl::is_trivially_relocatable<S>::value, "");
}
// A user-provided move assignment operator disqualifies a type from
// being trivially relocatable.
TEST(TriviallyRelocatable, UserProvidedMoveAssignment) {
struct S {
S(S&&) = default;
S& operator=(S&&) { return *this; } // NOLINT(modernize-use-equals-default)
};
static_assert(!absl::is_trivially_relocatable<S>::value, "");
}
// A user-provided destructor disqualifies a type from being trivially
// relocatable.
TEST(TriviallyRelocatable, UserProvidedDestructor) {
struct S {
~S() {} // NOLINT(modernize-use-equals-default)
};
static_assert(!absl::is_trivially_relocatable<S>::value, "");
}
// TODO(b/275003464): remove the opt-out for Clang on Windows once
// __is_trivially_relocatable is used there again.
// TODO(b/324278148): remove the opt-out for Apple once
// __is_trivially_relocatable is fixed there.
// TODO(b/325479096): remove the opt-out for Clang once
// __is_trivially_relocatable is fixed there.
#if defined(ABSL_HAVE_ATTRIBUTE_TRIVIAL_ABI) && \
ABSL_HAVE_BUILTIN(__is_trivially_relocatable) && \
(defined(__cpp_impl_trivially_relocatable) || \
(!defined(__clang__) && !defined(__APPLE__) && !defined(__NVCC__)))
// A type marked with the "trivial ABI" attribute is trivially relocatable even
// if it has user-provided special members.
TEST(TriviallyRelocatable, TrivialAbi) {
struct ABSL_ATTRIBUTE_TRIVIAL_ABI S {
S(S&&) {} // NOLINT(modernize-use-equals-default)
S(const S&) {} // NOLINT(modernize-use-equals-default)
S& operator=(S&&) { return *this; }
S& operator=(const S&) { return *this; }
~S() {} // NOLINT(modernize-use-equals-default)
};
static_assert(absl::is_trivially_relocatable<S>::value, "");
}
#endif
// TODO(b/275003464): remove the opt-out for Clang on Windows once
// __is_trivially_relocatable is used there again.
// TODO(b/324278148): remove the opt-out for Apple once
// __is_trivially_relocatable is fixed there.
#if defined(ABSL_HAVE_ATTRIBUTE_TRIVIAL_ABI) && \
ABSL_HAVE_BUILTIN(__is_trivially_relocatable) && defined(__clang__) && \
!(defined(_WIN32) || defined(_WIN64)) && !defined(__APPLE__) && \
!defined(__NVCC__)
// A type marked with the "trivial ABI" attribute is trivially relocatable even
// if it has a user-provided copy constructor and a user-provided destructor.
TEST(TriviallyRelocatable, TrivialAbi_NoUserProvidedMove) {
struct ABSL_ATTRIBUTE_TRIVIAL_ABI S {
S(const S&) {} // NOLINT(modernize-use-equals-default)
~S() {} // NOLINT(modernize-use-equals-default)
};
static_assert(absl::is_trivially_relocatable<S>::value, "");
}
#endif
#ifdef ABSL_HAVE_CONSTANT_EVALUATED
constexpr int64_t NegateIfConstantEvaluated(int64_t i) {
if (absl::is_constant_evaluated()) {
return -i;
} else {
return i;
}
}
#endif // ABSL_HAVE_CONSTANT_EVALUATED
TEST(IsConstantEvaluated, is_constant_evaluated) {
#ifdef ABSL_HAVE_CONSTANT_EVALUATED
constexpr int64_t constant = NegateIfConstantEvaluated(42);
EXPECT_EQ(constant, -42);
int64_t now = absl::ToUnixSeconds(absl::Now());
int64_t not_constant = NegateIfConstantEvaluated(now);
EXPECT_EQ(not_constant, now);
static int64_t const_init = NegateIfConstantEvaluated(42);
EXPECT_EQ(const_init, -42);
#else
GTEST_SKIP() << "absl::is_constant_evaluated is not defined";
#endif // ABSL_HAVE_CONSTANT_EVALUATED
}
} // namespace