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

in #[var]s, handle renamed #[func]s #1019

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

0x53A
Copy link
Contributor

@0x53A 0x53A commented Jan 20, 2025

closes #863

@0x53A 0x53A force-pushed the dev-handle-func-rename branch from 7d0f540 to 6695a62 Compare January 20, 2025 02:33
@GodotRust
Copy link

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

@0x53A 0x53A force-pushed the dev-handle-func-rename branch from 6695a62 to 56e5be7 Compare January 20, 2025 02:35
@0x53A 0x53A force-pushed the dev-handle-func-rename branch from 56e5be7 to e8a3c98 Compare January 20, 2025 02:39
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! 👍

Some of it may clash with a similar thing I did in #1000, but this is nicely isolated, and I can deal with the conflicts later.

godot-macros/src/class/data_models/inherent_impl.rs Outdated Show resolved Hide resolved
godot-macros/src/class/data_models/inherent_impl.rs Outdated Show resolved Hide resolved
@@ -200,10 +201,14 @@ impl GetterSetterImpl {
}
}

let constant_declaration =
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
let constant_declaration =
let func_name_constant =

To make clear that it's not a user-defined #[constant].

@@ -84,6 +86,8 @@ pub fn transform_inherent_impl(
let (funcs, signals) = process_godot_fns(&class_name, &mut impl_block, meta.secondary)?;
let consts = process_godot_constants(&mut impl_block)?;

let func_export_name_constants = make_function_registered_name_constants(&funcs, &class_name);
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
let func_export_name_constants = make_function_registered_name_constants(&funcs, &class_name);
let func_name_constants = make_function_registered_name_constants(&funcs, &class_name);

"export" has a very specific meaning; using it here is probably confusing.

Copy link
Contributor Author

@0x53A 0x53A Jan 21, 2025

Choose a reason for hiding this comment

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

it was also not consistent with the function name. I'd propose standardizing on "registered" function name, at least that's what it's called in other places in the code.

Copy link
Member

Choose a reason for hiding this comment

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

It's fine if the local variables are more terse/concise than the function names. Context matters, and if you see the

let func_name_constants = make_function_registered_name_constants(...);

once, then you know what func_name_constants refers to. I think using overly long variable names makes expressions harder to read; the "registered" doesn't really add useful information to understand the codegen.

There's also the point of scope: global function names are visible in their entire module, while variable names are limited to the block/function in which they are declared. It's much easier to mentally keep track of a block, than it is of a module; and there's less potential for collisions.

godot-macros/src/class/data_models/inherent_impl.rs Outdated Show resolved Hide resolved
godot-macros/src/util/mod.rs Show resolved Hide resolved
Comment on lines 352 to 412
pub fn format_function_registered_name_constant(class_name: &Ident, func_name: &Ident) -> Ident {
format_ident!("__gdext_func_{}_{}", class_name, func_name)
}
Copy link
Member

Choose a reason for hiding this comment

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

I'd use consistent naming, that makes it obvious you're referring to the same thing.
Above I suggested func_name_constant, maybe that can be done here, too?

Suggested change
pub fn format_function_registered_name_constant(class_name: &Ident, func_name: &Ident) -> Ident {
format_ident!("__gdext_func_{}_{}", class_name, func_name)
}
pub fn format_func_name_constant(class_name: &Ident, func_name: &Ident) -> Ident {
format_ident!("__gdext_func_{class_name}_{func_name}")
}

itest/rust/src/object_tests/property_test.rs Show resolved Hide resolved
@0x53A
Copy link
Contributor Author

0x53A commented Jan 21, 2025

proposal: to not pollute the struct namespace with all these constants, I could create a dummy struct.

Currently the following code is generated:

// #[derive(GodotClass)]

impl O {
    pub fn get_int_val(&self) -> <i32 as ::godot::meta::GodotConvert>::Via {
        <i32 as ::godot::register::property::Var>::get_property(&self.int_val)
    }
    #[doc(hidden)]
    #[allow(non_upper_case_globals)]
    pub const __gdext_func_O_get_int_val: &str = "get_int_val";
    pub fn set_int_val(&mut self, int_val: <i32 as ::godot::meta::GodotConvert>::Via) {
        <i32 as ::godot::register::property::Var>::set_property(&mut self.int_val, int_val);
    }
    #[doc(hidden)]
    #[allow(non_upper_case_globals)]
    pub const __gdext_func_O_set_int_val: &str = "set_int_val";
}


// #[godot_api]

impl O {
    #[doc(hidden)]
    #[allow(non_upper_case_globals)]
    pub const __gdext_func_O_foo: &str = "f1";
    #[doc(hidden)]
    #[allow(non_upper_case_globals)]
    pub const __gdext_func_O_bar: &str = "f2";
}

I would propose the following:

// #[derive(GodotClass)]

impl O {
    pub fn get_int_val(&self) -> <i32 as ::godot::meta::GodotConvert>::Via {
        <i32 as ::godot::register::property::Var>::get_property(&self.int_val)
    }
    pub fn set_int_val(&mut self, int_val: <i32 as ::godot::meta::GodotConvert>::Via) {
        <i32 as ::godot::register::property::Var>::set_property(&mut self.int_val, int_val);
    }
}

#[doc(hidden)]
struct O_Functions { }

impl O_Functions {
    #[doc(hidden)]
    #[allow(non_upper_case_globals)]
    pub const __gdext_func_O_set_int_val: &str = "set_int_val";

    #[doc(hidden)]
    #[allow(non_upper_case_globals)]
    pub const __gdext_func_O_get_int_val: &str = "get_int_val";
}

// #[godot_api]

impl O_Functions {
    #[doc(hidden)]
    #[allow(non_upper_case_globals)]
    pub const __gdext_func_O_foo: &str = "f1";
    #[doc(hidden)]
    #[allow(non_upper_case_globals)]
    pub const __gdext_func_O_bar: &str = "f2";
}

The dummy struct is required instead of a mod so that we can contribute to the same namespace from multiple places.

@0x53A
Copy link
Contributor Author

0x53A commented Jan 21, 2025

alright no more time for right now, I need to go.

I'll look through it one more time when I'm back, then I'll rebase-squash.

Todo:

  • make sure naming is sane and consistent
  • simplify the path handling in inherent_impl.rs
  • this failure: linux-memcheck?
=================================================================
==3088==ERROR: LeakSanitizer: detected memory leaks

Direct leak of 232 byte(s) in 1 object(s) allocated from:
    #0 0x55ba3602b62e in malloc (/home/runner/work/_temp/godot_bin/godot.linuxbsd.editor.dev.x86_64.llvm.san+0x21c9c62e) (BuildId: 874db2a84ccfa9b3)
    #1 0x55ba55e654e6 in Memory::alloc_static(unsigned long, bool) /home/runner/work/godot4-nightly/godot4-nightly/core/os/memory.cpp:106:14
    #2 0x55ba55e65362 in operator new(unsigned long, char const*) /home/runner/work/godot4-nightly/godot4-nightly/core/os/memory.cpp:39:9
    #3 0x55ba58953974 in GDExtension::_register_extension_class_method(void*, void const*, GDExtensionClassMethodInfo const*) /home/runner/work/godot4-nightly/godot4-nightly/core/extension/gdextension.cpp:509:12
    #4 0x7f5c738209e0 in godot_core::registry::method::ClassMethodInfo::register_nonvirtual_class_method::h6f44d6b7c80457d4 /home/runner/work/gdext/gdext/godot-core/src/registry/method.rs:157:13
    #5 0x7f5c7381fff8 in godot_core::registry::method::ClassMethodInfo::register_extension_class_method::hfb75fa1a2544ea5f /home/runner/work/gdext/gdext/godot-core/src/registry/method.rs:147:13
    #6 0x7f5c730ae1e2 in itest::register_tests::func_test::_::__init::__inner_init::_$u7b$$u7b$closure$u7d$$u7d$::h9ae97532f88693d3 /home/runner/work/gdext/gdext/itest/rust/src/register_tests/func_test.rs:79:1
    #7 0x7f5c72dbfffd in core::ops::function::FnOnce::call_once::h0a8edd5c08646abf /rustc/f3d1d47fd84dfcf7f513be1dbad356e74c8f3b2b/library/core/src/ops/function.rs:250:5
    #8 0x7f5c72c230a2 in godot_core::registry::callbacks::register_user_methods_constants::ha0604185de96699d /home/runner/work/gdext/gdext/godot-core/src/registry/callbacks.rs:392:5
    #9 0x7f5c7381756d in godot_core::registry::class::auto_register_classes::h71cfbbdffdad7470 /home/runner/work/gdext/gdext/godot-core/src/registry/class.rs:228:9
    #10 0x7f5c7386886c in godot_core::init::gdext_on_level_init::h1d4172dd01e950e9 /home/runner/work/gdext/gdext/godot-core/src/init/mod.rs:161:5
    #11 0x7f5c72fd3a5a in godot_core::init::ffi_initialize_layer::_$u7b$$u7b$closure$u7d$$u7d$::hdf873678b0f13b97 /home/runner/work/gdext/gdext/godot-core/src/init/mod.rs:107:9
    #12 0x7f5c71290641 in godot_core::private::handle_panic_with_print::h4b774d0a828afef5 /home/runner/work/gdext/gdext/godot-core/src/private.rs:377:17
    #13 0x7f5c70cf8878 in godot_core::private::handle_panic::h129b9f0e49e1359a /home/runner/work/gdext/gdext/godot-core/src/private.rs:275:5
    #14 0x55ba5895bbdd in GDExtension::initialize_library(GDExtension::InitializationLevel) /home/runner/work/godot4-nightly/godot4-nightly/core/extension/gdextension.cpp:745:2
    #15 0x55ba589f5fb1 in GDExtensionManager::initialize_extensions(GDExtension::InitializationLevel) /home/runner/work/godot4-nightly/godot4-nightly/core/extension/gdextension_manager.cpp:228:12
    #16 0x55ba364c1f2b in Main::setup2(bool) /home/runner/work/godot4-nightly/godot4-nightly/main/main.cpp:3381:40
    #17 0x55ba364aed45 in Main::setup(char const*, int, char**, bool) /home/runner/work/godot4-nightly/godot4-nightly/main/main.cpp:2676:14
    #18 0x55ba36068f93 in main /home/runner/work/godot4-nightly/godot4-nightly/platform/linuxbsd/godot_linuxbsd.cpp:74:14
    #19 0x7f5c7be29d8f  (/lib/x86_64-linux-gnu/libc.so.6+0x29d8f) (BuildId: 490fef8403240c91833978d494d39e537409b92e)

Direct leak of 232 byte(s) in 1 object(s) allocated from:
    #0 0x55ba3602b62e in malloc (/home/runner/work/_temp/godot_bin/godot.linuxbsd.editor.dev.x86_64.llvm.san+0x21c9c62e) (BuildId: 874db2a84ccfa9b3)
    #1 0x55ba55e654e6 in Memory::alloc_static(unsigned long, bool) /home/runner/work/godot4-nightly/godot4-nightly/core/os/memory.cpp:106:14
    #2 0x55ba55e65362 in operator new(unsigned long, char const*) /home/runner/work/godot4-nightly/godot4-nightly/core/os/memory.cpp:39:9
    #3 0x55ba58953974 in GDExtension::_register_extension_class_method(void*, void const*, GDExtensionClassMethodInfo const*) /home/runner/work/godot4-nightly/godot4-nightly/core/extension/gdextension.cpp:509:12
    #4 0x7f5c738209e0 in godot_core::registry::method::ClassMethodInfo::register_nonvirtual_class_method::h6f44d6b7c80457d4 /home/runner/work/gdext/gdext/godot-core/src/registry/method.rs:157:13
    #5 0x7f5c7381fff8 in godot_core::registry::method::ClassMethodInfo::register_extension_class_method::hfb75fa1a2544ea5f /home/runner/work/gdext/gdext/godot-core/src/registry/method.rs:147:13
    #6 0x7f5c730ae00f in itest::register_tests::func_test::_::__init::__inner_init::_$u7b$$u7b$closure$u7d$$u7d$::h9ae97532f88693d3 /home/runner/work/gdext/gdext/itest/rust/src/register_tests/func_test.rs:79:1
    #7 0x7f5c72dbfffd in core::ops::function::FnOnce::call_once::h0a8edd5c08646abf /rustc/f3d1d47fd84dfcf7f513be1dbad356e74c8f3b2b/library/core/src/ops/function.rs:250:5
    #8 0x7f5c72c230a2 in godot_core::registry::callbacks::register_user_methods_constants::ha0604185de96699d /home/runner/work/gdext/gdext/godot-core/src/registry/callbacks.rs:392:5
    #9 0x7f5c7381756d in godot_core::registry::class::auto_register_classes::h71cfbbdffdad7470 /home/runner/work/gdext/gdext/godot-core/src/registry/class.rs:228:9
    #10 0x7f5c7386886c in godot_core::init::gdext_on_level_init::h1d4172dd01e950e9 /home/runner/work/gdext/gdext/godot-core/src/init/mod.rs:161:5
    #11 0x7f5c72fd3a5a in godot_core::init::ffi_initialize_layer::_$u7b$$u7b$closure$u7d$$u7d$::hdf873678b0f13b97 /home/runner/work/gdext/gdext/godot-core/src/init/mod.rs:107:9
    #12 0x7f5c71290641 in godot_core::private::handle_panic_with_print::h4b774d0a828afef5 /home/runner/work/gdext/gdext/godot-core/src/private.rs:377:17
    #13 0x7f5c70cf8878 in godot_core::private::handle_panic::h129b9f0e49e1359a /home/runner/work/gdext/gdext/godot-core/src/private.rs:275:5
    #14 0x55ba5895bbdd in GDExtension::initialize_library(GDExtension::InitializationLevel) /home/runner/work/godot4-nightly/godot4-nightly/core/extension/gdextension.cpp:745:2
    #15 0x55ba589f5fb1 in GDExtensionManager::initialize_extensions(GDExtension::InitializationLevel) /home/runner/work/godot4-nightly/godot4-nightly/core/extension/gdextension_manager.cpp:228:12
    #16 0x55ba364c1f2b in Main::setup2(bool) /home/runner/work/godot4-nightly/godot4-nightly/main/main.cpp:3381:40
    #17 0x55ba364aed45 in Main::setup(char const*, int, char**, bool) /home/runner/work/godot4-nightly/godot4-nightly/main/main.cpp:2676:14
    #18 0x55ba36068f93 in main /home/runner/work/godot4-nightly/godot4-nightly/platform/linuxbsd/godot_linuxbsd.cpp:74:14
    #19 0x7f5c7be29d8f  (/lib/x86_64-linux-gnu/libc.so.6+0x29d8f) (BuildId: 490fef8403240c91833978d494d39e537409b92e)

Indirect leak of 20 byte(s) in 1 object(s) allocated from:
    #0 0x55ba3602b62e in malloc (/home/runner/work/_temp/godot_bin/godot.linuxbsd.editor.dev.x86_64.llvm.san+0x21c9c62e) (BuildId: 874db2a84ccfa9b3)
    #1 0x55ba55e654e6 in Memory::alloc_static(unsigned long, bool) /home/runner/work/godot4-nightly/godot4-nightly/core/os/memory.cpp:106:14
    #2 0x55ba3957bf2d in Variant::Type* memnew_arr_template<Variant::Type>(unsigned long) /home/runner/work/godot4-nightly/godot4-nightly/./core/os/memory.h:180:28
    #3 0x55ba58b24fc5 in MethodBind::_generate_argument_types(int) /home/runner/work/godot4-nightly/godot4-nightly/core/object/method_bind.cpp:104:24
    #4 0x55ba5898964e in GDExtensionMethodBind::update(GDExtensionClassMethodInfo const*) /home/runner/work/godot4-nightly/godot4-nightly/core/extension/gdextension.cpp:223:3
    #5 0x55ba5896bf65 in GDExtensionMethodBind::GDExtensionMethodBind(GDExtensionClassMethodInfo const*) /home/runner/work/godot4-nightly/godot4-nightly/core/extension/gdextension.cpp:237:3
    #6 0x55ba589539b7 in GDExtension::_register_extension_class_method(void*, void const*, GDExtensionClassMethodInfo const*) /home/runner/work/godot4-nightly/godot4-nightly/core/extension/gdextension.cpp:509:12
    #7 0x7f5c738209e0 in godot_core::registry::method::ClassMethodInfo::register_nonvirtual_class_method::h6f44d6b7c80457d4 /home/runner/work/gdext/gdext/godot-core/src/registry/method.rs:157:13
    #8 0x7f5c7381fff8 in godot_core::registry::method::ClassMethodInfo::register_extension_class_method::hfb75fa1a2544ea5f /home/runner/work/gdext/gdext/godot-core/src/registry/method.rs:147:13
    #9 0x7f5c730ae1e2 in itest::register_tests::func_test::_::__init::__inner_init::_$u7b$$u7b$closure$u7d$$u7d$::h9ae97532f88693d3 /home/runner/work/gdext/gdext/itest/rust/src/register_tests/func_test.rs:79:1
    #10 0x7f5c72dbfffd in core::ops::function::FnOnce::call_once::h0a8edd5c08646abf /rustc/f3d1d47fd84dfcf7f513be1dbad356e74c8f3b2b/library/core/src/ops/function.rs:250:5
    #11 0x7f5c72c230a2 in godot_core::registry::callbacks::register_user_methods_constants::ha0604185de96699d /home/runner/work/gdext/gdext/godot-core/src/registry/callbacks.rs:392:5
    #12 0x7f5c7381756d in godot_core::registry::class::auto_register_classes::h71cfbbdffdad7470 /home/runner/work/gdext/gdext/godot-core/src/registry/class.rs:228:9
    #13 0x7f5c7386886c in godot_core::init::gdext_on_level_init::h1d4172dd01e950e9 /home/runner/work/gdext/gdext/godot-core/src/init/mod.rs:161:5
    #14 0x7f5c72fd3a5a in godot_core::init::ffi_initialize_layer::_$u7b$$u7b$closure$u7d$$u7d$::hdf873678b0f13b97 /home/runner/work/gdext/gdext/godot-core/src/init/mod.rs:107:9
    #15 0x7f5c71290641 in godot_core::private::handle_panic_with_print::h4b774d0a828afef5 /home/runner/work/gdext/gdext/godot-core/src/private.rs:377:17
    #16 0x7f5c70cf8878 in godot_core::private::handle_panic::h129b9f0e49e1359a /home/runner/work/gdext/gdext/godot-core/src/private.rs:275:5
    #17 0x55ba5895bbdd in GDExtension::initialize_library(GDExtension::InitializationLevel) /home/runner/work/godot4-nightly/godot4-nightly/core/extension/gdextension.cpp:745:2
    #18 0x55ba589f5fb1 in GDExtensionManager::initialize_extensions(GDExtension::InitializationLevel) /home/runner/work/godot4-nightly/godot4-nightly/core/extension/gdextension_manager.cpp:228:12
    #19 0x55ba364c1f2b in Main::setup2(bool) /home/runner/work/godot4-nightly/godot4-nightly/main/main.cpp:3381:40
    #20 0x55ba364aed45 in Main::setup(char const*, int, char**, bool) /home/runner/work/godot4-nightly/godot4-nightly/main/main.cpp:2676:14
    #21 0x55ba36068f93 in main /home/runner/work/godot4-nightly/godot4-nightly/platform/linuxbsd/godot_linuxbsd.cpp:74:14
    #22 0x7f5c7be29d8f  (/lib/x86_64-linux-gnu/libc.so.6+0x29d8f) (BuildId: 490fef8403240c91833978d494d39e537409b92e)

Indirect leak of 20 byte(s) in 1 object(s) allocated from:
    #0 0x55ba3602b62e in malloc (/home/runner/work/_temp/godot_bin/godot.linuxbsd.editor.dev.x86_64.llvm.san+0x21c9c62e) (BuildId: 874db2a84ccfa9b3)
    #1 0x55ba55e654e6 in Memory::alloc_static(unsigned long, bool) /home/runner/work/godot4-nightly/godot4-nightly/core/os/memory.cpp:106:14
    #2 0x55ba3957bf2d in Variant::Type* memnew_arr_template<Variant::Type>(unsigned long) /home/runner/work/godot4-nightly/godot4-nightly/./core/os/memory.h:180:28
    #3 0x55ba58b24fc5 in MethodBind::_generate_argument_types(int) /home/runner/work/godot4-nightly/godot4-nightly/core/object/method_bind.cpp:104:24
    #4 0x55ba5898964e in GDExtensionMethodBind::update(GDExtensionClassMethodInfo const*) /home/runner/work/godot4-nightly/godot4-nightly/core/extension/gdextension.cpp:223:3
    #5 0x55ba5896bf65 in GDExtensionMethodBind::GDExtensionMethodBind(GDExtensionClassMethodInfo const*) /home/runner/work/godot4-nightly/godot4-nightly/core/extension/gdextension.cpp:237:3
    #6 0x55ba589539b7 in GDExtension::_register_extension_class_method(void*, void const*, GDExtensionClassMethodInfo const*) /home/runner/work/godot4-nightly/godot4-nightly/core/extension/gdextension.cpp:509:12
    #7 0x7f5c738209e0 in godot_core::registry::method::ClassMethodInfo::register_nonvirtual_class_method::h6f44d6b7c80457d4 /home/runner/work/gdext/gdext/godot-core/src/registry/method.rs:157:13
    #8 0x7f5c7381fff8 in godot_core::registry::method::ClassMethodInfo::register_extension_class_method::hfb75fa1a2544ea5f /home/runner/work/gdext/gdext/godot-core/src/registry/method.rs:147:13
    #9 0x7f5c730ae00f in itest::register_tests::func_test::_::__init::__inner_init::_$u7b$$u7b$closure$u7d$$u7d$::h9ae97532f88693d3 /home/runner/work/gdext/gdext/itest/rust/src/register_tests/func_test.rs:79:1
    #10 0x7f5c72dbfffd in core::ops::function::FnOnce::call_once::h0a8edd5c08646abf /rustc/f3d1d47fd84dfcf7f513be1dbad356e74c8f3b2b/library/core/src/ops/function.rs:250:5
    #11 0x7f5c72c230a2 in godot_core::registry::callbacks::register_user_methods_constants::ha0604185de96699d /home/runner/work/gdext/gdext/godot-core/src/registry/callbacks.rs:392:5
    #12 0x7f5c738[1756](https://github.com/godot-rust/gdext/actions/runs/12881698201/job/35912681146#step:3:1772)d in godot_core::registry::class::auto_register_classes::h71cfbbdffdad7470 /home/runner/work/gdext/gdext/godot-core/src/registry/class.rs:228:9
    #13 0x7f5c7386886c in godot_core::init::gdext_on_level_init::h1d4172dd01e950e9 /home/runner/work/gdext/gdext/godot-core/src/init/mod.rs:161:5
    #14 0x7f5c72fd3a5a in godot_core::init::ffi_initialize_layer::_$u7b$$u7b$closure$u7d$$u7d$::hdf873678b0f13b97 /home/runner/work/gdext/gdext/godot-core/src/init/mod.rs:107:9
    #15 0x7f5c71290641 in godot_core::private::handle_panic_with_print::h4b774d0a828afef5 /home/runner/work/gdext/gdext/godot-core/src/private.rs:377:17
    #16 0x7f5c70cf8878 in godot_core::private::handle_panic::h129b9f0e49e1359a /home/runner/work/gdext/gdext/godot-core/src/private.rs:275:5
    #17 0x55ba5895bbdd in GDExtension::initialize_library(GDExtension::InitializationLevel) /home/runner/work/godot4-nightly/godot4-nightly/core/extension/gdextension.cpp:745:2
    #18 0x55ba589f5fb1 in GDExtensionManager::initialize_extensions(GDExtension::InitializationLevel) /home/runner/work/godot4-nightly/godot4-nightly/core/extension/gdextension_manager.cpp:228:12
    #19 0x55ba364c1f2b in Main::setup2(bool) /home/runner/work/godot4-nightly/godot4-nightly/main/main.cpp:3381:40
    #20 0x55ba364aed45 in Main::setup(char const*, int, char**, bool) /home/runner/work/godot4-nightly/godot4-nightly/main/main.cpp:2676:14
    #21 0x55ba36068f93 in main /home/runner/work/godot4-nightly/godot4-nightly/platform/linuxbsd/godot_linuxbsd.cpp:74:14
    #22 0x7f5c7be29d8f  (/lib/x86_64-linux-gnu/libc.so.6+0x29d8f) (BuildId: 490fef8403240c91833978d494d39e537409b92e)

SUMMARY: AddressSanitizer: 504 byte(s) leaked in 4 allocation(s).

@0x53A
Copy link
Contributor Author

0x53A commented Jan 21, 2025

regarding the memory leak,

the memory is allocated here: https://github.com/godotengine/godot/blob/1b7b009674e05b566be11a5377b935f5d3d6c0ee/core/extension/gdextension.cpp#L509

and I could see it being leaked, if it returns with an error here, in case the method name is duplicated: https://github.com/godotengine/godot/blob/master/core/object/class_db.cpp#L1881

why would the method name be duplicated? no idea

@0x53A
Copy link
Contributor Author

0x53A commented Jan 21, 2025

After looking into this, I believe it's a bug in godot: godotengine/godot#101870

It's triggered because in mod.rs#244, it doesn't handle cfg_attr

pub(crate) fn extract_cfg_attrs(
    attrs: &[venial::Attribute],
) -> impl IntoIterator<Item = &venial::Attribute> {
    attrs.iter().filter(|attr| {
        attr.get_single_path_segment()
            .is_some_and(|name| name == "cfg")
    })
}

and I added a test case that uses cfg_attr, therefore the method is registered multiple times with godot, hitting the memory leak there

@0x53A 0x53A changed the title in #[var]s, handle renamed #[func]s [WIP] in #[var]s, handle renamed #[func]s Jan 21, 2025
@Bromeon
Copy link
Member

Bromeon commented Jan 21, 2025

Quick tip: instead of renaming the title to [WIP], you can mark the PR as a draft. This also prevents accidental merging.

@Bromeon Bromeon added bug c: register Register classes, functions and other symbols to GDScript labels Jan 21, 2025
@0x53A 0x53A marked this pull request as draft January 21, 2025 09:25
@@ -84,6 +86,8 @@ pub fn transform_inherent_impl(
let (funcs, signals) = process_godot_fns(&class_name, &mut impl_block, meta.secondary)?;
let consts = process_godot_constants(&mut impl_block)?;

let func_export_name_constants = make_function_registered_name_constants(&funcs, &class_name);
Copy link
Member

Choose a reason for hiding this comment

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

It's fine if the local variables are more terse/concise than the function names. Context matters, and if you see the

let func_name_constants = make_function_registered_name_constants(...);

once, then you know what func_name_constants refers to. I think using overly long variable names makes expressions harder to read; the "registered" doesn't really add useful information to understand the codegen.

There's also the point of scope: global function names are visible in their entire module, while variable names are limited to the block/function in which they are declared. It's much easier to mentally keep track of a block, than it is of a module; and there's less potential for collisions.

@@ -119,6 +125,32 @@ pub fn transform_inherent_impl(
});
};

let class_functions_name = format_function_registered_name_struct_name(&class_name);
let class_functions_path: Path = match impl_block.self_ty.as_path().unwrap().segments.as_slice()
Copy link
Member

Choose a reason for hiding this comment

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

The way you use unwrap here indicates that it's infallible. In general, this would need a brief comment documenting why.

Here, it might be better to pass in previously evaluated values via parameters, making the extraction unnecessary. This especially holds true here since as_path() isn't cheap to compute, even less so with some upcoming validations in venial.

Might also make sense to extract the match expression into its own variable 🙂

godot-macros/src/class/data_models/inherent_impl.rs Outdated Show resolved Hide resolved
godot-macros/src/util/mod.rs Outdated Show resolved Hide resolved
godot-macros/src/util/mod.rs Outdated Show resolved Hide resolved
godot-macros/src/util/mod.rs Outdated Show resolved Hide resolved
godot-macros/src/util/mod.rs Show resolved Hide resolved
godot-macros/src/util/mod.rs Outdated Show resolved Hide resolved
itest/godot/ManualFfiTests.gd Show resolved Hide resolved
itest/godot/ManualFfiTests.gd Outdated Show resolved Hide resolved
@Bromeon
Copy link
Member

Bromeon commented Jan 21, 2025

proposal: to not pollute the struct namespace with all these constants, I could create a dummy struct.

That sounds like a good idea!
It could be called {ClassName}_Funcs (to express that it contains the #[func] data).

@lilizoey
Copy link
Member

proposal: to not pollute the struct namespace with all these constants, I could create a dummy struct.

That sounds like a good idea! It could be called {ClassName}_Funcs (to express that it contains the #[func] data).

i feel like it should be prefixed with __gdext or something too, i think it'd be very unlikely for {ClassName}_Funcs to conflict with anything, but it's more to be on the safe side.

@Bromeon
Copy link
Member

Bromeon commented Jan 23, 2025

i feel like it should be prefixed with __gdext or something too

Good point, in that case we could maybe even drop the prefix of the function constants themselves, as they're now scoped.

@0x53A
Copy link
Contributor Author

0x53A commented Jan 23, 2025

It's already implemented, the naming patterns are here:

/// Returns the name of the constant that will be autogenerated.
pub fn format_function_registered_name_constant_name(
class_name: &Ident,
func_name: &Ident,
) -> Ident {
format_ident!("__gdext_func_{}_{}", class_name, func_name)
}
/// Returns the name of the dummy struct that's used as container for all function name constants.
pub fn format_function_registered_name_struct_name(class_name: &Ident) -> Ident {
format_ident!("{class_name}_Functions")
}

Functionally, the PR is done, I just need to implement all the feedback and simplify two places I've hacked together.

@0x53A 0x53A force-pushed the dev-handle-func-rename branch from 0965b33 to 7e6fb7a Compare January 27, 2025 00:48
@0x53A 0x53A force-pushed the dev-handle-func-rename branch from 7e6fb7a to 74fab79 Compare January 27, 2025 00:49
@0x53A 0x53A changed the title [WIP] in #[var]s, handle renamed #[func]s in #[var]s, handle renamed #[func]s Jan 27, 2025
@0x53A 0x53A marked this pull request as ready for review January 27, 2025 00:50
@0x53A
Copy link
Contributor Author

0x53A commented Jan 27, 2025

alright took a little more time but should now be ready.

I hope I caught all the review comments w.r.t. naming.

It copies over both #[cfg] and #[cfg_attr(condition, attrs)] if attrs contains cfg.

As mentioned before, it generates the code below.

#[derive(GodotClass)]
#[class(base=Node, init)]
struct RenamedFunc {
    #[var(get, set)]
    int_val: i32,
}

#[godot_api]
impl RenamedFunc {
    #[func(rename=f1)]
    pub fn get_int_val(&self) -> i32 {
        self.int_val
    }

    #[func(rename=f2)]
    pub fn set_int_val(&mut self, val: i32) {
        self.int_val = val;
    }
}
// Recursive expansion of GodotClass macro
// ========================================

#[doc(hidden)]
pub struct __gdext_RenamedFunc_Functions {}

// ...

impl __gdext_RenamedFunc_Functions {
    #[doc = "The Rust function `get_int_val` is registered with Godot as `get_int_val`."]
    #[doc(hidden)]
    #[allow(non_upper_case_globals)]
    pub const get_int_val: &str = "get_int_val";
    #[doc = "The Rust function `set_int_val` is registered with Godot as `set_int_val`."]
    #[doc(hidden)]
    #[allow(non_upper_case_globals)]
    pub const set_int_val: &str = "set_int_val";
}

// ...


// Recursive expansion of godot_api macro
// =======================================

// ...

impl __gdext_RenamedFunc_Functions {
    #[doc = "The Rust function `get_int_val` is registered with Godot as `f1`."]
    #[doc(hidden)]
    #[allow(non_upper_case_globals)]
    pub const get_int_val: &str = "f1";
    #[doc = "The Rust function `set_int_val` is registered with Godot as `f2`."]
    #[doc(hidden)]
    #[allow(non_upper_case_globals)]
    pub const set_int_val: &str = "f2";
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug c: register Register classes, functions and other symbols to GDScript
Projects
None yet
Development

Successfully merging this pull request may close these issues.

#[var(set = function_name)] is not compatible with #[func(rename = gdscript_name)]
5 participants