From a1e3ef919c14424e047f5e0e4d2569b4e4ca33d9 Mon Sep 17 00:00:00 2001 From: George Marques Date: Fri, 10 Mar 2023 15:58:55 -0300 Subject: [PATCH 1/2] Add operator NOT for all Variant types --- core/variant/variant_op.cpp | 33 +++++++++++++++++++++++++++++++ core/variant/variant_op.h | 6 +++--- tests/core/variant/test_variant.h | 16 +++++++++++++++ 3 files changed, 52 insertions(+), 3 deletions(-) diff --git a/core/variant/variant_op.cpp b/core/variant/variant_op.cpp index 33c285dc6d0..aed83ac010d 100644 --- a/core/variant/variant_op.cpp +++ b/core/variant/variant_op.cpp @@ -900,6 +900,39 @@ void Variant::_register_variant_operators() { register_op(Variant::OP_NOT, Variant::INT, Variant::NIL); register_op(Variant::OP_NOT, Variant::FLOAT, Variant::NIL); register_op(Variant::OP_NOT, Variant::OBJECT, Variant::NIL); + register_op>(Variant::OP_NOT, Variant::STRING, Variant::NIL); + register_op>(Variant::OP_NOT, Variant::VECTOR2, Variant::NIL); + register_op>(Variant::OP_NOT, Variant::VECTOR2I, Variant::NIL); + register_op>(Variant::OP_NOT, Variant::RECT2, Variant::NIL); + register_op>(Variant::OP_NOT, Variant::RECT2I, Variant::NIL); + register_op>(Variant::OP_NOT, Variant::VECTOR3, Variant::NIL); + register_op>(Variant::OP_NOT, Variant::VECTOR3I, Variant::NIL); + register_op>(Variant::OP_NOT, Variant::TRANSFORM2D, Variant::NIL); + register_op>(Variant::OP_NOT, Variant::VECTOR4, Variant::NIL); + register_op>(Variant::OP_NOT, Variant::VECTOR4I, Variant::NIL); + register_op>(Variant::OP_NOT, Variant::PLANE, Variant::NIL); + register_op>(Variant::OP_NOT, Variant::QUATERNION, Variant::NIL); + register_op>(Variant::OP_NOT, Variant::AABB, Variant::NIL); + register_op>(Variant::OP_NOT, Variant::BASIS, Variant::NIL); + register_op>(Variant::OP_NOT, Variant::TRANSFORM3D, Variant::NIL); + register_op>(Variant::OP_NOT, Variant::PROJECTION, Variant::NIL); + register_op>(Variant::OP_NOT, Variant::COLOR, Variant::NIL); + register_op>(Variant::OP_NOT, Variant::STRING_NAME, Variant::NIL); + register_op>(Variant::OP_NOT, Variant::NODE_PATH, Variant::NIL); + register_op>(Variant::OP_NOT, Variant::RID, Variant::NIL); + register_op>(Variant::OP_NOT, Variant::CALLABLE, Variant::NIL); + register_op>(Variant::OP_NOT, Variant::SIGNAL, Variant::NIL); + register_op>(Variant::OP_NOT, Variant::DICTIONARY, Variant::NIL); + register_op>(Variant::OP_NOT, Variant::ARRAY, Variant::NIL); + register_op>(Variant::OP_NOT, Variant::PACKED_BYTE_ARRAY, Variant::NIL); + register_op>(Variant::OP_NOT, Variant::PACKED_INT32_ARRAY, Variant::NIL); + register_op>(Variant::OP_NOT, Variant::PACKED_INT64_ARRAY, Variant::NIL); + register_op>(Variant::OP_NOT, Variant::PACKED_FLOAT32_ARRAY, Variant::NIL); + register_op>(Variant::OP_NOT, Variant::PACKED_FLOAT64_ARRAY, Variant::NIL); + register_op>(Variant::OP_NOT, Variant::PACKED_STRING_ARRAY, Variant::NIL); + register_op>(Variant::OP_NOT, Variant::PACKED_VECTOR2_ARRAY, Variant::NIL); + register_op>(Variant::OP_NOT, Variant::PACKED_VECTOR3_ARRAY, Variant::NIL); + register_op>(Variant::OP_NOT, Variant::PACKED_COLOR_ARRAY, Variant::NIL); register_string_op(OperatorEvaluatorInStringFind, Variant::OP_IN); diff --git a/core/variant/variant_op.h b/core/variant/variant_op.h index d2163cf92dd..5db4d638067 100644 --- a/core/variant/variant_op.h +++ b/core/variant/variant_op.h @@ -805,14 +805,14 @@ class OperatorEvaluatorNot { public: static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { const A &a = *VariantGetInternalPtr::get_ptr(&p_left); - *r_ret = !a; + *r_ret = a == A(); r_valid = true; } static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { - *VariantGetInternalPtr::get_ptr(r_ret) = !*VariantGetInternalPtr::get_ptr(left); + *VariantGetInternalPtr::get_ptr(r_ret) = *VariantGetInternalPtr::get_ptr(left) == A(); } static void ptr_evaluate(const void *left, const void *right, void *r_ret) { - PtrToArg::encode(!PtrToArg::convert(left)); + PtrToArg::encode(PtrToArg::convert(left) == A(), r_ret); } static Variant::Type get_return_type() { return Variant::BOOL; } }; diff --git a/tests/core/variant/test_variant.h b/tests/core/variant/test_variant.h index 024fcf53c43..66b58620d28 100644 --- a/tests/core/variant/test_variant.h +++ b/tests/core/variant/test_variant.h @@ -1171,6 +1171,22 @@ TEST_CASE("[Variant] Utility functions") { } } +TEST_CASE("[Variant] Operator NOT") { + // Verify that operator NOT works for all types and is consistent with booleanize(). + for (int i = 0; i < Variant::VARIANT_MAX; i++) { + Variant value; + Callable::CallError err; + Variant::construct((Variant::Type)i, value, nullptr, 0, err); + + REQUIRE_EQ(err.error, Callable::CallError::CALL_OK); + + Variant result = Variant::evaluate(Variant::OP_NOT, value, Variant()); + + REQUIRE_EQ(result.get_type(), Variant::BOOL); + CHECK_EQ(!value.booleanize(), result.operator bool()); + } +} + } // namespace TestVariant #endif // TEST_VARIANT_H From d76b3f2a4c7f2e59a0e0592cac8877f382070f14 Mon Sep 17 00:00:00 2001 From: George Marques Date: Fri, 10 Mar 2023 16:01:17 -0300 Subject: [PATCH 2/2] GDScript: Allow boolean operators between any types To make consistent with previous behavior. Mostly to be used in conditions for `if` and `while`. --- modules/gdscript/gdscript_analyzer.cpp | 11 + .../boolean_operators_for_all_types.gd | 347 ++++++++++++++++++ .../boolean_operators_for_all_types.out | 229 ++++++++++++ 3 files changed, 587 insertions(+) create mode 100644 modules/gdscript/tests/scripts/analyzer/features/boolean_operators_for_all_types.gd create mode 100644 modules/gdscript/tests/scripts/analyzer/features/boolean_operators_for_all_types.out diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index 38d5ae6b77c..831f9a3efd0 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -4846,6 +4846,17 @@ GDScriptParser::DataType GDScriptAnalyzer::get_operation_type(Variant::Operator } GDScriptParser::DataType GDScriptAnalyzer::get_operation_type(Variant::Operator p_operation, const GDScriptParser::DataType &p_a, const GDScriptParser::DataType &p_b, bool &r_valid, const GDScriptParser::Node *p_source) { + if (p_operation == Variant::OP_AND || p_operation == Variant::OP_OR) { + // Those work for any type of argument and always return a boolean. + // They don't use the Variant operator since they have short-circuit semantics. + r_valid = true; + GDScriptParser::DataType result; + result.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED; + result.kind = GDScriptParser::DataType::BUILTIN; + result.builtin_type = Variant::BOOL; + return result; + } + Variant::Type a_type = p_a.builtin_type; Variant::Type b_type = p_b.builtin_type; diff --git a/modules/gdscript/tests/scripts/analyzer/features/boolean_operators_for_all_types.gd b/modules/gdscript/tests/scripts/analyzer/features/boolean_operators_for_all_types.gd new file mode 100644 index 00000000000..73d0f9096c6 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/boolean_operators_for_all_types.gd @@ -0,0 +1,347 @@ +extends Resource + +signal foo + +func test(): + var x + # TYPE_NIL + x = null + prints("TYPE_NIL") + prints(not x) + prints(x and false) + prints(x and true) + prints(x or false) + prints(x or true) + + # TYPE_BOOL + x = true + prints("TYPE_BOOL") + prints(not x) + prints(x and false) + prints(x and true) + prints(x or false) + prints(x or true) + + # TYPE_INT + x = 1 + prints("TYPE_INT") + prints(not x) + prints(x and false) + prints(x and true) + prints(x or false) + prints(x or true) + + # TYPE_FLOAT + x = 1.1 + prints("TYPE_FLOAT") + prints(not x) + prints(x and false) + prints(x and true) + prints(x or false) + prints(x or true) + + # TYPE_STRING + x = "foo" + prints("TYPE_STRING") + prints(not x) + prints(x and false) + prints(x and true) + prints(x or false) + prints(x or true) + + # TYPE_VECTOR2 + x = Vector2(1, 1) + prints("TYPE_VECTOR2") + prints(not x) + prints(x and false) + prints(x and true) + prints(x or false) + prints(x or true) + + # TYPE_VECTOR2I + x = Vector2i(1, 1) + prints("TYPE_VECTOR2I") + prints(not x) + prints(x and false) + prints(x and true) + prints(x or false) + prints(x or true) + + # TYPE_RECT2 + x = Rect2(1, 1, 1, 1) + prints("TYPE_RECT2") + prints(not x) + prints(x and false) + prints(x and true) + prints(x or false) + prints(x or true) + + # TYPE_RECT2I + x = Rect2i(1, 1, 1, 1) + prints("TYPE_RECT2I") + prints(not x) + prints(x and false) + prints(x and true) + prints(x or false) + prints(x or true) + + # TYPE_VECTOR3 + x = Vector3(1, 1, 1) + prints("TYPE_VECTOR3") + prints(not x) + prints(x and false) + prints(x and true) + prints(x or false) + prints(x or true) + + # TYPE_VECTOR3I + x = Vector3i(1, 1, 1) + prints("TYPE_VECTOR3I") + prints(not x) + prints(x and false) + prints(x and true) + prints(x or false) + prints(x or true) + + # TYPE_TRANSFORM2D + x = Transform2D.IDENTITY + prints("TYPE_TRANSFORM2D") + prints(not x) + prints(x and false) + prints(x and true) + prints(x or false) + prints(x or true) + + # TYPE_VECTOR4 + x = Vector4(1, 1, 1, 1) + prints("TYPE_VECTOR4") + prints(not x) + prints(x and false) + prints(x and true) + prints(x or false) + prints(x or true) + + # TYPE_VECTOR4I + x = Vector4i(1, 1, 1, 1) + prints("TYPE_VECTOR4I") + prints(not x) + prints(x and false) + prints(x and true) + prints(x or false) + prints(x or true) + + # TYPE_PLANE + x = Plane.PLANE_XY + prints("TYPE_PLANE") + prints(not x) + prints(x and false) + prints(x and true) + prints(x or false) + prints(x or true) + + # TYPE_QUATERNION + x = Quaternion.IDENTITY + prints("TYPE_QUATERNION") + prints(not x) + prints(x and false) + prints(x and true) + prints(x or false) + prints(x or true) + + # TYPE_AABB + x = AABB(Vector3.ONE, Vector3.ONE) + prints("TYPE_AABB") + prints(not x) + prints(x and false) + prints(x and true) + prints(x or false) + prints(x or true) + + # TYPE_BASIS + x = Basis.IDENTITY + prints("TYPE_BASIS") + prints(not x) + prints(x and false) + prints(x and true) + prints(x or false) + prints(x or true) + + # TYPE_TRANSFORM3D + x = Transform3D.IDENTITY + prints("TYPE_TRANSFORM3D") + prints(not x) + prints(x and false) + prints(x and true) + prints(x or false) + prints(x or true) + + # TYPE_PROJECTION + x = Projection.IDENTITY + prints("TYPE_PROJECTION") + prints(not x) + prints(x and false) + prints(x and true) + prints(x or false) + prints(x or true) + + # TYPE_COLOR + x = Color.WHITE + prints("TYPE_COLOR") + prints(not x) + prints(x and false) + prints(x and true) + prints(x or false) + prints(x or true) + + # TYPE_STRING_NAME + x = &"name" + prints("TYPE_STRING_NAME") + prints(not x) + prints(x and false) + prints(x and true) + prints(x or false) + prints(x or true) + + # TYPE_NODE_PATH + x = ^"path" + prints("TYPE_NODE_PATH") + prints(not x) + prints(x and false) + prints(x and true) + prints(x or false) + prints(x or true) + + # TYPE_RID + x = get_rid() + prints("TYPE_RID") + prints(not x) + prints(x and false) + prints(x and true) + prints(x or false) + prints(x or true) + + # TYPE_OBJECT + x = self + prints("TYPE_OBJECT") + prints(not x) + prints(x and false) + prints(x and true) + prints(x or false) + prints(x or true) + + # TYPE_CALLABLE + x = test + prints("TYPE_CALLABLE") + prints(not x) + prints(x and false) + prints(x and true) + prints(x or false) + prints(x or true) + + # TYPE_SIGNAL + x = foo + prints("TYPE_SIGNAL") + prints(not x) + prints(x and false) + prints(x and true) + prints(x or false) + prints(x or true) + + # TYPE_DICTIONARY + x = { a = 1} + prints("TYPE_DICTIONARY") + prints(not x) + prints(x and false) + prints(x and true) + prints(x or false) + prints(x or true) + + # TYPE_ARRAY + x = [1] + prints("TYPE_ARRAY") + prints(not x) + prints(x and false) + prints(x and true) + prints(x or false) + prints(x or true) + + # TYPE_PACKED_BYTE_ARRAY + x = PackedByteArray([1]) + prints("TYPE_PACKED_BYTE_ARRAY") + prints(not x) + prints(x and false) + prints(x and true) + prints(x or false) + prints(x or true) + + # TYPE_PACKED_INT32_ARRAY + x = PackedInt32Array([1]) + prints("TYPE_PACKED_INT32_ARRAY") + prints(not x) + prints(x and false) + prints(x and true) + prints(x or false) + prints(x or true) + + # TYPE_PACKED_INT64_ARRAY + x = PackedInt64Array([1]) + prints("TYPE_PACKED_INT64_ARRAY") + prints(not x) + prints(x and false) + prints(x and true) + prints(x or false) + prints(x or true) + + # TYPE_PACKED_FLOAT32_ARRAY + x = PackedFloat32Array([1]) + prints("TYPE_PACKED_FLOAT32_ARRAY") + prints(not x) + prints(x and false) + prints(x and true) + prints(x or false) + prints(x or true) + + # TYPE_PACKED_FLOAT64_ARRAY + x = PackedFloat64Array([1]) + prints("TYPE_PACKED_FLOAT64_ARRAY") + prints(not x) + prints(x and false) + prints(x and true) + prints(x or false) + prints(x or true) + + # TYPE_PACKED_STRING_ARRAY + x = PackedStringArray(["1"]) + prints("TYPE_PACKED_STRING_ARRAY") + prints(not x) + prints(x and false) + prints(x and true) + prints(x or false) + prints(x or true) + + # TYPE_PACKED_VECTOR2_ARRAY + x = PackedVector2Array([Vector2.ONE]) + prints("TYPE_PACKED_VECTOR2_ARRAY") + prints(not x) + prints(x and false) + prints(x and true) + prints(x or false) + prints(x or true) + + # TYPE_PACKED_VECTOR3_ARRAY + x = PackedVector3Array([Vector3.ONE]) + prints("TYPE_PACKED_VECTOR3_ARRAY") + prints(not x) + prints(x and false) + prints(x and true) + prints(x or false) + prints(x or true) + + # TYPE_PACKED_COLOR_ARRAY + x = PackedColorArray([Color.WHITE]) + prints("TYPE_PACKED_COLOR_ARRAY") + prints(not x) + prints(x and false) + prints(x and true) + prints(x or false) + prints(x or true) diff --git a/modules/gdscript/tests/scripts/analyzer/features/boolean_operators_for_all_types.out b/modules/gdscript/tests/scripts/analyzer/features/boolean_operators_for_all_types.out new file mode 100644 index 00000000000..e2945c910ae --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/boolean_operators_for_all_types.out @@ -0,0 +1,229 @@ +GDTEST_OK +TYPE_NIL +true +false +false +false +true +TYPE_BOOL +false +false +true +true +true +TYPE_INT +false +false +true +true +true +TYPE_FLOAT +false +false +true +true +true +TYPE_STRING +false +false +true +true +true +TYPE_VECTOR2 +false +false +true +true +true +TYPE_VECTOR2I +false +false +true +true +true +TYPE_RECT2 +false +false +true +true +true +TYPE_RECT2I +false +false +true +true +true +TYPE_VECTOR3 +false +false +true +true +true +TYPE_VECTOR3I +false +false +true +true +true +TYPE_TRANSFORM2D +true +false +false +false +true +TYPE_VECTOR4 +false +false +true +true +true +TYPE_VECTOR4I +false +false +true +true +true +TYPE_PLANE +false +false +true +true +true +TYPE_QUATERNION +true +false +false +false +true +TYPE_AABB +false +false +true +true +true +TYPE_BASIS +true +false +false +false +true +TYPE_TRANSFORM3D +true +false +false +false +true +TYPE_PROJECTION +true +false +false +false +true +TYPE_COLOR +false +false +true +true +true +TYPE_STRING_NAME +false +false +true +true +true +TYPE_NODE_PATH +false +false +true +true +true +TYPE_RID +true +false +false +false +true +TYPE_OBJECT +false +false +true +true +true +TYPE_CALLABLE +false +false +true +true +true +TYPE_SIGNAL +false +false +true +true +true +TYPE_DICTIONARY +false +false +true +true +true +TYPE_ARRAY +false +false +true +true +true +TYPE_PACKED_BYTE_ARRAY +false +false +true +true +true +TYPE_PACKED_INT32_ARRAY +false +false +true +true +true +TYPE_PACKED_INT64_ARRAY +false +false +true +true +true +TYPE_PACKED_FLOAT32_ARRAY +false +false +true +true +true +TYPE_PACKED_FLOAT64_ARRAY +false +false +true +true +true +TYPE_PACKED_STRING_ARRAY +false +false +true +true +true +TYPE_PACKED_VECTOR2_ARRAY +false +false +true +true +true +TYPE_PACKED_VECTOR3_ARRAY +false +false +true +true +true +TYPE_PACKED_COLOR_ARRAY +false +false +true +true +true