-
-
Notifications
You must be signed in to change notification settings - Fork 21.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Modify Array/Dictionary::operator== to do real key/value comparison #35816
Modify Array/Dictionary::operator== to do real key/value comparison #35816
Conversation
From what you discovered about the use of == operators in the code base, it does seem like this is a better solution than PR #29222. Two things I would like to mention:
|
@pouleyKetchoupp thanks a lot for you inputs ;-)
I'll had to this PR your change related to this issue then 👍
That's a really good point ! var recursive_a_1 = [1]
recursive_a_1.append(recursive_a_1)
print("recursive_a_1 == recursive_a_1 ==> ", recursive_a_1 == recursive_a_1) This is interesting given doing So I've modified the PR to handle recursive check with a similar stack system. However I fell like this code would benefit a lot from unit testing... but there is no such thing in Godot (well there is the https://github.com/godotengine/godot-tests repo, but it doesn't seems really used) Here is the tests I've done manually: var x1 = {}
var y1 = {}
x1[1] = y1
y1[1] = y1
var x2 = {}
var y2 = {}
x2[1] = y2
y2[1] = y2
print("x1 == x2 ==> ", x1 == x2)
# Test dictionary
var d1 = {1: 1}
var d2 = {1: 1}
var d1_ref2 = d1
var other_d = {1: 2}
print("d1 == d2 ==> ", d1 == d2)
print("d1 == d1_ref2 ==> ", d1 == d1_ref2)
print("d1 != other_d ==> ", d1 != other_d)
var nested1_d1 = {1: d1, 2: 2}
var nested2_d1 = {1: d1, 2: 2}
var nested3_d2 = {1: d2, 2: 2}
var other_d_nested = {1: other_d, 2: 2}
print("nested1_d1 == nested2_d1 ==> ", nested1_d1 == nested2_d1)
print("nested1_d1 == nested3_d2 ==> ", nested1_d1 == nested3_d2)
print("nested1_d1 != other_d_nested ==> ", nested1_d1 != other_d_nested)
var recursive_d_1 = {1: 1}
recursive_d_1[2] = recursive_d_1
print("recursive_d_1 == recursive_d_1 ==> ", recursive_d_1 == recursive_d_1)
var recursive_d_2 = {1: 1}
recursive_d_2[2] = recursive_d_2
print("recursive_d_1 == recursive_d_2 ==> ", recursive_d_1 == recursive_d_2)
var cross_recursive_d_1 = {1: 1}
var cross_recursive_d_2 = {1: 1}
cross_recursive_d_1[2] = cross_recursive_d_2
cross_recursive_d_2[2] = cross_recursive_d_1
print("cross_recursive_d_1 == cross_recursive_d_2 ==> ", cross_recursive_d_1 == cross_recursive_d_2)
if false:
var key_recursive_d_1 = {1: 1}
var key_recursive_d_2 = {1: 1}
# BUG !!!! This Crash here !
key_recursive_d_1[key_recursive_d_2] = 1
key_recursive_d_2[key_recursive_d_1] = 1
print("key_recursive_d_1 == key_recursive_d_2 ==> ", key_recursive_d_1 == key_recursive_d_2)
if false:
var x = ['x']
var y = ['y']
var z = ['z']
y.append(y)
x.append(y)
x.append(y)
# BUG !!!!! this prints "[x, [y, [...]], [...]]" but it should be "[x, [y, [...]], [y, [...]]]"
print(x)
# Test array
var a1 = [1]
var a2 = [1]
var a1_ref2 = a1
var other_a = [2]
print("a1 == a2 ==> ", a1 == a2)
print("a1 == a1_ref2 ==> ", a1 == a1_ref2)
print("a1 != other_a ==> ", a1 != other_a)
var nested1_a1 = [a1, 2]
var nested2_a1 = [a1, 2]
var nested3_a2 = [a2, 2]
var other_a_nested = [other_a, 2]
print("nested1_a1 == nested2_a1 ==> ", nested1_a1 == nested2_a1)
print("nested1_a1 == nested3_a2 ==> ", nested1_a1 == nested3_a2)
print("nested1_a1 != other_a_nested ==> ", nested1_a1 != other_a_nested)
var recursive_a_1 = [1]
recursive_a_1.append(recursive_a_1)
print("recursive: ", recursive_a_1)
print("recursive_a_1 == recursive_a_1 ==> ", recursive_a_1 == recursive_a_1)
var recursive_a_2 = [1]
recursive_a_2.append(recursive_a_2)
print("recursive_a_1 == recursive_a_2 ==> ", recursive_a_1 == recursive_a_2)
var cross_recursive_a_1 = [1]
var cross_recursive_a_2 = [1]
cross_recursive_a_1.append(cross_recursive_a_2)
cross_recursive_a_2.append(cross_recursive_a_1)
print("cross_recursive_a_1 == cross_recursive_a_2 ==> ", cross_recursive_a_1 == cross_recursive_a_2) As you can see I've also discovered two unrelated bugs ;-) |
75e87e4
to
5c214d1
Compare
0d8f573
to
84ccc0f
Compare
What's the status of this? |
@RandomShaper It should be ready to merge, my only concern is there is currently no place to add unittests in godot :'( |
See #30743. |
84ccc0f
to
3b4d515
Compare
I've rebased this PR ;-) |
@akien-mga sorry to directly ping you, but I'm not sure what can be done to get this and #35813 merged... |
Just tested on master. |
Can this be rebased? |
3b4d515
to
1207f14
Compare
@fire I've rebased the PR However it seems to have an issue related to
I suspect this is linked to what @reduz said in #42780:
|
1207f14
to
b23138c
Compare
Hang on, this basically replaces the behavior of |
The idea is to replace the behavior of
In Python you can do But I think the most important thing is to have both kind of comparisons (per value and per pointer) available and documented (I'm sick of having to do that or that). |
@touilleMan This PR needs to be rebased on the latest master when you have the time. |
0673582
to
13c64f5
Compare
c87cddb
to
394b6c5
Compare
The doc changes in the CI build seem to be due to changes in the printing of arrays: Might be a rebase mistake as those changed recently IINM. |
@akien-mga Thanks for the tip ! I'll have a look at this ;-) |
7d6613a
to
b66f841
Compare
@akien-mga the last error in the build involve the documentation: iff --git a/doc/classes/CodeEdit.xml b/doc/classes/CodeEdit.xml
index 93f72d45ae..60efd5d3a5 100644
--- a/doc/classes/CodeEdit.xml
+++ b/doc/classes/CodeEdit.xml
@@ -494,7 +494,6 @@
<member name="line_length_guidelines" type="int[]" setter="set_line_length_guidelines" getter="get_line_length_guidelines" default="[]">
Draws vertical lines at the provided columns. The first entry is considered a main hard guideline and is draw more prominently
</member>
- <member name="structured_text_bidi_override_options" type="Array" setter="set_structured_text_bidi_override_options" getter="get_structured_text_bidi_override_options" override="true" default="[]" />
<member name="symbol_lookup_on_click" type="bool" setter="set_symbol_lookup_on_click_enabled" getter="is_symbol_lookup_on_click_enabled" default="false">
Set when a validated word from [signal symbol_validate] is clicked, the [signal symbol_lookup] should be emitted.
</member> It is due to new array comparison behavior in here: Line 309 in 84faf39
Here, when dealing with My guess is the per-pointer comparison was not intended here given this part of the code aims at not duplicating parent attributes that have been left untouched by children. So my last commit 2351380d74f7604a9707f8e7b5892cec10dbb929 corrects the documentation, this PR should be finally ready to merge now ;-) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess the commits could benefit from some squashing.
…cursive support (and add unittests)
…rrect array comparison
2351380
to
ce47ce8
Compare
@RandomShaper rebased&squashed, ready for merge ;-) |
The documentation for Dictionary (and maybe Array) should be updated to reflect these changes. At least Dictionary's description still has an explicit warning that Dictionary comparisons are not based on contents. IIUC, we now lack a way to compare Arrays and Dictionaries by reference. It would be good to add one so that we can close all related issues / proposals as solved. |
Here is my attempt to fix the current infamous dictionary comparison behavior (see #27615)
Basically the issue is currently two Dictionary object only considered equal if they point on the same underlying hashmap.
This is very error prone given this behavior leaks into GDScript:
There is already PR #29222 that tries to fix this by modifying Variant comparison. However I believe this is not the right way to go given it only fix GDScript, but not GDNative !
Currently GDNative's
godot_dictionary_operator_equal
is pretty useless (I would even say it is extremely error prone, I've personally been bitten multiple time by this function) and so GDNative users has to implement their own comparison (see https://github.com/touilleMan/godot-python/blob/72df9d1b38120aeec3c702004e14dcb82e95bde5/tools/builtins_templates/dictionary.tmpl.pxi#L180-L185) which is both cumbersome and inefficient.Given I couldn't really figure out the usecase for this underlying-hashmap-only comparison, I've recompiled a modified version of Godot with
Dictionary::operator==
andDictionary::operator!=
removed (see touilleMan@2409aa6).The only place those operator are used are in
variant_op.cpp
in theOP_EQUAL
andOP_NOT_EQUAL
operators.So I guess I can officially say the current implementation of
Dictionary::operator==
is useless \o/Of course, I think we should do the same for
Array::operator==
, I'll update this PR with those change if we agree on the idea ;-)Bugsquad edit:
master
) #25375.==
, unlike Arrays which are compared by value #27615.