Refactor the code to introduce CorpusType as a type parameter in DomainBase. This simplifies the code a bit: 1. DomainBase knows about CorpusType and it can use it in its implementation. This will also simplify future extensions, e.g., seeded domains. 2. The derived domains no longer explicitly define `value_type`, `corpus_type`, and `has_custom_corpus_type`. Instead, they inherit them from DomainBase. Moreover, `has_custom_corpus_type` is now simply defined to be true if the value type and corpus type are not the same. PiperOrigin-RevId: 520941062
diff --git a/domain_tests/BUILD b/domain_tests/BUILD index 02f4527..b7ea683 100644 --- a/domain_tests/BUILD +++ b/domain_tests/BUILD
@@ -57,8 +57,11 @@ srcs = ["arbitrary_domains_test.cc"], deps = [ ":domain_testing", + "@com_google_absl//absl/container:flat_hash_map", "@com_google_absl//absl/container:flat_hash_set", + "@com_google_absl//absl/random", "@com_google_absl//absl/random:bit_gen_ref", + "@com_google_absl//absl/status", "@com_google_absl//absl/time", "@com_google_fuzztest//fuzztest:absl_helpers", "@com_google_fuzztest//fuzztest:domain",
diff --git a/domain_tests/arbitrary_domains_test.cc b/domain_tests/arbitrary_domains_test.cc index f8f78d7..ab2a97b 100644 --- a/domain_tests/arbitrary_domains_test.cc +++ b/domain_tests/arbitrary_domains_test.cc
@@ -18,6 +18,7 @@ #include <cmath> #include <cstdint> #include <limits> +#include <list> #include <memory> #include <optional> #include <string> @@ -31,8 +32,11 @@ #include "google/protobuf/descriptor.h" #include "gmock/gmock.h" #include "gtest/gtest.h" +#include "absl/container/flat_hash_map.h" #include "absl/container/flat_hash_set.h" #include "absl/random/bit_gen_ref.h" +#include "absl/random/random.h" +#include "absl/status/status.h" #include "absl/time/time.h" #include "./fuzztest/domain.h" #include "./domain_tests/domain_testing.h" @@ -151,12 +155,10 @@ } struct StatefulIncrementDomain - : public internal::DomainBase<StatefulIncrementDomain, int> { - using value_type = int; - // Just to make sure we don't mix value_type with corpus_type - using corpus_type = std::tuple<int>; - static constexpr bool has_custom_corpus_type = true; - + : public internal::DomainBase<StatefulIncrementDomain, int, + // Just to make sure we don't mix value_type + // with corpus_type + std::tuple<int>> { corpus_type Init(absl::BitGenRef prng) { // Minimal code to exercise prng. corpus_type result = {absl::Uniform<value_type>(prng, i, i + 1)};
diff --git a/domain_tests/container_combinators_test.cc b/domain_tests/container_combinators_test.cc index 6247c83..664124a 100644 --- a/domain_tests/container_combinators_test.cc +++ b/domain_tests/container_combinators_test.cc
@@ -79,7 +79,7 @@ // Basic checks to make sure we have a few sizes and values. // TODO: Check these values in a more principled way. absl::flat_hash_map<size_t, size_t> size_distribution; - absl::flat_hash_map<typename T::value_type, size_t> value_distribution; + absl::flat_hash_map<internal::value_type_t<T>, size_t> value_distribution; for (const auto& s : values) { ++size_distribution[s.user_value.size()]; for (const auto& v : s.user_value) ++value_distribution[v]; @@ -156,7 +156,7 @@ TestMinMaxContainerSize(Arbitrary<T>().WithMaxSize(7), 0, 7); TestMinMaxContainerSize(Arbitrary<T>().WithMinSize(3).WithMaxSize(7), 3, 7); - auto inner = Arbitrary<typename T::value_type>(); + auto inner = Arbitrary<internal::value_type_t<T>>(); TestMinMaxContainerSize(ContainerOf<T>(inner).WithSize(7), 7, 7); TestMinMaxContainerSize(ContainerOf<T>(inner).WithMinSize(7), 7, ~size_t{}); @@ -289,7 +289,8 @@ // Note that we avoid using testing::IsSubsetOf(some_values) here because it // isn't optimized for some_values being an associative collection of values. using ArgT = std::remove_reference_t<decltype(arg)>; - absl::flat_hash_set<typename ArgT::value_type> copy(arg.begin(), arg.end()); + absl::flat_hash_set<internal::value_type_t<ArgT>> copy(arg.begin(), + arg.end()); return arg.size() == copy.size(); }
diff --git a/domain_tests/domain_testing.h b/domain_tests/domain_testing.h index ec01ec2..78760d9 100644 --- a/domain_tests/domain_testing.h +++ b/domain_tests/domain_testing.h
@@ -68,7 +68,7 @@ return !o || std::isnan(*o) ? 0 : absl::Hash<T>{}(*o); } else if constexpr (internal::Requires<T>( [](auto v) -> decltype(v.hash_function()) {})) { - return (*this)(std::set<typename T::value_type>(v.begin(), v.end())); + return (*this)(std::set<internal::value_type_t<T>>(v.begin(), v.end())); } else { return absl::Hash<T>{}(v); } @@ -106,7 +106,7 @@ // simplify their access and mutation. template <typename Domain> struct Value { - using T = typename Domain::value_type; + using T = internal::value_type_t<Domain>; internal::corpus_type_t<Domain> corpus_value; T user_value; @@ -137,8 +137,8 @@ return H::combine(std::move(state), o); } else if constexpr (internal::Requires<T>( [](auto v) -> decltype(v.hash_function()) {})) { - return H::combine(std::move(state), - std::set<typename T::value_type>(v.begin(), v.end())); + return H::combine(std::move(state), std::set<internal::value_type_t<T>>( + v.begin(), v.end())); } else { return H::combine(std::move(state), v); } @@ -266,7 +266,7 @@ } template <typename Domain, typename IsTerminal, typename IsCloser, - typename T = typename Domain::value_type> + typename T = internal::value_type_t<Domain>> absl::Status TestShrink(Domain domain, const absl::flat_hash_set<Value<Domain>>& values, IsTerminal is_terminal, IsCloser is_closer_to_zero) {
diff --git a/fuzztest/BUILD b/fuzztest/BUILD index 6b120e3..b0e484d 100644 --- a/fuzztest/BUILD +++ b/fuzztest/BUILD
@@ -82,6 +82,7 @@ name = "table_of_recent_compares", hdrs = ["internal/table_of_recent_compares.h"], deps = [ + ":type_support", "@com_google_absl//absl/container:flat_hash_set", "@com_google_absl//absl/random:bit_gen_ref", "@com_google_absl//absl/random:distributions",
diff --git a/fuzztest/domain.h b/fuzztest/domain.h index 401332b..9a58420 100644 --- a/fuzztest/domain.h +++ b/fuzztest/domain.h
@@ -79,8 +79,11 @@ using corpus_type = internal::GenericDomainCorpusType; static constexpr bool has_custom_corpus_type = true; - template <typename Inner> - Domain(const internal::DomainBase<Inner, T>& inner) + // Intentionally not marked as explicit to allow implicit conversion from the + // internal domain implementations. + template <int&... ExplicitArgumentBarrier, typename Inner, + typename CorpusType> + Domain(const internal::DomainBase<Inner, T, CorpusType>& inner) : inner_(new auto(static_cast<const Inner&>(inner))) {} Domain(const Domain& other) { *this = other; } @@ -251,11 +254,13 @@ // Domains that uses a layer of indirection. This allows us to create domains // for recursive data structures. template <typename T> - class IndirectDomain : public internal::DomainBase<IndirectDomain<T>> { + class IndirectDomain + : public internal::DomainBase<IndirectDomain<T>, + internal::value_type_t<Domain<T>>, + internal::corpus_type_t<Domain<T>>> { public: - using value_type = typename Domain<T>::value_type; - using corpus_type = typename Domain<T>::corpus_type; - static constexpr bool has_custom_corpus_type = true; + using typename IndirectDomain::DomainBase::corpus_type; + using typename IndirectDomain::DomainBase::value_type; explicit IndirectDomain(internal::MoveOnlyAny* indirect) : indirect_inner_(indirect) {} @@ -301,16 +306,18 @@ // Same as Domain<T>, but also holds ownership of the lookup table. // This is for toplevel domains. template <typename T> - class OwningDomain : public internal::DomainBase<OwningDomain<T>> { + class OwningDomain + : public internal::DomainBase<OwningDomain<T>, + internal::value_type_t<Domain<T>>, + internal::corpus_type_t<Domain<T>>> { public: + using typename OwningDomain::DomainBase::corpus_type; + using typename OwningDomain::DomainBase::value_type; + OwningDomain(const Domain<T>& inner, std::unique_ptr<DomainLookUpTable> domain_lookup_table) : inner_(inner), domain_lookup_table_(std::move(domain_lookup_table)) {} - using value_type = typename Domain<T>::value_type; - using corpus_type = typename Domain<T>::corpus_type; - static constexpr bool has_custom_corpus_type = true; - corpus_type Init(absl::BitGenRef prng) { return inner_.Init(prng); } void Mutate(corpus_type& val, absl::BitGenRef prng, bool only_shrink) { @@ -557,8 +564,8 @@ template <typename T, int&... ExplicitArgumentBarrier, typename Inner> auto ContainerOf(Inner inner) { static_assert( - std::is_same_v<internal::DropConst<typename T::value_type>, - internal::DropConst<typename Inner::value_type>>); + std::is_same_v<internal::DropConst<internal::value_type_t<T>>, + internal::DropConst<internal::value_type_t<Inner>>>); return internal::ContainerOfImpl<T, Inner>(std::move(inner)); } @@ -572,11 +579,11 @@ // template <template <typename, typename...> class T, int&... ExplicitArgumentBarrier, typename Inner, - typename C = T<typename Inner::value_type>> + typename C = T<internal::value_type_t<Inner>>> auto ContainerOf(Inner inner) { static_assert( - std::is_same_v<internal::DropConst<typename C::value_type>, - internal::DropConst<typename Inner::value_type>>); + std::is_same_v<internal::DropConst<internal::value_type_t<C>>, + internal::DropConst<internal::value_type_t<Inner>>>); return internal::ContainerOfImpl<C, Inner>(std::move(inner)); } @@ -654,7 +661,7 @@ template <int&... ExplicitArgumentBarrier, typename Inner1, typename Inner2> auto PairOf(Inner1 inner1, Inner2 inner2) { return internal::AggregateOfImpl< - std::pair<typename Inner1::value_type, typename Inner2::value_type>, + std::pair<internal::value_type_t<Inner1>, internal::value_type_t<Inner2>>, internal::RequireCustomCorpusType::kNo, Inner1, Inner2>( std::in_place, std::move(inner1), std::move(inner2)); } @@ -668,7 +675,7 @@ // template <int&... ExplicitArgumentBarrier, typename... Inner> auto TupleOf(Inner... inner) { - return internal::AggregateOfImpl<std::tuple<typename Inner::value_type...>, + return internal::AggregateOfImpl<std::tuple<internal::value_type_t<Inner>...>, internal::RequireCustomCorpusType::kNo, Inner...>(std::in_place, std::move(inner)...); @@ -698,7 +705,7 @@ template <int&... ExplicitArgumentBarrier, typename... Inner> auto VariantOf(Inner... inner) { - return VariantOf<std::variant<typename Inner::value_type...>>( + return VariantOf<std::variant<internal::value_type_t<Inner>...>>( std::move(inner)...); } @@ -725,7 +732,7 @@ template <int&... ExplicitArgumentBarrier, typename Inner> auto OptionalOf(Inner inner) { - return OptionalOf<std::optional<typename Inner::value_type>>( + return OptionalOf<std::optional<internal::value_type_t<Inner>>>( std::move(inner)); } @@ -775,7 +782,7 @@ // template <int&... ExplicitArgumentBarrier, typename Inner> auto UniquePtrOf(Inner inner) { - return SmartPointerOf<std::unique_ptr<typename Inner::value_type>>( + return SmartPointerOf<std::unique_ptr<internal::value_type_t<Inner>>>( std::move(inner)); } @@ -788,7 +795,7 @@ // template <int&... ExplicitArgumentBarrier, typename Inner> auto SharedPtrOf(Inner inner) { - return SmartPointerOf<std::shared_ptr<typename Inner::value_type>>( + return SmartPointerOf<std::shared_ptr<internal::value_type_t<Inner>>>( std::move(inner)); } @@ -834,7 +841,8 @@ // template <int&... ExplicitArgumentBarrier, typename Inner> auto VectorOf(Inner inner) { - return ContainerOf<std::vector<typename Inner::value_type>>(std::move(inner)); + return ContainerOf<std::vector<internal::value_type_t<Inner>>>( + std::move(inner)); } // DequeOf(inner) combinator creates a `std::deque` domain with elements of the @@ -846,7 +854,8 @@ // template <int&... ExplicitArgumentBarrier, typename Inner> auto DequeOf(Inner inner) { - return ContainerOf<std::deque<typename Inner::value_type>>(std::move(inner)); + return ContainerOf<std::deque<internal::value_type_t<Inner>>>( + std::move(inner)); } // ListOf(inner) combinator creates a `std::list` domain with elements of the @@ -858,7 +867,8 @@ // template <int&... ExplicitArgumentBarrier, typename Inner> auto ListOf(Inner inner) { - return ContainerOf<std::list<typename Inner::value_type>>(std::move(inner)); + return ContainerOf<std::list<internal::value_type_t<Inner>>>( + std::move(inner)); } // SetOf(inner) combinator creates a `std::set` domain with elements of the @@ -870,7 +880,7 @@ // template <int&... ExplicitArgumentBarrier, typename Inner> auto SetOf(Inner inner) { - return ContainerOf<std::set<typename Inner::value_type>>(std::move(inner)); + return ContainerOf<std::set<internal::value_type_t<Inner>>>(std::move(inner)); } // MapOf(key_domain, value_domain) combinator creates a `std::map` domain with @@ -883,8 +893,8 @@ template <int&... ExplicitArgumentBarrier, typename KeyDomain, typename ValueDomain> auto MapOf(KeyDomain key_domain, ValueDomain value_domain) { - return ContainerOf<std::map<typename KeyDomain::value_type, - typename ValueDomain::value_type>>( + return ContainerOf<std::map<internal::value_type_t<KeyDomain>, + internal::value_type_t<ValueDomain>>>( PairOf(std::move(key_domain), std::move(value_domain))); } @@ -897,7 +907,7 @@ // template <int&... ExplicitArgumentBarrier, typename Inner> auto UnorderedSetOf(Inner inner) { - return ContainerOf<std::unordered_set<typename Inner::value_type>>( + return ContainerOf<std::unordered_set<internal::value_type_t<Inner>>>( std::move(inner)); } @@ -912,8 +922,8 @@ template <int&... ExplicitArgumentBarrier, typename KeyDomain, typename ValueDomain> auto UnorderedMapOf(KeyDomain key_domain, ValueDomain value_domain) { - return ContainerOf<std::unordered_map<typename KeyDomain::value_type, - typename ValueDomain::value_type>>( + return ContainerOf<std::unordered_map<internal::value_type_t<KeyDomain>, + internal::value_type_t<ValueDomain>>>( PairOf(std::move(key_domain), std::move(value_domain))); } @@ -941,9 +951,9 @@ // All value_types of inner domains must be the same, though they can have // different corpus_types. using value_type = - typename std::tuple_element_t<0, std::tuple<Inner...>>::value_type; + internal::value_type_t<std::tuple_element_t<0, std::tuple<Inner...>>>; static_assert(std::conjunction_v< - std::is_same<value_type, typename Inner::value_type>...>, + std::is_same<value_type, internal::value_type_t<Inner>>...>, "All domains in a ArrayOf must have the same value_type."); return internal::AggregateOfImpl<std::array<value_type, sizeof...(Inner)>, internal::RequireCustomCorpusType::kNo, @@ -990,8 +1000,8 @@ template <typename T, int&... ExplicitArgumentBarrier, typename Inner> auto UniqueElementsContainerOf(Inner inner) { static_assert( - std::is_same_v<internal::DropConst<typename T::value_type>, - internal::DropConst<typename Inner::value_type>>); + std::is_same_v<internal::DropConst<internal::value_type_t<T>>, + internal::DropConst<internal::value_type_t<Inner>>>); return internal::UniqueElementsContainerImpl<T, Inner>(std::move(inner)); } @@ -1007,7 +1017,7 @@ // template <typename Inner> auto UniqueElementsVectorOf(Inner inner) { - return UniqueElementsContainerOf<std::vector<typename Inner::value_type>>( + return UniqueElementsContainerOf<std::vector<internal::value_type_t<Inner>>>( std::move(inner)); }
diff --git a/fuzztest/internal/domains/aggregate_of_impl.h b/fuzztest/internal/domains/aggregate_of_impl.h index e93932d..674ad39 100644 --- a/fuzztest/internal/domains/aggregate_of_impl.h +++ b/fuzztest/internal/domains/aggregate_of_impl.h
@@ -35,19 +35,23 @@ enum class RequireCustomCorpusType { kNo, kYes }; +// For user defined types (structs) we require a custom corpus_type +// (std::tuple), because the serializer does not support structs, only tuples. +template <typename T, RequireCustomCorpusType require_custom, typename... Inner> +using AggregateOfImplCorpusType = + std::conditional_t<require_custom == RequireCustomCorpusType::kYes || + (Inner::has_custom_corpus_type || ...), + std::tuple<corpus_type_t<Inner>...>, T>; + template <typename T, RequireCustomCorpusType require_custom, typename... Inner> class AggregateOfImpl - : public DomainBase<AggregateOfImpl<T, require_custom, Inner...>, T> { + : public DomainBase< + AggregateOfImpl<T, require_custom, Inner...>, T, + AggregateOfImplCorpusType<T, require_custom, Inner...>> { public: - using value_type = T; - // For user defined types (structs) we require a custom corpus_type - // (std::tuple), because the serializer does not support structs, only tuples. - static constexpr bool has_custom_corpus_type = - require_custom == RequireCustomCorpusType::kYes || - (Inner::has_custom_corpus_type || ...); - using corpus_type = - std::conditional_t<has_custom_corpus_type, - std::tuple<corpus_type_t<Inner>...>, T>; + using AggregateOfImpl::DomainBase::has_custom_corpus_type; + using typename AggregateOfImpl::DomainBase::corpus_type; + using typename AggregateOfImpl::DomainBase::value_type; AggregateOfImpl() = default; explicit AggregateOfImpl(std::in_place_t, Inner... inner) @@ -106,8 +110,7 @@ if (*tuple_elem >= 0 && *tuple_elem < sizeof...(Inner)) { Switch<sizeof...(Inner)>(*tuple_elem, [&](auto I) { PrintValue(std::get<I>(inner_), - std::get<I>(val.GetAs<corpus_type_t<AggregateOfImpl>>()), - out, mode); + std::get<I>(val.GetAs<corpus_type>()), out, mode); }); } }
diff --git a/fuzztest/internal/domains/arbitrary_impl.h b/fuzztest/internal/domains/arbitrary_impl.h index a641885..e10187c 100644 --- a/fuzztest/internal/domains/arbitrary_impl.h +++ b/fuzztest/internal/domains/arbitrary_impl.h
@@ -67,7 +67,7 @@ class ArbitraryImpl<T, std::enable_if_t<is_monostate_v<T>>> : public DomainBase<ArbitraryImpl<T>> { public: - using value_type = T; + using typename ArbitraryImpl::DomainBase::value_type; value_type Init(absl::BitGenRef) { return value_type{}; } @@ -80,8 +80,6 @@ template <> class ArbitraryImpl<bool> : public DomainBase<ArbitraryImpl<bool>> { public: - using value_type = bool; - value_type Init(absl::BitGenRef prng) { return static_cast<bool>(absl::Uniform(prng, 0, 2)); } @@ -103,7 +101,8 @@ std::numeric_limits<T>::is_integer>> : public DomainBase<ArbitraryImpl<T>> { public: - using value_type = T; + using typename ArbitraryImpl::DomainBase::value_type; + static constexpr bool is_memory_dictionary_compatible_v = sizeof(T) == 1 || sizeof(T) == 2 || sizeof(T) == 4 || sizeof(T) == 8; using IntegerDictionaryT = @@ -191,7 +190,7 @@ class ArbitraryImpl<T, std::enable_if_t<std::is_floating_point_v<T>>> : public DomainBase<ArbitraryImpl<T>> { public: - using value_type = T; + using typename ArbitraryImpl::DomainBase::value_type; value_type Init(absl::BitGenRef prng) { const T special[] = { @@ -234,20 +233,20 @@ // Arbitrary for containers. template <typename T> class ArbitraryImpl< - T, std::enable_if_t<always_true<T>, - decltype( - // Iterable - T().begin(), T().end(), T().size(), - // Values are mutable - // This rejects associative containers, for example - // *T().begin() = std::declval<typename - // T::value_type>(), Can insert and erase elements - T().insert(T().end(), - std::declval<typename T::value_type>()), - T().erase(T().begin()), - // - (void)0)>> - : public ContainerOfImpl<T, ArbitraryImpl<typename T::value_type>> {}; + T, + std::enable_if_t<always_true<T>, + decltype( + // Iterable + T().begin(), T().end(), T().size(), + // Values are mutable + // This rejects associative containers, for example + // *T().begin() = std::declval<value_type_t<T>>(), + // Can insert and erase elements + T().insert(T().end(), std::declval<value_type_t<T>>()), + T().erase(T().begin()), + // + (void)0)>> + : public ContainerOfImpl<T, ArbitraryImpl<value_type_t<T>>> {}; // Arbitrary for std::string_view. // @@ -255,13 +254,14 @@ // better. See below. template <typename Char> class ArbitraryImpl<std::basic_string_view<Char>> - : public DomainBase<ArbitraryImpl<std::basic_string_view<Char>>> { + : public DomainBase<ArbitraryImpl<std::basic_string_view<Char>>, + std::basic_string_view<Char>, + // We use a vector to better manage the buffer and help + // ASan find out-of-bounds bugs. + std::vector<Char>> { public: - using value_type = std::string_view; - // We use a vector to better manage the buffer and help ASan find - // out-of-bounds bugs. - using corpus_type = std::vector<Char>; - static constexpr bool has_custom_corpus_type = true; + using typename ArbitraryImpl::DomainBase::corpus_type; + using typename ArbitraryImpl::DomainBase::value_type; corpus_type Init(absl::BitGenRef prng) { return inner_.Init(prng); }
diff --git a/fuzztest/internal/domains/bit_flag_combination_of_impl.h b/fuzztest/internal/domains/bit_flag_combination_of_impl.h index b612a98..f7ec199 100644 --- a/fuzztest/internal/domains/bit_flag_combination_of_impl.h +++ b/fuzztest/internal/domains/bit_flag_combination_of_impl.h
@@ -30,7 +30,7 @@ class BitFlagCombinationOfImpl : public DomainBase<BitFlagCombinationOfImpl<T>> { public: - using value_type = T; + using typename BitFlagCombinationOfImpl::DomainBase::value_type; explicit BitFlagCombinationOfImpl(absl::Span<const T> flags) : flags_(flags.begin(), flags.end()) {
diff --git a/fuzztest/internal/domains/container_of_impl.h b/fuzztest/internal/domains/container_of_impl.h index 219f216..f09e965 100644 --- a/fuzztest/internal/domains/container_of_impl.h +++ b/fuzztest/internal/domains/container_of_impl.h
@@ -52,23 +52,32 @@ return std::next(val.begin(), i); } +template <typename ContainerDomain, + typename ValueType = ExtractTemplateParameter<0, ContainerDomain>, + typename InnerDomain = ExtractTemplateParameter<1, ContainerDomain>> +using ContainerOfImplBaseCorpusType = std::conditional_t< + // Specialized handling of vector<bool> since you can't actually hold + // a reference to a single bit but instead get a proxy value. + is_bitvector_v<ValueType> || + // If the container is associative we force a custom corpus type to + // allow modifying the keys. + is_associative_container_v<ValueType> || + InnerDomain::has_custom_corpus_type, + // Corpus type might be immutable (eg std::pair<const int, int> for maps + // inner domain). We store them in a std::list to allow for this. + std::list<corpus_type_t<InnerDomain>>, ValueType>; + // Common base for container domains. Provides common APIs. template <typename Derived> -class ContainerOfImplBase : public DomainBase<Derived> { +class ContainerOfImplBase + : public DomainBase<Derived, ExtractTemplateParameter<0, Derived>, + ContainerOfImplBaseCorpusType<Derived>> { using InnerDomainT = ExtractTemplateParameter<1, Derived>; public: - using value_type = ExtractTemplateParameter<0, Derived>; - static constexpr bool has_custom_corpus_type = - // Specialized handling of vector<bool> since you can't actually hold - // a reference to a single bit but instead get a proxy value. - is_bitvector_v<value_type> || - // If the container is associative we force a custom corpus type to allow - // modifying the keys. - is_associative_container_v<value_type> || - InnerDomainT::has_custom_corpus_type; - // `corpus_type` might be immutable (eg std::pair<const int, int> for maps - // inner domain). We store them in a std::list to allow for this. + using ContainerOfImplBase::DomainBase::has_custom_corpus_type; + using typename ContainerOfImplBase::DomainBase::corpus_type; + using typename ContainerOfImplBase::DomainBase::value_type; // Some container mutation only applies to vector or string types which do // not have a custom corpus type. @@ -83,10 +92,6 @@ is_memory_dictionary_compatible<InnerDomainT>::value && is_vector_or_string; - using corpus_type = - std::conditional_t<has_custom_corpus_type, - std::list<corpus_type_t<InnerDomainT>>, value_type>; - // If `!container_has_memory_dict`, dict_type is a bool and dict // is not used. This conditional_t may be neccessary because some // value_type may not have copy constructors(for example, proto). @@ -281,7 +286,7 @@ InnerDomainT Inner() const { return inner_; } - template <typename OtherDerived> + template <typename> friend class ContainerOfImplBase; template <typename OtherDerived> @@ -367,10 +372,9 @@ using Base = typename AssociativeContainerOfImpl::ContainerOfImplBase; public: - using value_type = T; - using corpus_type = typename Base::corpus_type; - static constexpr bool has_custom_corpus_type = Base::has_custom_corpus_type; - static_assert(has_custom_corpus_type, "Must be custom to mutate keys"); + using typename Base::corpus_type; + + static_assert(Base::has_custom_corpus_type, "Must be custom to mutate keys"); AssociativeContainerOfImpl() = default; explicit AssociativeContainerOfImpl(InnerDomain inner) @@ -475,8 +479,7 @@ using Base = typename SequenceContainerOfImpl::ContainerOfImplBase; public: - using value_type = T; - using corpus_type = typename Base::corpus_type; + using typename Base::corpus_type; SequenceContainerOfImpl() = default; explicit SequenceContainerOfImpl(InnerDomain inner)
diff --git a/fuzztest/internal/domains/domain_base.h b/fuzztest/internal/domains/domain_base.h index 837579d..e8bdb2d 100644 --- a/fuzztest/internal/domains/domain_base.h +++ b/fuzztest/internal/domains/domain_base.h
@@ -39,7 +39,7 @@ // of just "false". template <typename T, typename U> constexpr void CheckIsSame() { - static_assert(std::is_same_v<std::remove_const_t<T>, std::remove_const_t<U>>); + static_assert(std::is_same_v<T, U>); } // Corpus value type used by Domain<T> template, regardless of T. @@ -99,9 +99,15 @@ }; template <typename Derived, - typename ValueType = ExtractTemplateParameter<0, Derived>> + typename ValueType = ExtractTemplateParameter<0, Derived>, + typename CorpusType = ValueType> class DomainBase : public TypedDomainInterface<ValueType> { public: + using value_type = ValueType; + using corpus_type = CorpusType; + static constexpr bool has_custom_corpus_type = + !std::is_same_v<ValueType, CorpusType>; + DomainBase() { // Check that the interface of `Derived` matches the requirements for a // domain implementation. We check these inside the constructor of @@ -109,47 +115,37 @@ // check them at class scope we would see an incomplete `Derived` class and // the checks would not work. - // Has value_type. - using CheckValueType = typename Derived::value_type; - static_assert(std::is_same_v<ValueType, CheckValueType>); - if constexpr (Derived::has_custom_corpus_type) { - // The type of values that are mutated and stored internally in the - // "corpus" may be different from the type of values produced by the - // domain. For example, the corpus_type of InRegexp is a custom data - // structure representing a path through a state machine, but the domain - // produces values of type std::string. - using CheckCorpusType [[maybe_unused]] = typename Derived::corpus_type; - } else { - CheckIsSame<typename Derived::value_type, corpus_type_t<Derived>>(); - } + CheckIsSame<ValueType, value_type_t<Derived>>(); + CheckIsSame<CorpusType, corpus_type_t<Derived>>(); + static_assert(has_custom_corpus_type == Derived::has_custom_corpus_type); } std::unique_ptr<UntypedDomainInterface> Clone() const final { return std::make_unique<Derived>(derived()); } - GenericDomainCorpusType UntypedInit(absl::BitGenRef ref) final { - return GenericDomainCorpusType(std::in_place_type<corpus_type_t<Derived>>, - derived().Init(ref)); + GenericDomainCorpusType UntypedInit(absl::BitGenRef prng) final { + return GenericDomainCorpusType(std::in_place_type<CorpusType>, + derived().Init(prng)); } void UntypedMutate(GenericDomainCorpusType& val, absl::BitGenRef prng, bool only_shrink) final { - derived().Mutate(val.GetAs<corpus_type_t<Derived>>(), prng, only_shrink); + derived().Mutate(val.GetAs<CorpusType>(), prng, only_shrink); } void UntypedUpdateMemoryDictionary(const GenericDomainCorpusType& val) final { - derived().UpdateMemoryDictionary(val.GetAs<corpus_type_t<Derived>>()); + derived().UpdateMemoryDictionary(val.GetAs<CorpusType>()); } ValueType TypedGetValue(const GenericDomainCorpusType& v) const final { - return derived().GetValue(v.GetAs<corpus_type_t<Derived>>()); + return derived().GetValue(v.GetAs<CorpusType>()); } std::optional<GenericDomainCorpusType> TypedFromValue( const ValueType& v) const final { if (auto c = derived().FromValue(v)) { - return GenericDomainCorpusType(std::in_place_type<corpus_type_t<Derived>>, + return GenericDomainCorpusType(std::in_place_type<CorpusType>, *std::move(c)); } else { return std::nullopt; @@ -159,7 +155,7 @@ std::optional<GenericDomainCorpusType> UntypedParseCorpus( const IRObject& obj) const final { if (auto res = derived().ParseCorpus(obj)) { - return GenericDomainCorpusType(std::in_place_type<corpus_type_t<Derived>>, + return GenericDomainCorpusType(std::in_place_type<CorpusType>, *std::move(res)); } else { return std::nullopt; @@ -168,20 +164,18 @@ IRObject UntypedSerializeCorpus( const GenericDomainCorpusType& v) const final { - return derived().SerializeCorpus( - v.template GetAs<corpus_type_t<Derived>>()); + return derived().SerializeCorpus(v.template GetAs<CorpusType>()); } uint64_t UntypedCountNumberOfFields(const GenericDomainCorpusType& v) final { - return derived().CountNumberOfFields(v.GetAs<corpus_type_t<Derived>>()); + return derived().CountNumberOfFields(v.GetAs<CorpusType>()); } uint64_t UntypedMutateSelectedField(GenericDomainCorpusType& v, absl::BitGenRef prng, bool only_shrink, uint64_t selected_field_index) final { - return derived().MutateSelectedField(v.GetAs<corpus_type_t<Derived>>(), - prng, only_shrink, - selected_field_index); + return derived().MutateSelectedField(v.GetAs<CorpusType>(), prng, + only_shrink, selected_field_index); } int UntypedPrintCorpusValue(const GenericDomainCorpusType& val, @@ -190,50 +184,39 @@ FUZZTEST_INTERNAL_CHECK( !tuple_elem.has_value(), "No tuple element should be specified for this override."); - internal::PrintValue(derived(), val.GetAs<corpus_type_t<Derived>>(), out, - mode); + internal::PrintValue(derived(), val.GetAs<CorpusType>(), out, mode); return -1; } // Default GetValue and FromValue functions for !has_custom_corpus_type // domains. ValueType GetValue(const ValueType& v) const { - static_assert(!Derived::has_custom_corpus_type); + static_assert(!has_custom_corpus_type); return v; } std::optional<ValueType> FromValue(const ValueType& v) const { - static_assert(!Derived::has_custom_corpus_type); + static_assert(!has_custom_corpus_type); return v; } - template <typename D = Derived> - std::optional<corpus_type_t<D>> ParseCorpus(const IRObject& obj) const { - static_assert(!D::has_custom_corpus_type); - return obj.ToCorpus<corpus_type_t<D>>(); + std::optional<CorpusType> ParseCorpus(const IRObject& obj) const { + static_assert(!has_custom_corpus_type); + return obj.ToCorpus<CorpusType>(); } - template <typename D = Derived> - IRObject SerializeCorpus(const corpus_type_t<D>& v) const { - static_assert(!D::has_custom_corpus_type); + IRObject SerializeCorpus(const CorpusType& v) const { + static_assert(!has_custom_corpus_type); return IRObject::FromCorpus(v); } - template <typename D = Derived> - void UpdateMemoryDictionary(const corpus_type_t<D>& val) {} + void UpdateMemoryDictionary(const CorpusType& val) {} - template <typename D = Derived> - uint64_t CountNumberOfFields(const corpus_type_t<D>&) { + uint64_t CountNumberOfFields(const CorpusType&) { return 0; } + + uint64_t MutateSelectedField(CorpusType&, absl::BitGenRef, bool, uint64_t) { return 0; } - template <typename D = Derived> - uint64_t MutateSelectedField(corpus_type_t<D>&, absl::BitGenRef, bool, - uint64_t) { - return 0; - } - - static constexpr bool has_custom_corpus_type = false; - private: Derived& derived() { return static_cast<Derived&>(*this); } const Derived& derived() const { return static_cast<const Derived&>(*this); }
diff --git a/fuzztest/internal/domains/element_of_impl.h b/fuzztest/internal/domains/element_of_impl.h index 39a4d1d..bf53844 100644 --- a/fuzztest/internal/domains/element_of_impl.h +++ b/fuzztest/internal/domains/element_of_impl.h
@@ -30,12 +30,14 @@ namespace fuzztest::internal { +enum class ElementOfImplCorpusType : size_t; + template <typename T> -class ElementOfImpl : public DomainBase<ElementOfImpl<T>> { +class ElementOfImpl + : public DomainBase<ElementOfImpl<T>, T, ElementOfImplCorpusType> { public: - using value_type = T; - enum class corpus_type : size_t; - static constexpr bool has_custom_corpus_type = true; + using typename ElementOfImpl::DomainBase::corpus_type; + using typename ElementOfImpl::DomainBase::value_type; explicit ElementOfImpl(std::vector<T> values) : values_(values) { FUZZTEST_INTERNAL_CHECK_PRECONDITION(
diff --git a/fuzztest/internal/domains/filter_impl.h b/fuzztest/internal/domains/filter_impl.h index 3061b81..ee13a1a 100644 --- a/fuzztest/internal/domains/filter_impl.h +++ b/fuzztest/internal/domains/filter_impl.h
@@ -29,11 +29,11 @@ template <typename Pred, typename Inner> class FilterImpl - : public DomainBase<FilterImpl<Pred, Inner>, typename Inner::value_type> { + : public DomainBase<FilterImpl<Pred, Inner>, value_type_t<Inner>, + corpus_type_t<Inner>> { public: - using corpus_type = corpus_type_t<Inner>; - using value_type = typename Inner::value_type; - static constexpr bool has_custom_corpus_type = Inner::has_custom_corpus_type; + using typename FilterImpl::DomainBase::corpus_type; + using typename FilterImpl::DomainBase::value_type; FilterImpl() = default; explicit FilterImpl(Pred predicate, Inner inner)
diff --git a/fuzztest/internal/domains/flat_map_impl.h b/fuzztest/internal/domains/flat_map_impl.h index 9fee4e3..0ebdc6c 100644 --- a/fuzztest/internal/domains/flat_map_impl.h +++ b/fuzztest/internal/domains/flat_map_impl.h
@@ -31,20 +31,23 @@ namespace fuzztest::internal { template <typename FlatMapper, typename... Inner> +using FlatMapOutputDomain = std::decay_t< + std::invoke_result_t<FlatMapper, const value_type_t<Inner>&...>>; + +template <typename FlatMapper, typename... Inner> class FlatMapImpl : public DomainBase< FlatMapImpl<FlatMapper, Inner...>, - typename std::decay_t<std::invoke_result_t< - FlatMapper, const typename Inner::value_type&...>>::value_type> { + value_type_t<FlatMapOutputDomain<FlatMapper, Inner...>>, + std::tuple<corpus_type_t<FlatMapOutputDomain<FlatMapper, Inner...>>, + corpus_type_t<Inner>...>> { private: using output_domain = std::decay_t< - std::invoke_result_t<FlatMapper, const typename Inner::value_type&...>>; + std::invoke_result_t<FlatMapper, const value_type_t<Inner>&...>>; public: - using corpus_type = - std::tuple<corpus_type_t<output_domain>, corpus_type_t<Inner>...>; - using value_type = typename output_domain::value_type; - static constexpr bool has_custom_corpus_type = true; + using typename FlatMapImpl::DomainBase::corpus_type; + using typename FlatMapImpl::DomainBase::value_type; FlatMapImpl() = default; explicit FlatMapImpl(FlatMapper mapper, Inner... inner)
diff --git a/fuzztest/internal/domains/in_grammar_impl.h b/fuzztest/internal/domains/in_grammar_impl.h index d2662fa..7e1e4a9 100644 --- a/fuzztest/internal/domains/in_grammar_impl.h +++ b/fuzztest/internal/domains/in_grammar_impl.h
@@ -49,9 +49,9 @@ using ASTTypeId = int; struct ASTNode { ASTTypeId type_id; - std::variant<std::monostate, // If the node is a string terminal. - internal::InRegexpImpl::DFAPath, // If the node is a regex - // terminal. + std::variant<std::monostate, // If the node is a string terminal. + DFAPath, // If the node is a regex + // terminal. std::vector<ASTNode>> // If the node is a non-terminal. children; @@ -163,17 +163,14 @@ static IRObject SerializeCorpus(const ASTNode& astnode) { FUZZTEST_INTERNAL_CHECK( - CheckASTNodeTypeIdAndChildType<internal::InRegexpImpl::DFAPath>(astnode, - id), - "Invalid node!"); - return WrapASTIntoIRObject( - astnode, - GetInnerRegexpDomain().SerializeCorpus( - std::get<internal::InRegexpImpl::DFAPath>(astnode.children))); + CheckASTNodeTypeIdAndChildType<DFAPath>(astnode, id), "Invalid node!"); + return WrapASTIntoIRObject(astnode, + GetInnerRegexpDomain().SerializeCorpus( + std::get<DFAPath>(astnode.children))); } static std::optional<ASTNode> ParseCorpus(const IRObject& obj) { - if (!CheckASTCorpusStructure<internal::InRegexpImpl::DFAPath>(obj)) { + if (!CheckASTCorpusStructure<DFAPath>(obj)) { return std::nullopt; } auto subs = obj.Subs(); @@ -185,7 +182,7 @@ if (!path) { return std::nullopt; } - result.children.emplace<internal::InRegexpImpl::DFAPath>(*path); + result.children.emplace<DFAPath>(*path); return result; } @@ -636,11 +633,11 @@ absl::flat_hash_map<ASTTypeId, std::vector<ASTNode*>>& groups); template <typename TopDomain> -class InGrammarImpl : public DomainBase<InGrammarImpl<TopDomain>, std::string> { +class InGrammarImpl + : public DomainBase<InGrammarImpl<TopDomain>, std::string, ASTNode> { public: - using value_type = std::string; - using corpus_type = ASTNode; - static constexpr bool has_custom_corpus_type = true; + using typename InGrammarImpl::DomainBase::corpus_type; + using typename InGrammarImpl::DomainBase::value_type; ASTNode Init(absl::BitGenRef prng) { return TopDomain::Init(prng); }
diff --git a/fuzztest/internal/domains/in_range_impl.h b/fuzztest/internal/domains/in_range_impl.h index 9ff0198..9757131 100644 --- a/fuzztest/internal/domains/in_range_impl.h +++ b/fuzztest/internal/domains/in_range_impl.h
@@ -36,7 +36,8 @@ template <typename T> class InRangeImpl : public DomainBase<InRangeImpl<T>> { public: - using value_type = T; + using typename InRangeImpl::DomainBase::value_type; + constexpr static bool T_is_integer = std::numeric_limits<T>::is_integer; constexpr static bool T_is_signed = std::is_signed<T>::value; constexpr static bool T_is_memory_dictionary_compatible =
diff --git a/fuzztest/internal/domains/in_regexp_impl.h b/fuzztest/internal/domains/in_regexp_impl.h index f5afe1a..2790e79 100644 --- a/fuzztest/internal/domains/in_regexp_impl.h +++ b/fuzztest/internal/domains/in_regexp_impl.h
@@ -34,14 +34,10 @@ namespace fuzztest::internal { -class InRegexpImpl : public DomainBase<InRegexpImpl, std::string> { +using DFAPath = std::vector<RegexpDFA::Edge>; + +class InRegexpImpl : public DomainBase<InRegexpImpl, std::string, DFAPath> { public: - using DFAPath = std::vector<RegexpDFA::Edge>; - using value_type = std::string; - using corpus_type = DFAPath; - - static constexpr bool has_custom_corpus_type = true; - explicit InRegexpImpl(std::string_view regex_str) : dfa_(RegexpDFA::Create(regex_str)) {}
diff --git a/fuzztest/internal/domains/map_impl.h b/fuzztest/internal/domains/map_impl.h index ee0f7d1..ca2958d 100644 --- a/fuzztest/internal/domains/map_impl.h +++ b/fuzztest/internal/domains/map_impl.h
@@ -30,15 +30,13 @@ namespace fuzztest::internal { template <typename Mapper, typename... Inner> -class MapImpl - : public DomainBase<MapImpl<Mapper, Inner...>, - std::decay_t<std::invoke_result_t< - Mapper, const typename Inner::value_type&...>>> { +class MapImpl : public DomainBase<MapImpl<Mapper, Inner...>, + std::decay_t<std::invoke_result_t< + Mapper, const value_type_t<Inner>&...>>, + std::tuple<corpus_type_t<Inner>...>> { public: - using corpus_type = std::tuple<corpus_type_t<Inner>...>; - using value_type = std::decay_t< - std::invoke_result_t<Mapper, const typename Inner::value_type&...>>; - static constexpr bool has_custom_corpus_type = true; + using typename MapImpl::DomainBase::corpus_type; + using typename MapImpl::DomainBase::value_type; MapImpl() = default; explicit MapImpl(Mapper mapper, Inner... inner)
diff --git a/fuzztest/internal/domains/one_of_impl.h b/fuzztest/internal/domains/one_of_impl.h index 30d3a63..973734e 100644 --- a/fuzztest/internal/domains/one_of_impl.h +++ b/fuzztest/internal/domains/one_of_impl.h
@@ -33,29 +33,27 @@ template <typename... Inner> class OneOfImpl - : public DomainBase<OneOfImpl<Inner...>, - typename std::tuple_element_t< - 0, typename std::tuple<Inner...>>::value_type> { + : public DomainBase< + OneOfImpl<Inner...>, + value_type_t<std::tuple_element_t<0, std::tuple<Inner...>>>, + std::variant<corpus_type_t<Inner>...>> { public: + using typename OneOfImpl::DomainBase::corpus_type; + using typename OneOfImpl::DomainBase::value_type; + // All value_types of inner domains must be the same. (Though note that they // can have different corpus_types!) - using value_type = - typename std::tuple_element_t<0, - typename std::tuple<Inner...>>::value_type; - static_assert(std::conjunction_v< - std::is_same<value_type, typename Inner::value_type>...>, - "All domains in a OneOf must have the same value_type."); - - static constexpr bool has_custom_corpus_type = true; - using corpus_type = std::variant<corpus_type_t<Inner>...>; + static_assert( + std::conjunction_v<std::is_same<value_type, value_type_t<Inner>>...>, + "All domains in a OneOf must have the same value_type."); explicit OneOfImpl(Inner... domains) : domains_(std::move(domains)...) {} corpus_type Init(absl::BitGenRef prng) { // TODO(b/191368509): Consider the cardinality of the subdomains to weight // them. - return Switch<sizeof...(Inner)>( - absl::Uniform(prng, size_t{}, num_domains_), [&](auto I) { + return Switch<kNumDomains>( + absl::Uniform(prng, size_t{}, kNumDomains), [&](auto I) { return corpus_type(std::in_place_index<I>, std::get<I>(domains_).Init(prng)); }); @@ -63,18 +61,18 @@ void Mutate(corpus_type& val, absl::BitGenRef prng, bool only_shrink) { // Switch to another domain 1% of the time when not reducing. - if (num_domains_ > 1 && !only_shrink && absl::Bernoulli(prng, 0.01)) { + if (kNumDomains > 1 && !only_shrink && absl::Bernoulli(prng, 0.01)) { // Choose a different index. - size_t offset = absl::Uniform<size_t>(prng, 1, num_domains_); + size_t offset = absl::Uniform<size_t>(prng, 1, kNumDomains); size_t index = static_cast<size_t>(val.index()); index += offset; - if (index >= num_domains_) index -= num_domains_; - Switch<sizeof...(Inner)>(index, [&](auto I) { + if (index >= kNumDomains) index -= kNumDomains; + Switch<kNumDomains>(index, [&](auto I) { auto& domain = std::get<I>(domains_); val.template emplace<I>(domain.Init(prng)); }); } else { - Switch<sizeof...(Inner)>(val.index(), [&](auto I) { + Switch<kNumDomains>(val.index(), [&](auto I) { auto& domain = std::get<I>(domains_); domain.Mutate(std::get<I>(val), prng, only_shrink); }); @@ -82,7 +80,7 @@ } value_type GetValue(const corpus_type& v) const { - return Switch<sizeof...(Inner)>(v.index(), [&](auto I) -> value_type { + return Switch<kNumDomains>(v.index(), [&](auto I) -> value_type { auto domain = std::get<I>(domains_); return domain.GetValue(std::get<I>(v)); }); @@ -98,7 +96,7 @@ return false; }; - ApplyIndex<sizeof...(Inner)>([&](auto... I) { + ApplyIndex<kNumDomains>([&](auto... I) { // Try them in order, break on first success. (try_one_corpus(I) || ...); }); @@ -117,11 +115,10 @@ } private: + static constexpr size_t kNumDomains = sizeof...(Inner); + static_assert(kNumDomains > 0, "OneOf requires a non-empty list."); + std::tuple<Inner...> domains_; - static_assert(std::tuple_size_v<decltype(domains_)> > 0, - "OneOf requires a non-empty list."); - // For ease of reading. - const size_t num_domains_ = sizeof...(Inner); }; } // namespace fuzztest::internal
diff --git a/fuzztest/internal/domains/optional_of_impl.h b/fuzztest/internal/domains/optional_of_impl.h index 23eaf95..8651f74 100644 --- a/fuzztest/internal/domains/optional_of_impl.h +++ b/fuzztest/internal/domains/optional_of_impl.h
@@ -34,15 +34,18 @@ enum class OptionalPolicy { kWithNull, kWithoutNull, kAlwaysNull }; template <typename T, typename InnerDomain> -class OptionalOfImpl : public DomainBase<OptionalOfImpl<T, InnerDomain>> { +class OptionalOfImpl + : public DomainBase< + OptionalOfImpl<T, InnerDomain>, T, + // `T` might be a custom optional type. + // We use std::variant unconditionally to make it simpler. + std::variant<std::monostate, corpus_type_t<InnerDomain>>> { public: - using value_type = T; + using typename OptionalOfImpl::DomainBase::corpus_type; + using typename OptionalOfImpl::DomainBase::value_type; + static_assert(Requires<T>([](auto x) -> decltype(!x, *x) {}), "T must be an optional type."); - static constexpr bool has_custom_corpus_type = true; - // `T` might be a custom optional type. - // We use std::variant unconditionally to make it simpler. - using corpus_type = std::variant<std::monostate, corpus_type_t<InnerDomain>>; explicit OptionalOfImpl(InnerDomain inner) : inner_(std::move(inner)), policy_(OptionalPolicy::kWithNull) {}
diff --git a/fuzztest/internal/domains/protobuf_domain_impl.h b/fuzztest/internal/domains/protobuf_domain_impl.h index 9bbf405..a245df7 100644 --- a/fuzztest/internal/domains/protobuf_domain_impl.h +++ b/fuzztest/internal/domains/protobuf_domain_impl.h
@@ -400,15 +400,15 @@ template <typename Message> class ProtobufDomainUntypedImpl : public DomainBase<ProtobufDomainUntypedImpl<Message>, - std::unique_ptr<Message>> { + std::unique_ptr<Message>, + absl::flat_hash_map<int, GenericDomainCorpusType>> { using Descriptor = ProtobufDescriptor<Message>; using FieldDescriptor = ProtobufFieldDescriptor<Message>; using OneofDescriptor = ProtobufOneofDescriptor<Message>; public: - using corpus_type = absl::flat_hash_map<int, GenericDomainCorpusType>; - using value_type = std::unique_ptr<Message>; - static constexpr bool has_custom_corpus_type = true; + using typename ProtobufDomainUntypedImpl::DomainBase::corpus_type; + using typename ProtobufDomainUntypedImpl::DomainBase::value_type; explicit ProtobufDomainUntypedImpl(PrototypePtr<Message> prototype) : prototype_(std::move(prototype)), @@ -747,7 +747,7 @@ template <typename T> bool VisitSingular(const FieldDescriptor* field) { auto& domain = self.GetSubDomain<T, false>(field); - typename std::decay_t<decltype(domain)>::value_type inner_value; + value_type_t<std::decay_t<decltype(domain)>> inner_value; auto* reflection = message.GetReflection(); if constexpr (std::is_same_v<T, ProtoMessageTag>) { const auto& child = reflection->GetMessage(message, field); @@ -768,7 +768,7 @@ template <typename T> bool VisitRepeated(const FieldDescriptor* field) { auto& domain = self.GetSubDomain<T, true>(field); - typename std::decay_t<decltype(domain)>::value_type inner_value; + value_type_t<std::decay_t<decltype(domain)>> inner_value; auto* reflection = message.GetReflection(); const int size = reflection->FieldSize(message, field); for (int i = 0; i < size; ++i) { @@ -1281,8 +1281,7 @@ auto GetDomainForField(const FieldDescriptor* field, bool use_policy = true) const { auto base_domain = GetBaseDomainForFieldType<T>(field, use_policy); - using field_cpptype = - typename std::decay_t<decltype(base_domain)>::value_type; + using field_cpptype = value_type_t<std::decay_t<decltype(base_domain)>>; if constexpr (is_repeated) { return Domain<std::vector<field_cpptype>>( GetOuterDomainForField<is_repeated>(field, base_domain, use_policy)); @@ -1416,15 +1415,14 @@ // Domain for `T` where `T` is a Protobuf message type. // It is a small wrapper around `ProtobufDomainUntypedImpl` to make its API more // convenient. -template <typename T> -class ProtobufDomainImpl : public DomainBase<ProtobufDomainImpl<T>> { - using Inner = ProtobufDomainUntypedImpl<typename T::Message>; - +template <typename T, + typename UntypedImpl = ProtobufDomainUntypedImpl<typename T::Message>> +class ProtobufDomainImpl + : public DomainBase<ProtobufDomainImpl<T>, T, corpus_type_t<UntypedImpl>> { public: - using value_type = T; - using corpus_type = typename Inner::corpus_type; + using typename ProtobufDomainImpl::DomainBase::corpus_type; + using typename ProtobufDomainImpl::DomainBase::value_type; using FieldDescriptor = ProtobufFieldDescriptor<typename T::Message>; - static constexpr bool has_custom_corpus_type = true; corpus_type Init(absl::BitGenRef prng) { return inner_.Init(prng); } @@ -1813,11 +1811,11 @@ Domain<std::unique_ptr<typename T::Message>> ToUntypedProtoDomain( Inner inner_domain) { return internal::MapImpl<std::function<std::unique_ptr<typename T::Message>( - typename Inner::value_type)>, + value_type_t<Inner>)>, Inner>( - [](typename Inner::value_type proto_message) + [](value_type_t<Inner> proto_message) -> std::unique_ptr<typename T::Message> { - return {std::make_unique<typename Inner::value_type>(proto_message)}; + return {std::make_unique<value_type_t<Inner>>(proto_message)}; }, std::move(inner_domain)); } @@ -1827,9 +1825,9 @@ ToOptionalUntypedProtoDomain(Inner inner_domain) { return internal::MapImpl< std::function<std::optional<std::unique_ptr<typename T::Message>>( - typename Inner::value_type)>, + value_type_t<Inner>)>, Inner>( - [](typename Inner::value_type proto_message) + [](value_type_t<Inner> proto_message) -> std::optional<std::unique_ptr<typename T::Message>> { if (!proto_message.has_value()) return std::nullopt; return {std::make_unique< @@ -1844,9 +1842,9 @@ ToRepeatedUntypedProtoDomain(Inner inner_domain) { return internal::MapImpl< std::function<std::vector<std::unique_ptr<typename T::Message>>( - typename Inner::value_type)>, + value_type_t<Inner>)>, Inner>( - [](typename Inner::value_type proto_message) + [](value_type_t<Inner> proto_message) -> std::vector<std::unique_ptr<typename T::Message>> { std::vector<std::unique_ptr<typename T::Message>> result; for (auto& entry : proto_message) { @@ -1859,7 +1857,7 @@ std::move(inner_domain)); } - Inner inner_{&T::default_instance()}; + UntypedImpl inner_{&T::default_instance()}; }; template <typename T> @@ -1870,7 +1868,7 @@ class ArbitraryImpl<T, std::enable_if_t<is_protocol_buffer_enum_v<T>>> : public DomainBase<ArbitraryImpl<T>> { public: - using value_type = T; + using typename ArbitraryImpl::DomainBase::value_type; value_type Init(absl::BitGenRef prng) { const int index = absl::Uniform(prng, 0, descriptor()->value_count());
diff --git a/fuzztest/internal/domains/smart_pointer_of_impl.h b/fuzztest/internal/domains/smart_pointer_of_impl.h index 0977025..61993e3 100644 --- a/fuzztest/internal/domains/smart_pointer_of_impl.h +++ b/fuzztest/internal/domains/smart_pointer_of_impl.h
@@ -28,20 +28,21 @@ namespace fuzztest::internal { -template <typename T, typename Inner> -class SmartPointerOfImpl : public DomainBase<SmartPointerOfImpl<T, Inner>> { - // We use the type erased version here to allow for recursion in smart pointer - // domains. - // It helps cut the recursion in type traits (like corpus_type) and the - // indirection avoids having the domain contain itself by value. - using RealInner = Domain<typename T::element_type>; +template <typename T, typename Inner, + // We use the type erased version here to allow for recursion in smart + // pointer domains. It helps cut the recursion in type traits (like + // corpus_type) and the indirection avoids having the domain contain + // itself by value. + typename RealInner = Domain<typename T::element_type>> +class SmartPointerOfImpl + : public DomainBase< + SmartPointerOfImpl<T, Inner>, T, + std::variant<std::monostate, corpus_type_t<RealInner>>> { using InnerFn = const RealInner& (*)(); public: - using value_type = T; - static constexpr bool has_custom_corpus_type = true; - using corpus_type = - std::variant<std::monostate, typename RealInner::corpus_type>; + using typename SmartPointerOfImpl::DomainBase::corpus_type; + using typename SmartPointerOfImpl::DomainBase::value_type; // Since we allow for recursion in this domain, we want to delay the // construction of the inner domain. Otherwise we would have an infinite
diff --git a/fuzztest/internal/domains/unique_elements_container_of_impl.h b/fuzztest/internal/domains/unique_elements_container_of_impl.h index 73b80f3..c4789f7 100644 --- a/fuzztest/internal/domains/unique_elements_container_of_impl.h +++ b/fuzztest/internal/domains/unique_elements_container_of_impl.h
@@ -26,6 +26,13 @@ namespace fuzztest::internal { +template <typename InnerDomain> +using UniqueDomainValueT = absl::flat_hash_set<value_type_t<InnerDomain>>; + +template <typename InnerDomain> +using UniqueDomain = + AssociativeContainerOfImpl<UniqueDomainValueT<InnerDomain>, InnerDomain>; + // UniqueElementsContainerImpl supports producing containers of type `T`, with // elements of type `E` from domain `InnerDomain inner`, with a guarantee that // each element of the container has a unique value from `InnerDomain`. The @@ -33,16 +40,14 @@ // which is (effectively) produced by `UnorderedSetOf(inner)`. template <typename T, typename InnerDomain> class UniqueElementsContainerImpl - : public DomainBase<UniqueElementsContainerImpl<T, InnerDomain>> { - using UniqueDomainValueT = - absl::flat_hash_set<typename InnerDomain::value_type>; - using UniqueDomain = - AssociativeContainerOfImpl<UniqueDomainValueT, InnerDomain>; + : public DomainBase<UniqueElementsContainerImpl<T, InnerDomain>, T, + corpus_type_t<UniqueDomain<InnerDomain>>> { + using UniqueDomainValueT = UniqueDomainValueT<InnerDomain>; + using UniqueDomain = UniqueDomain<InnerDomain>; public: - using value_type = T; - using corpus_type = typename UniqueDomain::corpus_type; - static constexpr bool has_custom_corpus_type = true; + using typename UniqueElementsContainerImpl::DomainBase::corpus_type; + using typename UniqueElementsContainerImpl::DomainBase::value_type; UniqueElementsContainerImpl() = default; explicit UniqueElementsContainerImpl(InnerDomain inner) @@ -64,7 +69,7 @@ std::optional<corpus_type> FromValue(const value_type& v) const { return unique_domain_.FromValue( - typename UniqueDomain::value_type(v.begin(), v.end())); + value_type_t<UniqueDomain>(v.begin(), v.end())); } auto GetPrinter() const { return unique_domain_.GetPrinter(); }
diff --git a/fuzztest/internal/domains/variant_of_impl.h b/fuzztest/internal/domains/variant_of_impl.h index 67a8640..c747f68 100644 --- a/fuzztest/internal/domains/variant_of_impl.h +++ b/fuzztest/internal/domains/variant_of_impl.h
@@ -32,13 +32,14 @@ namespace fuzztest::internal { template <typename T, typename... Inner> -class VariantOfImpl : public DomainBase<VariantOfImpl<T, Inner...>> { +class VariantOfImpl : public DomainBase<VariantOfImpl<T, Inner...>, T, + // `T` might be a custom variant type. + // We use std::variant unconditionally + // to make it simpler. + std::variant<corpus_type_t<Inner>...>> { public: - using value_type = T; - static constexpr bool has_custom_corpus_type = true; - // `T` might be a custom variant type. - // We use std::variant unconditionally to make it simpler. - using corpus_type = std::variant<corpus_type_t<Inner>...>; + using typename VariantOfImpl::DomainBase::corpus_type; + using typename VariantOfImpl::DomainBase::value_type; VariantOfImpl() = default; explicit VariantOfImpl(std::in_place_t, Inner... inner)
diff --git a/fuzztest/internal/fixture_driver.h b/fuzztest/internal/fixture_driver.h index 6bc904b..fc55add 100644 --- a/fuzztest/internal/fixture_driver.h +++ b/fuzztest/internal/fixture_driver.h
@@ -152,7 +152,7 @@ (fixture_.get()->*target_function_)( ForceVectorForStringView<Args>(std::move(args))...); }, - args_untyped.GetAs<typename DomainT::value_type>()); + args_untyped.GetAs<value_type_t<DomainT>>()); } protected: @@ -185,7 +185,7 @@ [&](auto&&... args) { target_function_(ForceVectorForStringView<Args>(std::move(args))...); }, - args_untyped.GetAs<typename DomainT::value_type>()); + args_untyped.GetAs<value_type_t<DomainT>>()); } private:
diff --git a/fuzztest/internal/registration.h b/fuzztest/internal/registration.h index 344d6b5..93c7168 100644 --- a/fuzztest/internal/registration.h +++ b/fuzztest/internal/registration.h
@@ -122,10 +122,9 @@ // void MyProperty(std::string s, int i) { ... } // FUZZ_TEST(MySuite, MyProperty).WithDomains(StringAndIndex(10)); template <typename... NewDomains> - auto WithDomains( - AggregateOfImpl<std::tuple<typename NewDomains::value_type...>, - RequireCustomCorpusType::kNo, NewDomains...> - domain) && { + auto WithDomains(AggregateOfImpl<std::tuple<value_type_t<NewDomains>...>, + RequireCustomCorpusType::kNo, NewDomains...> + domain) && { static_assert(!Registration::kHasDomain, "WithDomains can only be called once."); static_assert(!Registration::kHasSeeds, @@ -134,8 +133,7 @@ Base::kNumArgs == sizeof...(NewDomains), "Number of domains specified in .WithDomains() does not match " "the number of function parameters."); - using NewBase = - RegistrationWithDomainsBase<typename NewDomains::value_type...>; + using NewBase = RegistrationWithDomainsBase<value_type_t<NewDomains>...>; return Registration<Fixture, TargetFunction, NewBase>( test_info_, target_function_, NewBase{std::move(domain)}); }
diff --git a/fuzztest/internal/table_of_recent_compares.h b/fuzztest/internal/table_of_recent_compares.h index 659dc39..2ec622c 100644 --- a/fuzztest/internal/table_of_recent_compares.h +++ b/fuzztest/internal/table_of_recent_compares.h
@@ -28,6 +28,7 @@ #include "absl/container/flat_hash_set.h" #include "absl/random/bit_gen_ref.h" #include "absl/random/distributions.h" +#include "./fuzztest/internal/type_support.h" namespace fuzztest::internal { @@ -205,7 +206,7 @@ template <typename ContainerT> std::vector<DictionaryEntry<ContainerT>> GetMatchingContainerDictionaryEntries(const ContainerT& val) const { - using T = typename ContainerT::value_type; + using T = value_type_t<ContainerT>; static_assert( sizeof(T) == 1 || sizeof(T) == 2 || sizeof(T) == 4 || sizeof(T) == 8, "GetMatchingDictionaryEntries only accepts basic" @@ -236,7 +237,7 @@ GetMatchingContainerDictionaryEntry(const ContainerT& val, const uint8_t* buf1, const uint8_t* buf2, size_t buf_size) { - using T = typename ContainerT::value_type; + using T = value_type_t<ContainerT>; size_t val_size = val.size() * sizeof(T); static constexpr size_t kBufSizeValueMask = sizeof(T) - 1; @@ -273,7 +274,7 @@ static std::optional<DictionaryEntry<ContainerT>> GetRandomSide( absl::BitGenRef prng, const uint8_t* buf1, const uint8_t* buf2, size_t buf_size) { - using T = typename ContainerT::value_type; + using T = value_type_t<ContainerT>; static constexpr size_t kBufSizeValueMask = sizeof(T) - 1; if ((buf_size & kBufSizeValueMask) != 0 || buf_size == 0) { return std::nullopt; @@ -288,7 +289,7 @@ } private: - template <typename ContainerT, typename T = typename ContainerT::value_type> + template <typename ContainerT, typename T = value_type_t<ContainerT>> static ContainerT MakeContainer(const uint8_t* buf, size_t buf_size) { ContainerT result = {}; std::copy(reinterpret_cast<const T*>(buf), @@ -385,11 +386,11 @@ template <typename ContainerT> class ContainerDictionary { - static_assert(std::is_integral_v<typename ContainerT::value_type> && - (sizeof(typename ContainerT::value_type) == 1 || - sizeof(typename ContainerT::value_type) == 2 || - sizeof(typename ContainerT::value_type) == 4 || - sizeof(typename ContainerT::value_type) == 8), + static_assert(std::is_integral_v<value_type_t<ContainerT>> && + (sizeof(value_type_t<ContainerT>) == 1 || + sizeof(value_type_t<ContainerT>) == 2 || + sizeof(value_type_t<ContainerT>) == 4 || + sizeof(value_type_t<ContainerT>) == 8), "ContainerDictionary only accepts container::value_type being " "basic types with size = " "{1, 2, 4, 8}."); @@ -415,7 +416,7 @@ static std::optional<DictionaryEntry<ContainerT>> GetRandomTORCEntry( const ContainerT& val, absl::BitGenRef prng, const TablesOfRecentCompares& torc) { - using T = typename ContainerT::value_type; + using T = value_type_t<ContainerT>; std::optional<DictionaryEntry<ContainerT>> result = std::nullopt; // Get from mem_cmp_table or i*_cmp_table with 50/50 probability. if (RandomBool(prng)) { @@ -492,7 +493,7 @@ // `GetMatchingContainerDictionaryEntry` to find matches in `val`. void AddMatchingIntegerDictionaryEntriesFromTORC( const ContainerT& val, const TablesOfRecentCompares& torc) { - using T = typename ContainerT::value_type; + using T = value_type_t<ContainerT>; if constexpr (sizeof(T) <= 4) { if (val.size() >= 4) { for (auto& i : torc.Get<4>().GetTable()) {
diff --git a/fuzztest/internal/type_support.h b/fuzztest/internal/type_support.h index 4ecdce6..d859b11 100644 --- a/fuzztest/internal/type_support.h +++ b/fuzztest/internal/type_support.h
@@ -37,22 +37,11 @@ namespace fuzztest::internal { -template <typename Domain, typename = void> -struct DomainTraitsImpl { - using corpus_type = typename Domain::value_type; -}; +template <typename Domain> +using value_type_t = typename Domain::value_type; template <typename Domain> -struct DomainTraitsImpl< - Domain, std::enable_if_t<Domain::has_custom_corpus_type, - std::void_t<typename Domain::corpus_type>>> { - using corpus_type = typename Domain::corpus_type; -}; - -// If `has_custom_corpus_type_v<Domain>`, `Domain::corpus_type`. -// Otherwise `Domain::value_type`. -template <typename Domain> -using corpus_type_t = typename DomainTraitsImpl<Domain>::corpus_type; +using corpus_type_t = typename Domain::corpus_type; // Return a best effort printer for type `T`. // This is useful for cases where the domain can't figure out how to print the @@ -249,7 +238,7 @@ void PrintCorpusValue(const corpus_type_t<Domain>& v, RawSink out, PrintMode mode) const { - using value_type = typename Domain::value_type; + using value_type = value_type_t<Domain>; constexpr bool is_pointer = Requires<value_type>( [](auto probe) -> std::enable_if_t<std::is_pointer_v<decltype(probe.get())>> {});
diff --git a/fuzztest/internal/type_support_test.cc b/fuzztest/internal/type_support_test.cc index edb1ef2..2a08ef5 100644 --- a/fuzztest/internal/type_support_test.cc +++ b/fuzztest/internal/type_support_test.cc
@@ -264,9 +264,9 @@ auto color_domain = ElementOf({kBlue}); auto print = [&](auto v, auto domain) { // We have to create the inner corpus_type of Domain here. - return TestPrintValue(typename decltype(domain)::corpus_type( - std::in_place_type<decltype(v)>, v), - domain); + return TestPrintValue( + corpus_type_t<decltype(domain)>(std::in_place_type<decltype(v)>, v), + domain); }; EXPECT_THAT(print('a', Domain<char>(Arbitrary<char>())), ElementsAre("'a' (97)", "'a'"));