From e8b2c23d2406be8d47f17040534ac70b5e64491e Mon Sep 17 00:00:00 2001 From: Jamieson Pryor Date: Wed, 23 Oct 2024 16:18:06 -0700 Subject: [PATCH] Fixing ambiguous equality operators for C++20 migration. This is an issue specifically on CRTP classes templated equality operators that is resolved by making equality operators free standing functions. e.g. ``` implementation/local_object_test.cc:194:22: error: use of overloaded operator '==' is ambiguous (with operand types 'LocalObject' and 'LocalObject') EXPECT_FALSE(val_1 == val_2); ~~~~~ ^ ~~~~~ ``` See https://github.com/google/jni-bind/issues/346. Sample failure https://github.com/google/jni-bind/actions/runs/11474529819/job/31930608615?pr=345. PiperOrigin-RevId: 689141602 --- implementation/BUILD | 14 ++--- implementation/array.h | 82 +++++++++++++++++++-------- implementation/array_test.cc | 3 +- implementation/array_view.h | 21 +++++-- implementation/class.h | 56 +++++++++++------- implementation/class_loader.h | 45 ++++++++++----- implementation/constructor.h | 14 +++-- implementation/default_class_loader.h | 48 +++++++++------- implementation/field_ref.h | 2 + implementation/forward_declarations.h | 9 ++- implementation/global_class_loader.h | 1 + implementation/global_object.h | 3 + implementation/global_object_test.cc | 19 +++++++ implementation/jvm.h | 25 ++++---- implementation/local_array.h | 30 +++++----- implementation/local_class_loader.h | 2 + implementation/local_object.h | 13 +---- implementation/local_object_test.cc | 28 ++++----- implementation/no_class_specified.h | 12 +++- implementation/promotion_mechanics.h | 56 ++++++++++++------ implementation/ref_base.h | 2 +- 21 files changed, 311 insertions(+), 174 deletions(-) diff --git a/implementation/BUILD b/implementation/BUILD index e570ee72..5044af27 100644 --- a/implementation/BUILD +++ b/implementation/BUILD @@ -83,6 +83,7 @@ cc_library( deps = [ ":array_type_conversion", "//:jni_dep", + "//implementation/jni_helper:get_array_element_result", "//implementation/jni_helper:jni_array_helper", "//implementation/jni_helper:lifecycle", ], @@ -296,23 +297,19 @@ cc_library( name = "field_ref", hdrs = ["field_ref.h"], deps = [ - ":class_ref", + ":default_class_loader", ":field_selection", ":id", ":id_type", ":no_idx", ":promotion_mechanics_tags", - ":proxy", ":proxy_convenience_aliases", ":ref_base", ":signature", "//:jni_dep", "//implementation/jni_helper", "//implementation/jni_helper:field_value_getter", - "//implementation/jni_helper:static_field_value", "//metaprogramming:double_locked_value", - "//metaprogramming:optional_wrap", - "//metaprogramming:queryable_map", ], ) @@ -365,10 +362,8 @@ cc_library( name = "forward_declarations", hdrs = ["forward_declarations.h"], deps = [ - ":default_class_loader", ":id_type", - ":jvm", - "//implementation/jni_helper:lifecycle_object", + "//implementation/jni_helper:lifecycle", ], ) @@ -397,8 +392,10 @@ cc_library( name = "global_object", hdrs = ["global_object.h"], deps = [ + ":default_class_loader", ":forward_declarations", ":jni_type", + ":jvm", ":local_object", ":promotion_mechanics", ":ref_base", @@ -724,6 +721,7 @@ cc_library( ":forward_declarations", ":jvm", ":promotion_mechanics_tags", + "//:jni_dep", "//implementation/jni_helper:lifecycle", ], ) diff --git a/implementation/array.h b/implementation/array.h index a1b925ed..32b48f91 100644 --- a/implementation/array.h +++ b/implementation/array.h @@ -34,11 +34,13 @@ struct Array; template struct Rank {}; +struct ArrayBase {}; + //////////////////////////////////////////////////////////////////////////////// // Array Non-Object Implementation. //////////////////////////////////////////////////////////////////////////////// template -struct ArrayNonObjectTypeImpl { +struct ArrayNonObjectTypeImpl : ArrayBase { RawType raw_; constexpr ArrayNonObjectTypeImpl(RawType raw) : raw_(raw) {} @@ -52,21 +54,48 @@ struct ArrayNonObjectTypeImpl { "JNI Error: Invalid array declaration, use Array { type{}, " "Rank{} }."); } +}; - template - constexpr bool operator==(const Array& rhs) const { - if constexpr (std::is_same_v) { - return (raw_ == rhs.raw_); - } - return false; - } +template +struct ArrayComparisonHelper {}; - template - constexpr bool operator!=(const Array& rhs) const { - return !(*this == rhs); - } +template +struct ArrayComparisonHelper>> { + using type = RawType; +}; + +template +struct ArrayComparisonHelper> { + using type = RawType; +}; + +template +using ArrayComparisonHelper_t = typename ArrayComparisonHelper::type; + +template +static constexpr bool IsArrayComparable() { + return std::is_base_of_v && std::is_base_of_v; }; +//////////////////////////////////////////////////////////////////////////////// +// Equality operators. +//////////////////////////////////////////////////////////////////////////////// +template +constexpr std::enable_if_t(), bool> operator==( + const T1& lhs, const T2& rhs) { + if constexpr (std::is_same_v, + ArrayComparisonHelper_t>) { + return (lhs.raw_ == rhs.raw_); + } + return false; +} + +template +constexpr std::enable_if_t(), bool> operator!=( + const T1& lhs, const T2& rhs) { + return !(lhs == rhs); +} + // Primitive array implementaiton. template struct ArrayImpl : public ArrayNonObjectTypeImpl, @@ -79,27 +108,32 @@ struct ArrayImpl : public ArrayNonObjectTypeImpl, // Array Object Implementation. //////////////////////////////////////////////////////////////////////////////// template -struct ArrayImpl : public ArrayTag { +struct ArrayImpl : public ArrayTag, + ArrayBase { RawType raw_; constexpr ArrayImpl(RawType raw) : raw_(raw) {} template constexpr ArrayImpl(RawType raw, Rank) : raw_(raw) {} +}; - template - constexpr bool operator==(const Array& rhs) const { - if constexpr (std::is_same_v) { - return (raw_ == rhs.raw_); - } - return false; +template +constexpr bool operator==(const ArrayImpl& lhs, + const ArrayImpl& rhs) { + if constexpr (std::is_same_v) { + return (lhs.raw_ == rhs.raw_); } + return false; +} - template - constexpr bool operator!=(const Array& rhs) const { - return !(*this == rhs); - } -}; +template +constexpr bool operator!=(const ArrayImpl& lhs, + const Array& rhs) { + return !(lhs == rhs); +} // This type correlates to those used in declarations, // e.g. Field { Array { Array { jint {} } } }. diff --git a/implementation/array_test.cc b/implementation/array_test.cc index 14833456..cc9858a7 100644 --- a/implementation/array_test.cc +++ b/implementation/array_test.cc @@ -14,6 +14,8 @@ * limitations under the License. */ +#include + #include #include #include "jni_bind.h" @@ -108,5 +110,4 @@ static_assert( static_assert(FullArrayStripV(arr8) == kClass); static_assert(FullArrayStripV(arr9) == kClass); - } // namespace diff --git a/implementation/array_view.h b/implementation/array_view.h index ea56dc8d..4e7a0df0 100644 --- a/implementation/array_view.h +++ b/implementation/array_view.h @@ -21,16 +21,21 @@ #include #include +#include #include "implementation/array_type_conversion.h" +#include "implementation/jni_helper/get_array_element_result.h" #include "implementation/jni_helper/jni_array_helper.h" #include "implementation/jni_helper/lifecycle.h" #include "jni_dep.h" namespace jni { +struct ArrayViewHelperBase {}; + template -struct ArrayViewHelper { +struct ArrayViewHelper : ArrayViewHelperBase { + using SpanType = T; const T val_; operator T() const { return val_; } @@ -38,9 +43,11 @@ struct ArrayViewHelper { }; // Primitive Rank 1 Arrays. -template +template class ArrayView { public: + using SpanType = SpanType_; + struct Iterator { using iterator_category = std::random_access_iterator_tag; using difference_type = std::size_t; @@ -115,12 +122,14 @@ class ArrayView { }; // Object arrays, or arrays with rank > 1 (which are object arrays), or strings. -template +template class ArrayView< - SpanType, kRank, - std::enable_if_t<(kRank > 1) || std::is_same_v || - std::is_same_v>> { + SpanType_, kRank, + std::enable_if_t<(kRank > 1) || std::is_same_v || + std::is_same_v>> { public: + using SpanType = SpanType_; + // Metafunction that returns the type after a single dereference. template struct PinHelper { diff --git a/implementation/class.h b/implementation/class.h index e72e8bbf..81dee205 100644 --- a/implementation/class.h +++ b/implementation/class.h @@ -156,27 +156,45 @@ struct Class, static_(statik), methods_(methods...), fields_(fields...) {} - - //////////////////////////////////////////////////////////////////////////////// - // Equality operators. - //////////////////////////////////////////////////////////////////////////////// - template - constexpr bool operator==( - const Class, - std::tuple, - std::tuple>>, - std::tuple, std::tuple>& rhs) const { - // Don't compare the other parameters so classes can be used as parameters - // or return values before the class itself is defined. - return std::string_view(name_) == std::string_view(rhs.name_); - } - - constexpr bool operator==(const NoClass&) const { return false; } - constexpr bool operator!=(const NoClass&) const { return false; } }; +//////////////////////////////////////////////////////////////////////////////// +// Equality operators. +//////////////////////////////////////////////////////////////////////////////// +template +constexpr bool operator==( + const Class, + std::tuple, + std::tuple>>, + std::tuple, std::tuple>& lhs, + const Class, + std::tuple, + std::tuple>>, + std::tuple, std::tuple>& rhs) { + // Don't compare the other parameters so classes can be used as parameters + // or return values before the class itself is defined. + return std::string_view(lhs.name_) == std::string_view(rhs.name_); +} + +template +constexpr bool operator==(const Class& lhs, const NoClass&) { + return false; +} + +template +constexpr bool operator!=(const Class& lhs, const NoClass&) { + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// CTAD. +//////////////////////////////////////////////////////////////////////////////// template Class(const char*, Params...) -> Class #include #include #include @@ -68,20 +69,6 @@ class ClassLoader : public Object { parent_loader_(parent_loader), supported_classes_(supported_class_set.supported_classes_) {} - bool constexpr operator==( - const ClassLoader& rhs) const { - return (*this).parent_loader_ == rhs.parent_loader_ && - (*this).supported_classes_ == rhs.supported_classes_; - } - template - bool constexpr operator==(const T& rhs) const { - return false; - } - template - bool constexpr operator!=(const T& rhs) const { - return !(*this == rhs); - } - template constexpr std::size_t IdxOfClassHelper( std::integer_sequence) const { @@ -131,6 +118,33 @@ class ClassLoader : public Object { } }; +//////////////////////////////////////////////////////////////////////////////// +// Equality operators. +//////////////////////////////////////////////////////////////////////////////// +template +bool constexpr operator==( + const ClassLoader& lhs, + const ClassLoader& rhs) { + return lhs.parent_loader_ == rhs.parent_loader_ && + lhs.supported_classes_ == rhs.supported_classes_; +} + +template +bool constexpr operator==( + const ClassLoader& lhs, const T& rhs) { + return false; +} + +template +bool constexpr operator!=( + const ClassLoader& lhs, const T& rhs) { + return !(lhs == rhs); +} + +//////////////////////////////////////////////////////////////////////////////// +// CTAD. +//////////////////////////////////////////////////////////////////////////////// + // Note: Null is chosen, not default, because LoadedBy requires a syntax like // LoadedBy{ClassLoader{"kClass"}} (using the CTAD loader type below), but // we want to prevent explicit usage of a default loader (as it makes no sense). @@ -146,6 +160,9 @@ template ClassLoader(SupportedClassSet) -> ClassLoader; +//////////////////////////////////////////////////////////////////////////////// +// Ancestral lookups. +//////////////////////////////////////////////////////////////////////////////// template constexpr auto& GetAncestor(const T& loader) { if constexpr (I == 0) { diff --git a/implementation/constructor.h b/implementation/constructor.h index d3f8546e..aa1c6b57 100644 --- a/implementation/constructor.h +++ b/implementation/constructor.h @@ -19,10 +19,12 @@ // IWYU pragma: private, include "third_party/jni_wrapper/jni_bind.h" -#include "method.h" -#include "params.h" -#include "return.h" +#include +#include +#include + #include "implementation/jni_helper/jni_helper.h" +#include "implementation/params.h" namespace jni { @@ -52,15 +54,15 @@ class Constructor : public ConstructorBase { constexpr explicit Constructor(Args...) {} }; -template -Constructor(ParamsRaw...) -> Constructor; - template constexpr bool operator==(const Constructor& lhs, const Constructor& rhs) { return lhs.params_ == rhs.params_; } +template +Constructor(ParamsRaw...) -> Constructor; + //============================================================================== // Represents a constructor used at runtime and has index data about where it // exists in the static class definition which is embedded on the caller's diff --git a/implementation/default_class_loader.h b/implementation/default_class_loader.h index 1e4f8944..b939be59 100644 --- a/implementation/default_class_loader.h +++ b/implementation/default_class_loader.h @@ -62,18 +62,21 @@ class DefaultClassLoader { constexpr std::size_t IdxOfAncestor(std::size_t cur_idx = 0) const { return kClassNotInLoaderSetIdx; } +}; - template - bool constexpr operator==(const T& rhs) const { - return false; - } - bool constexpr operator==(const DefaultClassLoader&) const { return true; } +template +bool constexpr operator==(const DefaultClassLoader& lhs, const T& rhs) { + return false; +} +bool constexpr operator==(const DefaultClassLoader& lhs, + const DefaultClassLoader& rhs) { + return true; +} - template - bool constexpr operator!=(const T& rhs) const { - return !(*this == rhs); - } -}; +template +bool constexpr operator!=(const DefaultClassLoader& lhs, const T& rhs) { + return !(lhs == rhs); +} // Class loader that cannot supply any classes. This should be the root loader // for most user defined classes. @@ -95,19 +98,22 @@ class NullClassLoader { constexpr std::size_t IdxOfAncestor(std::size_t cur_idx = 0) const { return kClassNotInLoaderSetIdx; } - - template - bool constexpr operator==(const T& rhs) const { - return false; - } - bool constexpr operator==(const NullClassLoader&) const { return true; } - - template - bool constexpr operator!=(const T& rhs) const { - return !(*this == rhs); - } }; +template +constexpr bool operator==(const NullClassLoader& lhs, const T& rhs) { + return false; +} +constexpr bool operator==(const NullClassLoader& lhs, + const NullClassLoader& rhs) { + return true; +} + +template +constexpr bool operator!=(const NullClassLoader& lhs, const T& rhs) { + return !(lhs == rhs); +} + static constexpr NullClassLoader kNullClassLoader; static constexpr DefaultClassLoader kDefaultClassLoader; diff --git a/implementation/field_ref.h b/implementation/field_ref.h index a364582b..e000dbf0 100644 --- a/implementation/field_ref.h +++ b/implementation/field_ref.h @@ -19,10 +19,12 @@ // IWYU pragma: private, include "third_party/jni_wrapper/jni_bind.h" +#include #include #include #include +#include "implementation/default_class_loader.h" #include "implementation/field_selection.h" #include "implementation/id.h" #include "implementation/id_type.h" diff --git a/implementation/forward_declarations.h b/implementation/forward_declarations.h index f3c475df..6aa11935 100644 --- a/implementation/forward_declarations.h +++ b/implementation/forward_declarations.h @@ -19,10 +19,10 @@ // IWYU pragma: private, include "third_party/jni_wrapper/jni_bind.h" -#include "implementation/default_class_loader.h" +#include + #include "implementation/id_type.h" -#include "implementation/jni_helper/lifecycle_object.h" -#include "implementation/jvm.h" +#include "implementation/jni_helper/lifecycle.h" namespace jni { @@ -37,6 +37,9 @@ namespace jni { // Provide this base tag to UserDefined to enable custom types. struct JniUserDefinedCorpusTag {}; +// ArrayViewHelperBase (shared by all `ArrayView`). +struct ArrayViewHelperBase; + // ArrayView Helper. template struct ArrayViewHelper; diff --git a/implementation/global_class_loader.h b/implementation/global_class_loader.h index 7b221671..cd08d62c 100644 --- a/implementation/global_class_loader.h +++ b/implementation/global_class_loader.h @@ -37,6 +37,7 @@ class GlobalClassLoader public: using Base = ClassLoaderRef; using Base::Base; + using SpanType = jobject; template GlobalClassLoader(GlobalClassLoader&& rhs) diff --git a/implementation/global_object.h b/implementation/global_object.h index 8cefa543..88056ad9 100644 --- a/implementation/global_object.h +++ b/implementation/global_object.h @@ -19,10 +19,12 @@ // IWYU pragma: private, include "third_party/jni_wrapper/jni_bind.h" +#include "implementation/default_class_loader.h" #include "implementation/forward_declarations.h" #include "implementation/jni_helper/lifecycle.h" #include "implementation/jni_helper/lifecycle_object.h" #include "implementation/jni_type.h" +#include "implementation/jvm.h" #include "implementation/local_object.h" #include "implementation/promotion_mechanics.h" #include "implementation/ref_base.h" @@ -44,6 +46,7 @@ class GlobalObject public: using Base = GlobalObjectImpl; using Base::Base; + using SpanType = jobject; using LifecycleT = LifecycleHelper; template diff --git a/implementation/global_object_test.cc b/implementation/global_object_test.cc index 69387eda..7e19f79d 100644 --- a/implementation/global_object_test.cc +++ b/implementation/global_object_test.cc @@ -79,6 +79,25 @@ TEST_F(JniTest, GlobalObject_ConstructsFromNonStandardConstructor) { EXPECT_EQ(jobject{global_object}, AsGlobal(Fake())); } +TEST_F(JniTest, GlobalObject_ComparesAgainstOtherGlobalObjects) { + static constexpr Class kClass{ + "kClass", + Constructor{jfloat{}, jfloat{}}, + }; + static constexpr Class kClass2{ + "kClass2", + }; + GlobalObject val_1{AdoptGlobal{}, Fake(1)}; + GlobalObject val_2{AdoptGlobal{}, Fake(2)}; + + EXPECT_TRUE(val_1 == val_1); + EXPECT_FALSE(val_1 == val_2); + EXPECT_TRUE(val_1 != val_2); + EXPECT_TRUE(val_2 == val_2); + EXPECT_TRUE(val_2 != val_1); + EXPECT_FALSE(val_1 == val_2); +} + TEST_F(JniTest, GlobalObject_DoesNotDeleteAnyLocalsForAdoptedGlobalJobject) { static constexpr Class kClass{"kClass"}; diff --git a/implementation/jvm.h b/implementation/jvm.h index a98c4304..52e9c2fb 100644 --- a/implementation/jvm.h +++ b/implementation/jvm.h @@ -50,18 +50,23 @@ class Jvm { return IdxOfClassLoaderHelper( std::make_integer_sequence()); } +}; - template - bool constexpr operator==(const T& rhs) const { - return false; - } - bool constexpr operator==(const Jvm&) const { return true; } +template +constexpr bool operator==(const Jvm& lhs, const T& rhs) { + return false; +} - template - bool constexpr operator!=(const T& rhs) const { - return !(*this == rhs); - } -}; +template +constexpr bool operator==(const Jvm& lhs, + const Jvm& rhs) { + return true; +} + +template +bool constexpr operator!=(const Jvm& lhs, const T& rhs) { + return !(lhs == rhs); +} template Jvm(ClassLoaderTs...) -> Jvm; diff --git a/implementation/local_array.h b/implementation/local_array.h index 40745b39..5a291bfa 100644 --- a/implementation/local_array.h +++ b/implementation/local_array.h @@ -41,16 +41,17 @@ namespace jni { // Currently GlobalArrays do not exist, as reasoning about the lifecycles of the // underlying objects is non-trivial, e.g. a GlobalArray taking a local object // would result in a possibly unexpected extension of lifetime. -template class LocalArray : public ArrayRef< - JniT> { + JniT> { public: static constexpr Class kClass{class_v_.name_}; static constexpr std::size_t kRank = kRank_; + using SpanType = SpanType_; using RawValT = std::conditional_t, std::decay_t, SpanType>; @@ -80,11 +81,12 @@ class LocalArray : Base(AdoptLocal{}, rhs.Release()) {} // Rvalue ctor. - template - LocalArray(LocalArray&& rhs) + LocalArray( + LocalArray&& rhs) : Base(AdoptLocal{}, rhs.Release()) { - static_assert(std::is_same_v && kRank == kRank_ && + static_assert(std::is_same_v && kRank == kRank_ && class_v == class_v_ && class_loader_v == class_loader_v_); } @@ -93,9 +95,9 @@ class LocalArray // e.g. // LocalArray arr { 5, LocalObject {args...} }; // LocalArray arr { 5, GlobalObject {args...} }; - template