diff --git a/common/hashing.h b/common/hashing.h index 704c0c345081..368937972d08 100644 --- a/common/hashing.h +++ b/common/hashing.h @@ -5,6 +5,7 @@ #ifndef CARBON_COMMON_HASHING_H_ #define CARBON_COMMON_HASHING_H_ +#include #include #include #include @@ -239,8 +240,8 @@ class Hasher { // This can be directly used for simple users combining some aggregation of // objects. However, when possible, prefer the variadic version below for // aggregating several primitive types into a hash. - template >> + template + requires std::has_unique_object_representations_v auto Hash(const T& value) -> void; // Incorporates a variable number of objects into the `hasher`s state in a @@ -256,9 +257,8 @@ class Hasher { // aggregations of data in this way is rarely results in effectively // high-performance hash table data structures and so should generally be // avoided. - template )>> + template + requires(... && std::has_unique_object_representations_v) auto Hash(const Ts&... value) -> void; // Simpler and more primitive functions to incorporate state represented in @@ -311,9 +311,8 @@ class Hasher { // Reads the underlying object representation of a type into a 64-bit integer // efficiently. Only supports types with unique object representation and at // most 8-bytes large. This is typically used to read primitive types. - template && sizeof(T) <= 8>> + template + requires std::has_unique_object_representations_v && (sizeof(T) <= 8) static auto ReadSmall(const T& value) -> uint64_t; // The core of the hash algorithm is this mix function. The specific @@ -429,7 +428,10 @@ class Hasher { // A dedicated namespace for `CarbonHashValue` overloads that are not found by // ADL with their associated types. For example, primitive type overloads or // overloads for types in LLVM's libraries. -namespace HashDispatch { +// +// Note that these are internal implementation details and **not** part of the +// public API. They should not be used directly by client code. +namespace InternalHashDispatch { inline auto CarbonHashValue(llvm::ArrayRef bytes, uint64_t seed) -> HashCode { @@ -482,31 +484,30 @@ template inline auto MapNullPtrToVoidPtr(const T& value) -> const T& { // This overload should never be selected for `std::nullptr_t`, so // static_assert to get some better compiler error messages. - static_assert(!std::is_same_v); + static_assert(!std::same_as); return value; } inline auto MapNullPtrToVoidPtr(std::nullptr_t /*value*/) -> const void* { return nullptr; } -// Predicate to be used in conjunction with a `nullptr` mapping routine like the -// above. +// Implementation detail predicate to be used in conjunction with a `nullptr` +// mapping routine like the above. template -constexpr bool NullPtrOrHasUniqueObjectRepresentations = - std::is_same_v || +concept NullPtrOrHasUniqueObjectRepresentations = + std::same_as || std::has_unique_object_representations_v; -template >> +template + requires NullPtrOrHasUniqueObjectRepresentations inline auto CarbonHashValue(const T& value, uint64_t seed) -> HashCode { Hasher hasher(seed); hasher.Hash(MapNullPtrToVoidPtr(value)); return static_cast(hasher); } -template )>> +template + requires(... && NullPtrOrHasUniqueObjectRepresentations) inline auto CarbonHashValue(const std::tuple& value, uint64_t seed) -> HashCode { Hasher hasher(seed); @@ -516,18 +517,17 @@ inline auto CarbonHashValue(const std::tuple& value, uint64_t seed) return static_cast(hasher); } -template && - NullPtrOrHasUniqueObjectRepresentations && - sizeof(T) <= sizeof(uint64_t) && sizeof(U) <= sizeof(uint64_t)>> +template + requires NullPtrOrHasUniqueObjectRepresentations && + NullPtrOrHasUniqueObjectRepresentations && + (sizeof(T) <= sizeof(uint64_t) && sizeof(U) <= sizeof(uint64_t)) inline auto CarbonHashValue(const std::pair& value, uint64_t seed) -> HashCode { return CarbonHashValue(std::tuple(value.first, value.second), seed); } -template >> +template + requires std::has_unique_object_representations_v inline auto CarbonHashValue(llvm::ArrayRef objs, uint64_t seed) -> HashCode { return CarbonHashValue( llvm::ArrayRef(reinterpret_cast(objs.data()), @@ -542,11 +542,11 @@ inline auto DispatchImpl(const T& value, uint64_t seed) -> HashCode { return CarbonHashValue(value, seed); } -} // namespace HashDispatch +} // namespace InternalHashDispatch template inline auto HashValue(const T& value, uint64_t seed) -> HashCode { - return HashDispatch::DispatchImpl(value, seed); + return InternalHashDispatch::DispatchImpl(value, seed); } template @@ -683,7 +683,8 @@ inline auto Hasher::HashDense(uint64_t data0, uint64_t data1) -> void { Mix(data0 ^ StaticRandomData[1], data1 ^ StaticRandomData[3] ^ buffer); } -template +template + requires std::has_unique_object_representations_v && (sizeof(T) <= 8) inline auto Hasher::ReadSmall(const T& value) -> uint64_t { const auto* storage = reinterpret_cast(&value); if constexpr (sizeof(T) == 1) { @@ -706,7 +707,8 @@ inline auto Hasher::ReadSmall(const T& value) -> uint64_t { } } -template +template + requires std::has_unique_object_representations_v inline auto Hasher::Hash(const T& value) -> void { if constexpr (sizeof(T) <= 8) { // For types size 8-bytes and smaller directly being hashed (as opposed to @@ -748,7 +750,8 @@ inline auto Hasher::Hash(const T& value) -> void { HashSizedBytesLarge(llvm::ArrayRef(data_ptr, sizeof(T))); } -template +template + requires(... && std::has_unique_object_representations_v) inline auto Hasher::Hash(const Ts&... value) -> void { if constexpr (sizeof...(Ts) == 0) { buffer ^= StaticRandomData[0]; diff --git a/common/hashing_test.cpp b/common/hashing_test.cpp index bee3ea4c8bc4..65c2ab44b2a9 100644 --- a/common/hashing_test.cpp +++ b/common/hashing_test.cpp @@ -7,7 +7,7 @@ #include #include -#include +#include #include "llvm/ADT/Sequence.h" #include "llvm/ADT/StringExtras.h" @@ -339,7 +339,8 @@ auto PrintFullWidthHex(llvm::raw_ostream& os, T value) { static_cast(value)); } -template >> +template + requires std::integral auto operator<<(llvm::raw_ostream& os, HashedValue hv) -> llvm::raw_ostream& { os << "hash " << hv.hash << " for value "; @@ -347,9 +348,8 @@ auto operator<<(llvm::raw_ostream& os, HashedValue hv) return os; } -template >, - typename = std::enable_if_t>> +template + requires std::integral && std::integral auto operator<<(llvm::raw_ostream& os, HashedValue> hv) -> llvm::raw_ostream& { os << "hash " << hv.hash << " for pair of "; diff --git a/common/ostream.h b/common/ostream.h index b01d393f2afb..2ada59306379 100644 --- a/common/ostream.h +++ b/common/ostream.h @@ -5,6 +5,7 @@ #ifndef CARBON_COMMON_OSTREAM_H_ #define CARBON_COMMON_OSTREAM_H_ +#include #include #include @@ -72,7 +73,7 @@ namespace llvm { // // To make this overload be unusually low priority, it is designed to take even // the `std::ostream` parameter as a template, and SFINAE disable itself unless -// that template parameter matches `std::ostream`. This ensures that an +// that template parameter is derived from `std::ostream`. This ensures that an // *explicit* operator will be preferred when provided. Some LLVM types may have // this, and so we want to prioritize accordingly. // @@ -80,11 +81,9 @@ namespace llvm { // `raw_os_ostream.h` so that we wouldn't need to inject into LLVM's namespace, // but supporting `std::ostream` isn't a priority for LLVM so we handle it // locally instead. -template >>, - typename = std::enable_if_t< - !std::is_same_v, raw_ostream>>> +template + requires std::derived_from, std::ostream> && + (!std::same_as, raw_ostream>) auto operator<<(StreamT& standard_out, const ClassT& value) -> StreamT& { raw_os_ostream(standard_out) << value; return standard_out;