-
-
Notifications
You must be signed in to change notification settings - Fork 21.1k
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
Fix GDScript base and outer classes, signals and functions lookup order #70246
Fix GDScript base and outer classes, signals and functions lookup order #70246
Conversation
Hah, but unfortunately not right. There is difference in logic for base and outer classes. In base classes they do handle variables, functions and signals, while in outer lookup it is ignored. So, the next example based on that: class A:
var Q := 'right one'
class X:
const Q := 'wrong one'
class Y extends X:
class B extends A:
func check() -> void:
print(Q)
func _ready() -> void:
var b := Y.B.new()
b.check() |
b30bb66
to
cbdc55f
Compare
Please. I do please you to create a complete example in advance. |
Yeah, sorry for that, my reasoning is that I try to show the simplest example that everyone will see is wrong, not the most convoluted one (because it will be confusing and less in priority). When you present a solution, I look at it and analyze what would be the next simplest showcase that the solution is wrong. I also pointed out the root of the problem - as I wrote in the issue opening text you cannot continue using |
48d7eb8
to
dcbccbc
Compare
This comment was marked as off-topic.
This comment was marked as off-topic.
a7dffc3
to
627d8d9
Compare
I think I provided the context, but solution is not a one-line change. My thoughts: I think that |
27bbe15
to
794c7ff
Compare
@vonagam I replaced the list value of Then, I realized that @rune-scape @anvilfolk If you could add your input to this complex issue, it would be greatly appreciated. |
Been looking at this a little bit, and I'm starting to wonder if maybe we shouldn't make the analyzer a little more flexible. What if we had a bitfield in I can imagine this bitfield having:
This way, depending on what we are searching for (e.g. whether we're looking into base classes or outer classes), we could pass the method some information stating that it should ignore instance members and class types, and only look for class members. Would this make sense at all? |
Alternative solution to separate bases from outer: |
I think you misunderstand the method. It returns, yes, base classes in priority over outer classes. But the recursive nature of the method makes it so that outer classes of the first base class have priority over the second base class. |
794c7ff
to
b1c0e50
Compare
Whoa, I'm totally wrong. You're right! |
Just found out how to add support for signals. It's in the PR. |
26ad979
to
1851996
Compare
d93c284
to
62df74b
Compare
|
||
p_identifier->set_datatype(p_identifier_datatype); | ||
Error err = OK; | ||
GDScript *scr = GDScriptCache::get_full_script(p_identifier_datatype.script_path, err).ptr(); |
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.
There's a bit of an issue about loading the script in the middle of the analyzer because this will not work if there are cyclic dependencies between the classes (that is, the script being loaded depends on the one being parsed, this will fail).
I'm not sure why this needs to be made a compile-time constant. I can see some benefits but I don't know if it can work properly in all cases so it might be better for the compiler to sort this out in the next phase.
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.
There's a bit of an issue about loading the script in the middle of the analyzer because this will not work if there are cyclic dependencies between the classes (that is, the script being loaded depends on the one being parsed, this will fail).
For now, the tests pass and no issue about this has been found yet. Do you have an idea of a test to add where the issue would happen?
I'm not sure why this needs to be made a compile-time constant.
I added the inlining as it's just a set of instructions to execute, there's no logic beyond this function, like there's no need for this function other than reduce duplicated code. But I'm kinda a noob at C++, so I can remove the inline part.
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.
Looking at the code deeper now I understand why this "works". get_full_script()
actually returns the shallow script here, since this is not asking to update from disk. The current script being loaded is already on the full_gdscript_cache
because it 's added to the map before it's actually compiled, so the get_full_script()
will never fail, but sometimes return a shallow (i.e. not compiled) script instead.
So technically this will always work because even if it can't compile due to a reference cycle, it will still get the script that will be compiled later.
The main problem now is that you add a reference to the script in the function's list of constants, which will create a reference cycle that can never be cleared. Especially since this is also done for the very script being compiled now, so any reference to the same class will create this ref cycle (noted, it's not often that one references the same class by name, but there's no reason to keep a new reference to same script if that happens).
My question is still the same though: why does this need to be a compile-time constant? Even if you are accessing members from it, I believe the analyzer is able to retrieve the members directly without having to store the class. For instance: var v = ClassName.InnerClass.SOME_CONST
can just grab SOME_CONST
and store it directly as a constant. With this PR the InnerClass
would also be stored in this case, which is unnecessary.
Unless I'm missing something else, making this constant does not serve any particular purpose and it's not needed in any case.
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.
It started with need to make const X := preload(...).X
work. If a subscript for an inner class is not constant then it cannot be assigned to a constant right now. And if it is a constant then it is expected to have reduced_value. #70055 is where it was fixed like that.
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.
My issue is that with a script like this:
class_name Foo
const x = 0
func bar():
print(Foo.x)
You now store a reference to the Foo
script inside the constants of its own function bar
. I understand storing references to other scripts (even if they make a cycle) but I don't see how this is necessary.
At the very least this should avoid storing the very same script being compiled.
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.
The overall approach is good, I just have the one concerned I commented about loading scripts during parsing.
44661b1
to
18c653a
Compare
The build fails, but it's Windows that failed to build the artifacts, not that it had errors elsewhere. |
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'm coauthoring (for part inside reduce_identifier_from_base
), so do approve.
Though, it is bugging me that get_full_script
is used for getting a script for a class and not get_shallow_script
with raise_status
as it is done for the same purpose in preload. But at this point it is already a preexisting thing, this PR extends what this logic applies to but does not change it. I do hope that in future shallow script can be made to work.
Otherwise everything is perfect.
I did not try that, I'll try! |
18c653a
to
0846a22
Compare
As just discussed with @vonagam, The So, as he suggested, another PR should maybe take a look at why and fix it. |
0846a22
to
e072bc2
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 believe we can merge as it is since it works and fixes the issues. I still have concerns about the lifetime of the scripts since I believe there are some cases creating reference cycles, but we can solve those problems (if they do exist indeed) properly later on.
e072bc2
to
c9e0763
Compare
c9e0763
to
e072bc2
Compare
- Add outer class lookup test - Add signal lookup test Co-authored-by: Dmitrii Maganov <[email protected]>
e072bc2
to
fb175d9
Compare
Thanks to both of you, huge work! |
Fix GDScript inner and outer classes lookup order
Fixes #70216 (again)
Fixes #70495 - Accessing keys() dict of an enum defined in a singleton crashes app