-
-
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 constants scope in extended or inner GDScript classes #69587
Fix constants scope in extended or inner GDScript classes #69587
Conversation
Generally makes sense - haven't be able to dig deeply. Is it possible to both have an outer class as well as a base class? If so, both ought to be checked right? Right now it's prioritizing the outer class over the base class. So something like: class_name OuterClass
class A:
const myvar = preload(...)
class B extends A:
func my_func(p : myvar):
print(myVar)
print(A.myVar) Would this work, or would class |
Gonna test this at once. |
i think you'll need 2 loops, one for checking base class, and one for checking each bases outer classes (because you don't want the outer-most script's base, you want the bases base (thats confusing, sorry)) |
b2ac34a
to
d09600d
Compare
I updated the logic! It now checks for outer classes and their extended classes, without the need of adding one more loop. I prioritized the outer classes first, as this was the logic as for now. This code now works: # `base.gd` declares `A`
extends "base.gd"
const OuterDeclared: = "OuterDeclared"
static func test(a: A) -> void:
print(A) # prints <GDScript #...>
# `base_inner.gd` declares `B`
class InnerBaseClass extends "base_inner.gd":
static func test_a(a: A) -> void:
print(OuterDeclared) # prints "OuterDeclared"
print(A) # prints <GDScript #...>
static func test_b(b: B) -> void:
print(B) # prints <GDScript #...> |
d09600d
to
c1c8cb2
Compare
current = current->outer; | ||
} | ||
} | ||
while (script_classes.size() > 0) { |
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 ideally this would just call reduce_identifier_from_base
recursively on outer classes, and check if the passed base is part of the current classes hierarchy to see if it can access its self
, but maybe thats just ideally
it would make the logic of searching a tree much simpler
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 don't really visualize how to use reduce_identifier_from_base
there as you do, if you could show an example.
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 it wouldn't have the information to avoid inf recursion, so looping is probably a better idea
@@ -2925,8 +2951,14 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod | |||
break; | |||
} | |||
} | |||
outer = outer->outer; | |||
if (script_class->outer != nullptr) { | |||
script_classes.insert(script_class->outer); |
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 don't know how to request changes, but i'm pretty sure this should be a loop inside the next if statement, collecting this outer class's bases outer classes (the terminology is so confusing), same in resolve_datatype
Look at these amazing MSPaint skills! But in all seriousness, inner classes need to check outer classes (blue) as well as base classes (red). I was thinking about this and it isn't really a tree - I think @adamscott is right that we need to worry about checking things twice, and about the possibility of cyclic dependencies. In that sense, having a |
c1c8cb2
to
35eca64
Compare
I updated the branch. Replaced |
while (outer != nullptr) { | ||
if (outer->has_member(name)) { | ||
const GDScriptParser::ClassNode::Member &member = outer->get_member(name); | ||
Vector<GDScriptParser::ClassNode *> checked_script_classes; |
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 should be moved outside of the base class loop and also collect the base classes of the base
while (outer != nullptr) { | ||
script_classes.append(outer); | ||
outer = outer->outer; | ||
} |
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 maybe this and the next loop should be instead something like this:
GDScriptParser::ClassNode *outer_base_class = script_class->base_type.class_type;
if (outer_base_class != nullptr) {
script_classes.append(outer_base_class);
GDScriptParser::ClassNode *outer_base_outer_class = outer_base_class->outer;
while (outer_base_outer_class != nullptr) {
script_classes.append(outer_base_outer_class );
outer = outer_base_outer_class ->outer;
}
}
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.
wait no, the bases of each outer need to be searched first...
i think i'm back to thinking recursion is the least complicated way to do this, and just have checked_script_classes
as a member of the analyzer or passed into reduce_identifier_from_base
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.
@rune-scape If you want to make a PR with your recursive changes and add me as a co-author, go for it! You seem to have an idea to fix this.
333b5a7
to
c112c6b
Compare
modules/gdscript/gdscript_cache.cpp
Outdated
if (singleton->destructing) { | ||
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.
For some reason, without this, the tests will result in a heap-use-after-free
error with the address sanitizer.
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.
That is so confusing! I don't see why these changes would do that.
Could it be a regression from elsewhere that shows up for you if you've rebased from master?
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 suspicious of this part, but the rest looks great!
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'll create a new PR for that part, it doesn't belong in this PR.
7ae904e
to
60ca361
Compare
Does this supersedes #57510? |
@trollodel Didn't know that there was an existing PR about the same issue. I searched the issue tracker before starting, didn't think about searching the pull requests too. Sorry. |
I would say yes. I just tested your PR and it doesn't pass, unfortunately, my added unit test. |
@@ -219,6 +219,22 @@ Error GDScriptAnalyzer::check_class_member_name_conflict(const GDScriptParser::C | |||
return OK; | |||
} | |||
|
|||
void GDScriptAnalyzer::get_script_classes(GDScriptParser::ClassNode *p_node, RBSet<GDScriptParser::ClassNode *> &p_set) { |
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 name implies you are getting all classes that are defined in scripts, but nothing else. Something about the name script_classes
makes it a little hard for me to understand what it does. Would it make more sense to say something like get_context_classes
or get_outer_and_base_classes
or something more in line with its intent? Those names are terrible too :P
If nothing else, perhaps some comments explaining exactly what the function does would be nice for future readers :)
It also seems like it doesn't really check for native/builtin classes? Should it?
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'll name the function get_class_node_current_scope_classes()
.
And no, no need for native/builtin classes, as checks are already made for those.
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 love the name!!!! That's perfect.
And just making sure: native/builtin class checks are already made going all the way up outer-class & extends hierarchy? Because maybe we're looking for an enum, say, somewhere in Object
, and you might need to follow up a few GDScript classes before you get there?
60ca361
to
d41655a
Compare
@adamscott ok. I want to test this PR in my personal project, if I'll find some time for that. But your tests are bigger than mine, so it's probably good. |
@trollodel it's always frustrating when this happens - so I personally really appreciate everyone's graciousness here <3 There's a couple active GDScript contributors right now, which didn't use to be the case and explains why your PR wasn't reviewed at the time. Please feel free to hop on RocketChat or tag any of us for GDScript PRs. We've been trying to review each other's PRs and make sure GDScript changes move along, and you'd be very welcome if you wanted to join these efforts again <3 |
@anvilfolk Thanks for your words, but I don't feel frustrated (quite the contrary TBH).
I'm already in the GDScript channel on RC, but I don't have anything to say or PR stalled now. |
82b2454
to
8a0dd2e
Compare
Updated the PR to use lists instead of vectors |
I just realized something that I hadn't realized before, so I thought I would make it clear here - especially since I think the PR doesn't fall into this trap! :) The treatment of outer classes and base classes is meant to be different. You should look for instance members in the base class only. Searching for types, constants, class members, etc, should be done on both base class and outer class, recursively in some smart order. So here's my understanding of how things are working from a bird's eye view:
I was worried that this was was doing base&outer class recursion for I feel like it's likely that there is some amount of duplication of work between Last comment is that I just realized how deeply important the order of |
I'm doing things that I didn't even knew I was doing. 🙃 |
} | ||
p_list->push_back(p_node); | ||
|
||
// Prioritize node base type over its outer class |
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.
Just to be sure @anvilfolk, this prioritizes the node base type over its outer class
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.
Yes, you are right! And it should prioritize all base classes before even the first outer class.
And then it prioritizes the first outer class's base classes (all the way up) before moving on to first outer class's outer class, and so forth.
It gets pretty confusing, but honestly if somebody is looking this deep into the class hierarchy for names that are reused everywhere, they should just refactor their code, hehehe :)
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 should also add native/builtin classes and everything from what I can tell! :)
8154042
to
35cce21
Compare
modules/gdscript/gdscript_cache.cpp
Outdated
if (singleton->destructing) { | ||
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'm suspicious of this part, but the rest looks great!
35cce21
to
d20b3b1
Compare
Removed all the |
@akien-mga The PR doesn't build because I split off the code that made this PR build to #69865. Once #69865 merged, it should fix the current sanitizers issues. |
d20b3b1
to
fc5cabb
Compare
fc5cabb
to
65a49ba
Compare
Thanks! |
Congraaaats! |
This PR depends on #69865Merged, thanksEspecially when checking the typing, the gdscript parser doesn't seem to pass through the code to set the right
script_class->outer
value for a givenGDScriptParser::ClassNode
. It is available though insidescript_class->base_type.class_type
inGDScriptAnalyzer::resolve_datatype()
.So this PR usesscript_class->base_type.class_type
if thescript_class->outer
is anullptr
.Edit: I updated the logic! It now checks for outer classes and their extended classes, without the need of adding one more loop. I prioritized the outer classes first, as this was the logic as for now.
This code now works:
It makes the constants of base class available as typing inside the extended classes.
Fixes #69586