-
-
Notifications
You must be signed in to change notification settings - Fork 21.2k
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
Implement typed dictionaries #78656
Implement typed dictionaries #78656
Conversation
307a874
to
09af208
Compare
I would prefer:
Just |
09af208
to
847943a
Compare
@lufog Great point! I adjusted the code to handle exactly that, but it revealed some other pitfalls in the process (mainly the process of transfering container types from one DataType to another) so I'll have to iron that out before showing off a new version In the meantime I've added documentation descriptions & made it possible to use a constructor to make a typed dictionary in GDScript, much like typed arrays |
847943a
to
bf74f2e
Compare
8b50554
to
6c41e2e
Compare
GDScript tests implemented! They're largely lifted from the tests given to typed Arrays so more specialization will be required, but they're a solid starting point to see what's awry off the bat Namely, typed arrays seem to be instantiating with null keys & values, though they're recognized as typed. I'm not quite sure yet where the incorrect implementation is happening, but that's my current focus as it very well may be the linchpin of the editor errors |
6c41e2e
to
f3dd0cc
Compare
With typed arrays, we have the variance problem: var typed: Array[int] = [1, 2, 3, 4]
var as_untyped: Array = typed
as_untyped[0] = "string" # silently fails at runtime I guess the same problem exists here, but for both keys and values? |
Yes, but it's still a hole in the type system. I understand the desire to pass typed arrays to APIs accepting untyped ones, and for reading, this is completely sound. The covariance violation occurs when writing to an My question is, is the same covariance conversion ("upcast") planned for dictionaries on each of the generic parameters? That is:
And if yes, are we fully aware of the pitfalls? |
However, this is not directly related to the PR. PRs of this kind tend to garner large numbers of comments that are difficult to navigate. If you'd like to discuss this, I suggest the |
Thanks! My point was not to discuss typed arrays, but the covariance problem regarding dictionaries, and what conversions we allow (see above). As such it's quite on-topic I believe. |
f3dd0cc
to
fad2a93
Compare
The holes in the type system certainly have been a bit of a headache to navigate, but attempting to overhaul that system as a whole is probably a bit beyond this PR (at least as I initially envisioned it). This is more about setting up the system that currently exists to dictionaries, warts and all. If the current implementation were to somehow inhibit the ability to implement typed dictionaries in the first place, then changes to the system would make sense On that note, I've figured out why variants were appearing as null & not changing when swapping types, so both those fixes are in place. As it stands, the two biggest remaining issues are:
|
39e959b
to
56daf46
Compare
52c0bb0
to
ce7009f
Compare
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.
Overall it looks good to me, the implementation is consistent with typed arrays. We debated for a long time whether we should resolve the typed array issues first, but then we came to the conclusion that waiting would take too long. So if we have to break typed collection compatibility in the future, it will be both arrays and dictionaries.
In my opinion, this PR is already in a very good state to be merged. However, it's worth waiting for approval from @vnen as the main GDScript maintainer.
Of course, there are some things we might have missed or overlooked, but I think we could address them in follow-up PRs.
- Attribute access does not have static checks, unlike index access. See the review comments.
- Add support for binary serialization of typed dictionaries, like in Core: Add typed array support for binary serialization #78219. Since this is a sensitive part, it's good to do in a separate PR.
PROPERTY_HINT_DICTIONARY_TYPE
vsPROPERTY_HINT_TYPE_STRING
. These hints have different formats and are sometimes mixed up in the engine.- The inspector part can be improved in the future. Currently, keys are displayed as string representations, which is not very convenient for viewing and editing.
- Perhaps it makes sense to adjust the documentation, GDScript tests, disassembler, etc.
Thanks for your great work, @Repiteo!
Patch
diff --git a/modules/gdscript/tests/scripts/utils.notest.gd b/modules/gdscript/tests/scripts/utils.notest.gd
index 5d615d8557..1e2788f765 100644
--- a/modules/gdscript/tests/scripts/utils.notest.gd
+++ b/modules/gdscript/tests/scripts/utils.notest.gd
@@ -24,6 +24,11 @@ static func get_type(property: Dictionary, is_return: bool = false) -> String:
if str(property.hint_string).is_empty():
return "Array[<unknown type>]"
return "Array[%s]" % property.hint_string
+ TYPE_DICTIONARY:
+ if property.hint == PROPERTY_HINT_DICTIONARY_TYPE:
+ if str(property.hint_string).is_empty():
+ return "Dictionary[<unknown type>, <unknown type>]"
+ return "Dictionary[%s]" % str(property.hint_string).replace(";", ", ")
TYPE_OBJECT:
if not str(property.class_name).is_empty():
return property.class_name
@@ -188,6 +193,8 @@ static func get_property_hint_name(hint: PropertyHint) -> String:
return "PROPERTY_HINT_INT_IS_POINTER"
PROPERTY_HINT_ARRAY_TYPE:
return "PROPERTY_HINT_ARRAY_TYPE"
+ PROPERTY_HINT_DICTIONARY_TYPE:
+ return "PROPERTY_HINT_DICTIONARY_TYPE"
PROPERTY_HINT_LOCALE_ID:
return "PROPERTY_HINT_LOCALE_ID"
PROPERTY_HINT_LOCALIZABLE_STRING:
@@ -4371,6 +4371,14 @@ bool GDScriptParser::export_annotations(AnnotationNode *p_annotation, Node *p_ta | |||
export_type.type_source = variable->datatype.type_source; | |||
} | |||
|
|||
bool is_dict = false; |
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.
Note that the is_array
is used to apply export annotations to array elements, see #82952 for details. Not sure if a similar feature makes sense for dictionaries, perhaps your code needs it to simplify handling of key and value types.
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 can't speak for the other types of export annotations, but this is still needed for the basic @export
scenario; otherwise, all exported dictionaries would show up as untyped in the inspector
@@ -4459,6 +4472,120 @@ bool GDScriptParser::export_annotations(AnnotationNode *p_annotation, Node *p_ta | |||
push_error(vformat(R"(Node export is only supported in Node-derived classes, but the current class inherits "%s".)", p_class->base_type.to_string()), p_annotation); | |||
return false; | |||
} | |||
|
|||
if (is_dict) { |
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.
This code may be in the wrong place. if (is_array)
is one level higher, after if (use_default_variable_type_check)
.
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 thiiink this is fine. This bit was tricky because I had to parse key & value independently, so there was a fair bit of repeat code as a result. It does the same logic as the outer if (is_array)
check, albeit resolved earlier.
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 think this is fine. But eventually we should extract the logic to avoid the repetition and make sure it's consistent.
ce7009f
to
bceae2f
Compare
This comment was marked as resolved.
This comment was marked as resolved.
bceae2f
to
b0c62ce
Compare
Serialisation now makes |
This shows we need some unit tests for this as the unit tests completely missed this, only the doc generator showed it which is an issue |
f6d14ff
to
101e47a
Compare
f38554a
to
c1a3195
Compare
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.
Reviewed mostly the GDScript part and have some small notes but nothing blocking. Thanks a lot for working on this and sorry again for taking so long to review.
c1a3195
to
9853a69
Compare
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 checked the code modifications on EditorPropertyDictionary
changes and tested it. This looks good.
Thanks! Amazing work 🎉 🥇 This is one of the most awaited features for GDScript users, and thus a major highlight for 4.4! |
This aims to add the ability to bind typed dictionaries to script & GDScript. The implementation takes heavy inspiration from the existing typed array format.
The primary difference is the ability to specify type for just a key, leaving the value as typeless much like current dictionaries.Both key and value need their types specified for typed dictionaries, withVariant
allowing for a typeless equivalent.Syntax
Editor
Issues
All core issues are resolved!
There are a fair number of areas that still require fixes, in addition to whatever lingering bugs/errors there are. In particular:Variant
text appears as white instead of green in GDSCript if used as the value:[] operator
); it throws an error if attempting to do so in runtime & does recognize invalid keys when creating a dictionary initiallycontainer_element_type
being a list now instead of a single item. (This is inexperience with the engine on my end, dunno if it needs something special to memdelete array entries)Closes godotengine/godot-proposals#56