-
-
Notifications
You must be signed in to change notification settings - Fork 21.4k
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
GDScript: Begin making constants deep, not shallow or flat #71051
Conversation
>> on function: test() | ||
>> runtime/errors/constant_array_is_deep.gd | ||
>> 6 | ||
>> Invalid set index '0' (on base: 'Dictionary') with value of type 'int' |
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 for future work: we should make this error message more explicit.
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.
Also, hopefully as an analyzer error rather than as a runtime error?
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.
Looks good to me as a start.
Which types? PackedArrays probably could use this since they're also passed by reference, but the others are passed by copy so you wouldn't be able to change the original constant anyway. The only other case would be objects which does introduce some hurdles to guarantee immutability. |
if (unlikely(_fp->read_only != nullptr)) { | ||
// If p_from is a read-only array, just copy the contents to avoid further modification. | ||
_unref(); | ||
_p = memnew(ArrayPrivate); | ||
_p->refcount.init(); | ||
_p->array = _fp->array; | ||
_p->typed = _fp->typed; | ||
return; | ||
} | ||
|
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 believe this was done to allow mutability on assignments, e.g.:
const arr = [1, 2, 3]
var copy = arr # Create copy so it's a regular variable.
copy.push_back(4) # Only changes `copy`.
print(copy) # [1, 2, 3, 4]
print(arr) # [1, 2, 3]
Which wouldn't work with this change (push_back()
would fail). But I don't think there's a valid use case for this and the user can call duplicate()
if this is needed.
Thanks! |
So does this mean we'll no longer be able to modify constant dictionaries and arrays in future builds? |
@ExplodingImplosion That seems to be the case, yeah. Which sucks since we still don't have static properties. |
We do have static properties and it's still a hack ;) func _ready() -> void:
if not (MyClass as Script).has_meta("test"):
MyClass.assign_test()
else:
print((MyClass as Script).get_meta("test"))
in MyClass code: class_name MyClass
static func assign_test():
(MyClass as Script).set_meta("test", 2) Attach the first script to 2 nodes and it will print the value of |
Just for the record, the GDScript team does want to have proper static properties in the future, but that's not ready yet, won't be ready for 4.0. Yet, fixing this behavior and making it more reliable and predictable for its intended use case was important to do before 4.0. |
Yes, If implementation of read-only functionality is moved from
Third linked issue was about changes allowed on constant color. But now second change in this PR (that an assignment to subscript of constant base is forbidden) solves it. So, I guess, true - all currently allowed types in constants are covered. |
The changes all seem right but shouldn't this be changed to Devs coming from other languages might find the behaviour confusing otherwise. |
|
I will miss the hacky implementation of |
Now my project is impossible to realize in Godot 4... |
@b0z1 You can use autoloads to store static data until static variables are implemented. @freehuntx Please refrain from making this kind of unhelpful comments. |
That's a pretty big change that snuck in pretty much just before 4.0. Docs need to be updated ASAP otherwise people will run into a LOT of trouble |
Seriously? So you break a crucial functionality and tell us with Godot 5 we will get an alternative (static variables)? Atleast add a workaround until you offer us static variables -.-
|
We never told anyone to wait until Godot 5. We'll likely implement static variables for one of the first 4.x releases. We did this change now precisely so that we can implement static variables in the future without breaking compatibility. We did not remove a feature - we fixed an unintentional inconsistency, which some users relied upon. This As mentioned, with autoloads you can have shared memory accessible to your script instances. |
Out of curiosity, @Zireael07, which part of the docs recommends using a const dictionary like that? If we have something like that in the official documentation, it needs to be removed regardless of this PR, because while a nice trick, it's not really a supported, or indeed intentional, feature. |
Docs don't recommend this usage (or mention it) anywhere that I can see so far, but I know many people use this "trick" so it should be pointed out that this no longer works. |
|
@freehuntx Where is that quote from? @Zireael07 We can reinforce the idea that constant members are deeply constant in the GDScript intro doc, but otherwise I don't see how to inform users that a particular hack doesn't work anymore. We don't have an area in the docs for documenting such things. |
Not freehunt, but I found the quote: https://docs.godotengine.org/en/latest/tutorials/scripting/gdscript/gdscript_basics.html, Ctrl+F "Constants", note on a blue background. Yeah, this should be reinforced in the docs. |
So the documentation cant be trusted? |
Chill, friend :) I appreciate your enthusiasm, but we're all in this together. We're doing our best, most of us in our free time, to make Godot behave as consistently & well as it possibly can in the long run. This is a hiccup, yes. I think we are all in agreement here that ideally this would've been spotted & fixed during the alpha stage! Unfortunately, the Real World happens, and it's getting handled during beta. Functionality should be reinstated sooner rather than later, in a more intentional & logical way, which is better in the long run :) Throwing shade and being snarky just breeds negative emotions, and lessens the motivation for people to keep working on making Godot better :) |
The Array class description has a Note which claims that when declaring with const, it can still be mutated. |
This remains incorrect in a Note on Constants within the GDScript basics reference:
|
Current behavior of const is troubled. They want to act like deep, but they are shallow and used by some as static variables:
It is possible to resolve this in two ways:
Either declare them as mutable and protected only from reassignment. Then do not expect them to produce constant results outside of constant context. (In this case, maybe, it is actually better to name them
static const
, as they are static.)Or declare them as deeply immutable. Then do not allow any modifications to the stored value, making it read-only.
In yesterday discussion between GDScript contributors it was decided to go with later option. This PR is a first move into that direction. It recursively makes array/dictionary literals to be read-only and disallows subscript assignments on constant base.
This does not cover all bases - other builtin types need to implement read-only mode too and other expressions than simple literals need to be covered too, also
set_read_only
most likely should be removed from public api for Array.But this one should cover most popular cases, making it clear what is expected and what is not. This change is a breaking one (resolution of this inconsistency in either direction will be breaking), preferably it should be included earlier than later.
Connected issues:
Related to #30212 (expectation about it being deep).
Related to #32063 (expectation about it being shallow and non-static).
Related to #54718 (expectation about it being deep).
Related to #56306 (expectation about it being shallow, problem with modifying).
Closes #61274 (expectation about it being deep).
Closes #61919 (expectation about it being shallow, problem with modifying).
Closes #62680 (expectation about it being deep, inconsistent behavior).
Closes #70349 (expectation about it being deep).
Closes #70469 (expectation about it being shallow, inconsistent behavior).
Closes #70730 (expectation about it being shallow, inconsistent behavior).
Closes #4576 (proposal to make them deep).