Skip to content
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

Allow godot-visible functions to receive a Gd instead of self. #396

Merged

Conversation

mhoff12358
Copy link
Contributor

This is done by tagging the function with #[func(gd_self)].

I haven't written tests because there was an unrelated issue on my machine, but I have tested with a different project and it seems to work.

@GodotRust
Copy link

API docs are being generated and will be shortly available at: https://godot-rust.github.io/docs/gdext/pr-396

@gg-yb
Copy link
Contributor

gg-yb commented Sep 4, 2023

Did you test whether this solves the issue in #338 ?

@mhoff12358
Copy link
Contributor Author

It doesn't solve #338 because that involves implementing a trait that already defines on_notification to use &mut self.
I did test that this does enable interleaving rust and gdscript function calls/signals though. So if the trait was rewritten to use this it would work. But there seemed to be a debate on whether that was better or not.

@lilizoey
Copy link
Member

lilizoey commented Sep 4, 2023

I'd find it a bit weird for it to not be possible to do this for the trait methods as well as regular methods. It would require a bigger change to codegen to do this though which would probably cause a conflict with #387. that's kinda why i waited with doing this. but it might still be nice to have this for user-defined functions before then.

would it be better to tag the argument rather than the function?

also there should definitely be some tests for this.

i dont think using .get() is the right approach for getting the base field. since it means you can't use this on classes without a base field, and it means we can't call a method that takes a Gd<Self> argument if we currently have a mutable reference to self. adding a Base to InstanceStorage seems like a better idea to me at least.

@Bromeon Bromeon added c: register Register classes, functions and other symbols to GDScript feature Adds functionality to the library labels Sep 4, 2023
@mhoff12358
Copy link
Contributor Author

  1. Yeah, agreed that it'd be way better if this could also be done for trait methods. But I'm not sure how to address the issues around not wanting to require it everywhere. I think it's better to have it at least available for user-defined functions, but I could see preferring consistency. Do you think this should be shelved until there's an answer for what to do for traits?
  2. I could see tagging the argument instead. It was really easy to implement this way, but if you think that's better I'll take a look at it when I have a chance.
  3. Yeah, that's one of the reasons I tried to make it clear it's a draft. I need to figure out why the tests are failing on the master branch on my machine before I can write new ones, but that needs to happen before this could be merged.
  4. You said earlier you wrote the code for adding a Base<> to InstanceStorage. Do you want to merge that and I can rebase on top of it? Or share it in some other way for me to include?

@gg-yb
Copy link
Contributor

gg-yb commented Sep 5, 2023

Wouldn't it be possible to scan for whether the type of the first parameter is Gd<Self> (or equivalent Gd<T> when implemented on T) when collecting `#[func]'s? That way no tag would be needed.

@lilizoey
Copy link
Member

lilizoey commented Sep 5, 2023

Wouldn't it be possible to scan for whether the type of the first parameter is Gd<Self> (or equivalent Gd<T> when implemented on T) when collecting `#[func]'s? That way no tag would be needed.

how do you distinguish between that and a static method that takes a Gd<Self> as its first argument?

@lilizoey
Copy link
Member

lilizoey commented Sep 5, 2023

4. You said earlier you wrote the code for adding a `Base<>` to `InstanceStorage`. Do you want to merge that and I can rebase on top of it? Or share it in some other way for me to include?

i could yeah, i didn't test it very much. i just did it quickly to see what would be needed to implement this. I'll see if i can find it again.

@gg-yb
Copy link
Contributor

gg-yb commented Sep 6, 2023

Wouldn't it be possible to scan for whether the type of the first parameter is Gd<Self> (or equivalent Gd<T> when implemented on T) when collecting `#[func]'s? That way no tag would be needed.

how do you distinguish between that and a static method that takes a Gd<Self> as its first argument?

My bad, thought rust-lang/rust#44874 was already stabilized, which would have lended itself for this purpose. You are right, it does not work if we cannot name the Gd parameter self

@lilizoey
Copy link
Member

lilizoey commented Sep 6, 2023

@mhoff12358 here: https://github.com/lilizoey/gdextension/tree/feature/instance-storage-get-gd. I haven't tested this much beyond just checking that it works (by manually calling all the functions). But i think this should work.

@mhoff12358 mhoff12358 force-pushed the mhoff12358/gd_as_self_argument branch 2 times, most recently from 76ec1f5 to 0dae372 Compare September 10, 2023 05:25
@mhoff12358
Copy link
Contributor Author

I've pulled in lilizoey's commit adding a base field to InstanceStorage, which does seem much better.
I still haven't gotten to figuring out why the tests weren't working on my machine to write new tests, so this'll stay a draft until I get a little more time for this.

@@ -113,7 +122,7 @@ mod multi_threaded {
use std::sync;
use std::sync::atomic::{AtomicU32, Ordering};

use crate::obj::GodotClass;
use crate::obj::{Base, Gd, GodotClass, Share};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh i forgot to add Inherits as an import here, that's why the CI failed.

@mhoff12358
Copy link
Contributor Author

mhoff12358 commented Sep 15, 2023

Alright, tests finally added. It's a really simple case, but still.

Lemme know if you'd like it to do something more complex.

@mhoff12358
Copy link
Contributor Author

Whoops, the test broke clippy. Seems I introduced a warning. But I really need to get to bed, I'll fix it tomorrow

Copy link
Member

@Bromeon Bromeon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot, this is a nice feature on the road to #338!

godot-core/src/obj/base.rs Show resolved Hide resolved
godot-core/src/storage.rs Show resolved Hide resolved
godot-macros/src/class/data_models/func.rs Outdated Show resolved Hide resolved
itest/godot/ManualFfiTests.gd Outdated Show resolved Hide resolved
itest/rust/src/register_tests/func_test.rs Outdated Show resolved Hide resolved
godot-macros/src/class/godot_api.rs Outdated Show resolved Hide resolved
godot-macros/src/class/godot_api.rs Outdated Show resolved Hide resolved
godot-macros/src/class/godot_api.rs Outdated Show resolved Hide resolved
@mhoff12358 mhoff12358 force-pushed the mhoff12358/gd_as_self_argument branch 2 times, most recently from 9f79a6d to 9f04e1f Compare September 17, 2023 21:52
@mhoff12358
Copy link
Contributor Author

I improved the test case to actually trigger the error condition.

@mhoff12358 mhoff12358 changed the title Draft: Allow godot-visible functions to receive a Gd instead of self. Allow godot-visible functions to receive a Gd instead of self. Sep 17, 2023
Copy link
Member

@Bromeon Bromeon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the update! 🙂
Some more comments. There are also some conflicts at the moment.

itest/godot/TestRunner.gd Outdated Show resolved Hide resolved
godot-core/src/obj/base.rs Show resolved Hide resolved
itest/rust/src/register_tests/func_test.rs Outdated Show resolved Hide resolved
godot-macros/src/class/godot_api.rs Outdated Show resolved Hide resolved
Comment on lines 246 to 251
sig.params[0].0 = FnParam::Receiver(FnReceiverParam {
attributes: vec![],
tk_ref: Some(proc_macro2::Punct::new('&', proc_macro2::Spacing::Alone)),
tk_mut: None,
tk_self: Ident::new("self", proc_macro2::Span::call_site()),
});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You construct a &self parameter here, but what is this used for?
And how do you know it's not &mut self?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an attempt to simplify func.rs's get_signature_info method. Now there's no case where both gd_self being true and the first argument being &self or &mut self can conflict set receiver_type twice.
I don't love this, and could also go back and just add a panic! to get_signature_info if the first argument is &self.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thinking on it again, there's already a panic! in get_signature_info.
I think it makes sense to inelegantly handle the case where gd_self is true and the first argument is &self in get_signature_info with a panic! as a fallback. And then have process_godot_fns check for that case and emit a prettier looking bail error instead.

I'll upload a separate commit that switches to that, so it'll be easier to go with which you prefer.

godot-macros/src/class/godot_api.rs Outdated Show resolved Hide resolved
@mhoff12358 mhoff12358 force-pushed the mhoff12358/gd_as_self_argument branch 4 times, most recently from c2b9dfb to 94f4866 Compare September 21, 2023 05:28
@mhoff12358 mhoff12358 force-pushed the mhoff12358/gd_as_self_argument branch 2 times, most recently from 9853ffc to cd921c7 Compare September 22, 2023 06:02
@mhoff12358
Copy link
Contributor Author

The test case that triggers the error seems to cause the integration test runner to fail on any non-windows machine. Even though every individual test passes.
It's probably better to just delete the case that verifies that an error occurs? It makes it more clear that this is actually fixing the issue, but isn't strictly necessary to catch any future regressions.

@Bromeon
Copy link
Member

Bromeon commented Sep 22, 2023

CI checks for the string SCRIPT ERROR: in the stderr output and aborts immediately if it sees that. It's a bit curious that this doesn't happen on Windows though...

What you could try is explicitly disable type annotations by using Variant:

var gd_self_reference: Variant
# or just:
var gd_self_reference

instead of:

var gd_self_reference: GdSelfReference

This should move the error from parse time to runtime.

@Bromeon
Copy link
Member

Bromeon commented Sep 30, 2023

@mhoff12358 Do you think you'll have time to update this over the next few days? 🙂

@mhoff12358
Copy link
Contributor Author

mhoff12358 commented Sep 30, 2023 via email

@mhoff12358 mhoff12358 force-pushed the mhoff12358/gd_as_self_argument branch 2 times, most recently from 3d71798 to d33813c Compare October 1, 2023 13:55
This is done by tagging the function with #[func(gd_self)].
@mhoff12358 mhoff12358 force-pushed the mhoff12358/gd_as_self_argument branch from d33813c to c28de15 Compare October 1, 2023 15:01
@mhoff12358
Copy link
Contributor Author

CI checks for the string SCRIPT ERROR: in the stderr output and aborts immediately if it sees that. It's a bit curious that this doesn't happen on Windows though...

What you could try is explicitly disable type annotations by using Variant:

var gd_self_reference: Variant
# or just:
var gd_self_reference

instead of:

var gd_self_reference: GdSelfReference

This should move the error from parse time to runtime.

This actually is a runtime error. Godot spits out "SCRIPT ERROR: Invalid call. Nonexistent function 'function_name' in base 'RustClass'." at runtime if function_name does exist but panics inside rust.

I don't see any easy way to do something that would assert for that error and prevent it from causing a real failure, so I commented out the test with a todo to re-add it if someone does build a way to depend on that kind of failure.

It looks like tests are passing, other than an issue with credentials in the docs test. (They're currently rerunning because I rebased to not have an unnecessary extra commit).

Were there any other outstanding issues?

Copy link
Member

@Bromeon Bromeon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! The docs is something I have to address separately, but it's not part of the main CI.

@Bromeon Bromeon added this pull request to the merge queue Oct 1, 2023
Merged via the queue into godot-rust:master with commit c3e7023 Oct 1, 2023
13 of 14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
c: register Register classes, functions and other symbols to GDScript feature Adds functionality to the library
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants