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

fix: ambiguous virtual method calls #1020

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

Conversation

adalinesimonian
Copy link

@adalinesimonian adalinesimonian commented Jan 20, 2025

Fixes #858

This issue was a great way to start getting introduced to the internals of this library. I'm still figuring out exactly what does what, and some of the libraries in use are also new to me. So I am not 100% of if this fix is entirely proper or not. For example, as far as I can tell, the __before_<method> function is really only ever __before_ready and is never a member of any Godot API trait or any type that would need it to be qualified. I'm also still wrapping my head around the web of different modules and functions and types. I would appreciate some guidance.

Pre-macro example
use godot::prelude::*;

struct MyExtension;

#[gdextension]
unsafe impl ExtensionLibrary for MyExtension {}

use godot::classes::{ISprite2D, InputEvent, Sprite2D};
use godot::prelude::*;

#[derive(GodotClass)]
#[class(base=Sprite2D)]
pub struct Player {
    base: Base<Sprite2D>,
    scene: Gd<PackedScene>,
    music: Option<Gd<AudioStreamPlayer>>,
    sound: Option<Gd<AudioStreamPlayer>>,
    number: i64,
}

#[godot_api]
impl ISprite2D for Player {
    fn init(base: Base<Sprite2D>) -> Self {
        Self {
            base,
            scene: PackedScene::new_gd(),
            music: None,
            sound: None,
            number: 0,
        }
    }

    fn process(&mut self, delta: f64) {
        godot_print!("Processing Player {:?}", delta);
    }

    fn draw(&mut self) {
        // manually draw some stuff, just for testing
        let colour = Color::from_rgb(1.0, 0.0, 0.0);

        self.base_mut()
            .draw_line_ex(Vector2::new(0.0, 0.0), Vector2::new(100.0, 100.0), colour)
            .width(5.0)
            .antialiased(true)
            .done();
    }

    fn ready(&mut self) {
        godot_print!("Player is ready");
    }

    fn input(&mut self, event: Gd<InputEvent>) {
        godot_print!("Player input {:?}", event);
    }
}

#[godot_api]
impl Player {
    fn some_weird_func() {
        godot_print!("Some weird func");
    }
}
trait TraitA {
    fn process(&mut self, delta: f64);
}

impl TraitA for Player {
    fn process(&mut self, delta: f64) {}
}
Expanded code before this change
#![feature(prelude_import)]
#[prelude_import]
use std::prelude::rust_2021::*;
#[macro_use]
extern crate std;
use godot::prelude::*;
struct MyExtension;
unsafe impl ExtensionLibrary for MyExtension {}
#[no_mangle]
unsafe extern "C" fn gdext_rust_init(
    get_proc_address: ::godot::sys::GDExtensionInterfaceGetProcAddress,
    library: ::godot::sys::GDExtensionClassLibraryPtr,
    init: *mut ::godot::sys::GDExtensionInitialization,
) -> ::godot::sys::GDExtensionBool {
    ::godot::init::__gdext_load_library::<MyExtension>(get_proc_address, library, init)
}
fn __static_type_check() {
    let _unused: ::godot::sys::GDExtensionInitializationFunction = Some(gdext_rust_init);
}
use godot::classes::{ISprite2D, InputEvent, Sprite2D};
use godot::prelude::*;
#[class(base = Sprite2D)]
pub struct Player {
    base: Base<Sprite2D>,
    scene: Gd<PackedScene>,
    music: Option<Gd<AudioStreamPlayer>>,
    sound: Option<Gd<AudioStreamPlayer>>,
    number: i64,
}
impl ::godot::obj::GodotClass for Player {
    type Base = ::godot::classes::Sprite2D;
    fn class_name() -> ::godot::meta::ClassName {
        use ::godot::meta::ClassName;
        static CLASS_NAME: std::sync::OnceLock<ClassName> = std::sync::OnceLock::new();
        let name: &'static ClassName = CLASS_NAME
            .get_or_init(|| ClassName::alloc_next_ascii(c"Player"));
        *name
    }
}
unsafe impl ::godot::obj::Bounds for Player {
    type Memory = <<Self as ::godot::obj::GodotClass>::Base as ::godot::obj::Bounds>::Memory;
    type DynMemory = <<Self as ::godot::obj::GodotClass>::Base as ::godot::obj::Bounds>::DynMemory;
    type Declarer = ::godot::obj::bounds::DeclUser;
    type Exportable = <<Self as ::godot::obj::GodotClass>::Base as ::godot::obj::Bounds>::Exportable;
}
impl ::godot::obj::WithBaseField for Player {
    fn to_gd(&self) -> ::godot::obj::Gd<Player> {
        let base = <Player as ::godot::obj::WithBaseField>::base_field(self);
        base.to_gd().cast()
    }
    fn base_field(
        &self,
    ) -> &::godot::obj::Base<<Player as ::godot::obj::GodotClass>::Base> {
        &self.base
    }
}
impl Player {}
impl ::godot::obj::cap::ImplementsGodotExports for Player {
    fn __register_exports() {}
}
impl ::godot::obj::UserClass for Player {
    fn __config() -> ::godot::private::ClassConfig {
        ::godot::private::ClassConfig {
            is_tool: false,
        }
    }
    fn __before_ready(&mut self) {
        ::godot::register::private::auto_register_rpcs::<Player>(self);
    }
}
#[allow(non_snake_case)]
fn class_Player_must_have_an_init_method() {
    fn __type_check<T: ::godot::obj::cap::GodotDefault>() {}
    __type_check::<Player>();
}
const _: () = {
    #[allow(non_upper_case_globals)]
    #[used]
    #[link_section = ".CRT$XCU"]
    static __init: extern "C" fn() = {
        extern "C" fn __inner_init() {
            {
                let mut guard = ::godot::private::__godot_rust_plugin___GODOT_PLUGIN_REGISTRY
                    .lock()
                    .unwrap();
                guard
                    .push(::godot::private::ClassPlugin {
                        class_name: <Player as ::godot::obj::GodotClass>::class_name(),
                        item: ::godot::private::PluginItem::Struct {
                            base_class_name: <::godot::classes::Sprite2D as ::godot::obj::GodotClass>::class_name(),
                            generated_create_fn: None,
                            generated_recreate_fn: None,
                            register_properties_fn: ::godot::private::ErasedRegisterFn {
                                raw: ::godot::private::callbacks::register_user_properties::<
                                    Player,
                                >,
                            },
                            free_fn: ::godot::private::callbacks::free::<Player>,
                            default_get_virtual_fn: None,
                            is_tool: false,
                            is_editor_plugin: false,
                            is_internal: false,
                            is_instantiable: true,
                            docs: None,
                        },
                        init_level: {
                            let level = <Player as ::godot::obj::GodotClass>::INIT_LEVEL;
                            let base_level = <::godot::classes::Sprite2D as ::godot::obj::GodotClass>::INIT_LEVEL;
                            if !(level >= base_level) {
                                {
                                    ::core::panicking::panic_fmt(
                                        format_args!(
                                            "Class `{0}` has init level `{1:?}`, but its base class has init level `{2:?}`.\nA class cannot be registered before its base class.",
                                            "Player",
                                            level,
                                            base_level,
                                        ),
                                    );
                                }
                            }
                            level
                        },
                    });
            }
        }
        __inner_init
    };
};
unsafe impl ::godot::obj::Inherits<::godot::classes::Sprite2D> for Player {}
unsafe impl ::godot::obj::Inherits<::godot::classes::Node2D> for Player {}
unsafe impl ::godot::obj::Inherits<::godot::classes::CanvasItem> for Player {}
unsafe impl ::godot::obj::Inherits<::godot::classes::Node> for Player {}
unsafe impl ::godot::obj::Inherits<::godot::classes::Object> for Player {}
impl ISprite2D for Player {
    fn init(base: Base<Sprite2D>) -> Self {
        Self {
            base,
            scene: PackedScene::new_gd(),
            music: None,
            sound: None,
            number: 0,
        }
    }
    fn process(&mut self, delta: f64) {
        ::godot_core::global::print(
            &[
                ::godot_core::builtin::Variant::from(
                    ::alloc::__export::must_use({
                        let res = ::alloc::fmt::format(
                            format_args!("Processing Player {0:?}", delta),
                        );
                        res
                    }),
                ),
            ],
        );
    }
    fn draw(&mut self) {
        let colour = Color::from_rgb(1.0, 0.0, 0.0);
        self.base_mut()
            .draw_line_ex(Vector2::new(0.0, 0.0), Vector2::new(100.0, 100.0), colour)
            .width(5.0)
            .antialiased(true)
            .done();
    }
    fn ready(&mut self) {
        ::godot_core::global::print(
            &[
                ::godot_core::builtin::Variant::from(
                    ::alloc::__export::must_use({
                        let res = ::alloc::fmt::format(format_args!("Player is ready"));
                        res
                    }),
                ),
            ],
        );
    }
    fn input(&mut self, event: Gd<InputEvent>) {
        ::godot_core::global::print(
            &[
                ::godot_core::builtin::Variant::from(
                    ::alloc::__export::must_use({
                        let res = ::alloc::fmt::format(
                            format_args!("Player input {0:?}", event),
                        );
                        res
                    }),
                ),
            ],
        );
    }
}
impl ::godot::obj::cap::GodotDefault for Player {
    fn __godot_user_init(base: ::godot::obj::Base<Self::Base>) -> Self {
        <Self as ISprite2D>::init(base)
    }
}
impl ::godot::private::You_forgot_the_attribute__godot_api for Player {}
impl ::godot::obj::cap::ImplementsGodotVirtual for Player {
    fn __virtual_call(name: &str) -> ::godot::sys::GDExtensionClassCallVirtual {
        use ::godot::obj::UserClass as _;
        match name {
            "_process" => {
                use ::godot::sys;
                type Sig = ((), f64);
                unsafe extern "C" fn virtual_fn(
                    instance_ptr: sys::GDExtensionClassInstancePtr,
                    args_ptr: *const sys::GDExtensionConstTypePtr,
                    ret: sys::GDExtensionTypePtr,
                ) {
                    let call_ctx = ::godot::meta::CallContext::func("Player", "process");
                    let _success = ::godot::private::handle_ptrcall_panic(
                        &call_ctx,
                        || <Sig as ::godot::meta::PtrcallSignatureTuple>::in_ptrcall(
                            instance_ptr,
                            &call_ctx,
                            args_ptr,
                            ret,
                            |instance_ptr, params| {
                                let (delta,) = params;
                                let storage = unsafe {
                                    ::godot::private::as_storage::<Player>(instance_ptr)
                                };
                                let mut instance = ::godot::private::Storage::get_mut(
                                    storage,
                                );
                                instance.process(delta)
                            },
                            sys::PtrcallType::Virtual,
                        ),
                    );
                }
                Some(virtual_fn)
            }
            "_draw" => {
                use ::godot::sys;
                type Sig = ((),);
                unsafe extern "C" fn virtual_fn(
                    instance_ptr: sys::GDExtensionClassInstancePtr,
                    args_ptr: *const sys::GDExtensionConstTypePtr,
                    ret: sys::GDExtensionTypePtr,
                ) {
                    let call_ctx = ::godot::meta::CallContext::func("Player", "draw");
                    let _success = ::godot::private::handle_ptrcall_panic(
                        &call_ctx,
                        || <Sig as ::godot::meta::PtrcallSignatureTuple>::in_ptrcall(
                            instance_ptr,
                            &call_ctx,
                            args_ptr,
                            ret,
                            |instance_ptr, params| {
                                let () = params;
                                let storage = unsafe {
                                    ::godot::private::as_storage::<Player>(instance_ptr)
                                };
                                let mut instance = ::godot::private::Storage::get_mut(
                                    storage,
                                );
                                instance.draw()
                            },
                            sys::PtrcallType::Virtual,
                        ),
                    );
                }
                Some(virtual_fn)
            }
            "_ready" => {
                use ::godot::sys;
                type Sig = ((),);
                unsafe extern "C" fn virtual_fn(
                    instance_ptr: sys::GDExtensionClassInstancePtr,
                    args_ptr: *const sys::GDExtensionConstTypePtr,
                    ret: sys::GDExtensionTypePtr,
                ) {
                    let call_ctx = ::godot::meta::CallContext::func("Player", "ready");
                    let _success = ::godot::private::handle_ptrcall_panic(
                        &call_ctx,
                        || <Sig as ::godot::meta::PtrcallSignatureTuple>::in_ptrcall(
                            instance_ptr,
                            &call_ctx,
                            args_ptr,
                            ret,
                            |instance_ptr, params| {
                                let () = params;
                                let storage = unsafe {
                                    ::godot::private::as_storage::<Player>(instance_ptr)
                                };
                                let mut instance = ::godot::private::Storage::get_mut(
                                    storage,
                                );
                                instance.__before_ready();
                                instance.ready()
                            },
                            sys::PtrcallType::Virtual,
                        ),
                    );
                }
                Some(virtual_fn)
            }
            "_input" => {
                use ::godot::sys;
                type Sig = ((), Gd<InputEvent>);
                unsafe extern "C" fn virtual_fn(
                    instance_ptr: sys::GDExtensionClassInstancePtr,
                    args_ptr: *const sys::GDExtensionConstTypePtr,
                    ret: sys::GDExtensionTypePtr,
                ) {
                    let call_ctx = ::godot::meta::CallContext::func("Player", "input");
                    let _success = ::godot::private::handle_ptrcall_panic(
                        &call_ctx,
                        || <Sig as ::godot::meta::PtrcallSignatureTuple>::in_ptrcall(
                            instance_ptr,
                            &call_ctx,
                            args_ptr,
                            ret,
                            |instance_ptr, params| {
                                let (event,) = params;
                                let storage = unsafe {
                                    ::godot::private::as_storage::<Player>(instance_ptr)
                                };
                                let mut instance = ::godot::private::Storage::get_mut(
                                    storage,
                                );
                                instance.input(event)
                            },
                            sys::PtrcallType::Virtual,
                        ),
                    );
                }
                Some(virtual_fn)
            }
            _ => None,
        }
    }
}
const _: () = {
    #[allow(non_upper_case_globals)]
    #[used]
    #[link_section = ".CRT$XCU"]
    static __init: extern "C" fn() = {
        extern "C" fn __inner_init() {
            {
                let mut guard = ::godot::private::__godot_rust_plugin___GODOT_PLUGIN_REGISTRY
                    .lock()
                    .unwrap();
                guard
                    .push(::godot::private::ClassPlugin {
                        class_name: <Player as ::godot::obj::GodotClass>::class_name(),
                        item: ::godot::private::PluginItem::ITraitImpl {
                            user_register_fn: None,
                            user_create_fn: {
                                #[allow(unreachable_patterns)]
                                #[allow(clippy::match_single_binding)]
                                match () {
                                    () => Some(::godot::private::callbacks::create::<Player>),
                                    _ => None,
                                }
                            },
                            user_recreate_fn: {
                                #[allow(unreachable_patterns)]
                                #[allow(clippy::match_single_binding)]
                                match () {
                                    () => Some(::godot::private::callbacks::recreate::<Player>),
                                    _ => None,
                                }
                            },
                            user_to_string_fn: None,
                            user_on_notification_fn: None,
                            user_set_fn: None,
                            user_get_fn: None,
                            user_get_property_list_fn: None,
                            user_free_property_list_fn: None,
                            user_property_get_revert_fn: None,
                            user_property_can_revert_fn: None,
                            get_virtual_fn: ::godot::private::callbacks::get_virtual::<
                                Player,
                            >,
                            virtual_method_docs: "",
                        },
                        init_level: <Player as ::godot::obj::GodotClass>::INIT_LEVEL,
                    });
            }
        }
        __inner_init
    };
};
impl Player {
    fn some_weird_func() {
        ::godot_core::global::print(
            &[
                ::godot_core::builtin::Variant::from(
                    ::alloc::__export::must_use({
                        let res = ::alloc::fmt::format(format_args!("Some weird func"));
                        res
                    }),
                ),
            ],
        );
    }
}
#[allow(non_upper_case_globals)]
#[doc(hidden)]
static __registration_methods_Player: std::sync::Mutex<Vec<fn()>> = std::sync::Mutex::new(
    Vec::new(),
);
#[allow(non_upper_case_globals)]
#[doc(hidden)]
static __registration_constants_Player: std::sync::Mutex<Vec<fn()>> = std::sync::Mutex::new(
    Vec::new(),
);
impl ::godot::obj::cap::ImplementsGodotApi for Player {
    fn __register_methods() {
        let guard = __registration_methods_Player.lock().unwrap();
        for f in guard.iter() {
            f();
        }
    }
    fn __register_constants() {
        let guard = __registration_constants_Player.lock().unwrap();
        for f in guard.iter() {
            f();
        }
    }
}
const _: () = {
    #[allow(non_upper_case_globals)]
    #[used]
    #[link_section = ".CRT$XCU"]
    static __init: extern "C" fn() = {
        extern "C" fn __inner_init() {
            {
                __registration_methods_Player.lock().unwrap().push(|| {});
                __registration_constants_Player.lock().unwrap().push(|| {});
            }
        }
        __inner_init
    };
};
const _: () = {
    #[allow(non_upper_case_globals)]
    #[used]
    #[link_section = ".CRT$XCU"]
    static __init: extern "C" fn() = {
        extern "C" fn __inner_init() {
            {
                let mut guard = ::godot::private::__godot_rust_plugin___GODOT_PLUGIN_REGISTRY
                    .lock()
                    .unwrap();
                guard
                    .push(::godot::private::ClassPlugin {
                        class_name: <Player as ::godot::obj::GodotClass>::class_name(),
                        item: ::godot::private::PluginItem::InherentImpl(::godot::private::InherentImpl {
                            register_methods_constants_fn: ::godot::private::ErasedRegisterFn {
                                raw: ::godot::private::callbacks::register_user_methods_constants::<
                                    Player,
                                >,
                            },
                            register_rpcs_fn: Some(::godot::private::ErasedRegisterRpcsFn {
                                raw: ::godot::private::callbacks::register_user_rpcs::<
                                    Player,
                                >,
                            }),
                            docs: ::godot::docs::InherentImplDocs {
                                methods: "",
                                signals_block: "",
                                constants_block: "",
                            },
                        }),
                        init_level: <Player as ::godot::obj::GodotClass>::INIT_LEVEL,
                    });
            }
        }
        __inner_init
    };
};
trait TraitA {
    fn process(&mut self, delta: f64);
}
impl TraitA for Player {
    fn process(&mut self, delta: f64) {}
}
Expanded Rust code after change
#![feature(prelude_import)]
#[prelude_import]
use std::prelude::rust_2021::*;
#[macro_use]
extern crate std;
use godot::prelude::*;
struct MyExtension;
unsafe impl ExtensionLibrary for MyExtension {}
#[no_mangle]
unsafe extern "C" fn gdext_rust_init(
    get_proc_address: ::godot::sys::GDExtensionInterfaceGetProcAddress,
    library: ::godot::sys::GDExtensionClassLibraryPtr,
    init: *mut ::godot::sys::GDExtensionInitialization,
) -> ::godot::sys::GDExtensionBool {
    ::godot::init::__gdext_load_library::<MyExtension>(get_proc_address, library, init)
}
fn __static_type_check() {
    let _unused: ::godot::sys::GDExtensionInitializationFunction = Some(gdext_rust_init);
}
use godot::classes::{ISprite2D, InputEvent, Sprite2D};
use godot::prelude::*;
#[class(base = Sprite2D)]
pub struct Player {
    base: Base<Sprite2D>,
    scene: Gd<PackedScene>,
    music: Option<Gd<AudioStreamPlayer>>,
    sound: Option<Gd<AudioStreamPlayer>>,
    number: i64,
}
impl ::godot::obj::GodotClass for Player {
    type Base = ::godot::classes::Sprite2D;
    fn class_name() -> ::godot::meta::ClassName {
        use ::godot::meta::ClassName;
        static CLASS_NAME: std::sync::OnceLock<ClassName> = std::sync::OnceLock::new();
        let name: &'static ClassName = CLASS_NAME
            .get_or_init(|| ClassName::alloc_next_ascii(c"Player"));
        *name
    }
}
unsafe impl ::godot::obj::Bounds for Player {
    type Memory = <<Self as ::godot::obj::GodotClass>::Base as ::godot::obj::Bounds>::Memory;
    type DynMemory = <<Self as ::godot::obj::GodotClass>::Base as ::godot::obj::Bounds>::DynMemory;
    type Declarer = ::godot::obj::bounds::DeclUser;
    type Exportable = <<Self as ::godot::obj::GodotClass>::Base as ::godot::obj::Bounds>::Exportable;
}
impl ::godot::obj::WithBaseField for Player {
    fn to_gd(&self) -> ::godot::obj::Gd<Player> {
        let base = <Player as ::godot::obj::WithBaseField>::base_field(self);
        base.to_gd().cast()
    }
    fn base_field(
        &self,
    ) -> &::godot::obj::Base<<Player as ::godot::obj::GodotClass>::Base> {
        &self.base
    }
}
impl Player {}
impl ::godot::obj::cap::ImplementsGodotExports for Player {
    fn __register_exports() {}
}
impl ::godot::obj::UserClass for Player {
    fn __config() -> ::godot::private::ClassConfig {
        ::godot::private::ClassConfig {
            is_tool: false,
        }
    }
    fn __before_ready(&mut self) {
        ::godot::register::private::auto_register_rpcs::<Player>(self);
    }
}
#[allow(non_snake_case)]
fn class_Player_must_have_an_init_method() {
    fn __type_check<T: ::godot::obj::cap::GodotDefault>() {}
    __type_check::<Player>();
}
const _: () = {
    #[allow(non_upper_case_globals)]
    #[used]
    #[link_section = ".CRT$XCU"]
    static __init: extern "C" fn() = {
        extern "C" fn __inner_init() {
            {
                let mut guard = ::godot::private::__godot_rust_plugin___GODOT_PLUGIN_REGISTRY
                    .lock()
                    .unwrap();
                guard
                    .push(::godot::private::ClassPlugin {
                        class_name: <Player as ::godot::obj::GodotClass>::class_name(),
                        item: ::godot::private::PluginItem::Struct {
                            base_class_name: <::godot::classes::Sprite2D as ::godot::obj::GodotClass>::class_name(),
                            generated_create_fn: None,
                            generated_recreate_fn: None,
                            register_properties_fn: ::godot::private::ErasedRegisterFn {
                                raw: ::godot::private::callbacks::register_user_properties::<
                                    Player,
                                >,
                            },
                            free_fn: ::godot::private::callbacks::free::<Player>,
                            default_get_virtual_fn: None,
                            is_tool: false,
                            is_editor_plugin: false,
                            is_internal: false,
                            is_instantiable: true,
                            docs: None,
                        },
                        init_level: {
                            let level = <Player as ::godot::obj::GodotClass>::INIT_LEVEL;
                            let base_level = <::godot::classes::Sprite2D as ::godot::obj::GodotClass>::INIT_LEVEL;
                            if !(level >= base_level) {
                                {
                                    ::core::panicking::panic_fmt(
                                        format_args!(
                                            "Class `{0}` has init level `{1:?}`, but its base class has init level `{2:?}`.\nA class cannot be registered before its base class.",
                                            "Player",
                                            level,
                                            base_level,
                                        ),
                                    );
                                }
                            }
                            level
                        },
                    });
            }
        }
        __inner_init
    };
};
unsafe impl ::godot::obj::Inherits<::godot::classes::Sprite2D> for Player {}
unsafe impl ::godot::obj::Inherits<::godot::classes::Node2D> for Player {}
unsafe impl ::godot::obj::Inherits<::godot::classes::CanvasItem> for Player {}
unsafe impl ::godot::obj::Inherits<::godot::classes::Node> for Player {}
unsafe impl ::godot::obj::Inherits<::godot::classes::Object> for Player {}
impl ISprite2D for Player {
    fn init(base: Base<Sprite2D>) -> Self {
        Self {
            base,
            scene: PackedScene::new_gd(),
            music: None,
            sound: None,
            number: 0,
        }
    }
    fn process(&mut self, delta: f64) {
        ::godot_core::global::print(
            &[
                ::godot_core::builtin::Variant::from(
                    ::alloc::__export::must_use({
                        let res = ::alloc::fmt::format(
                            format_args!("Processing Player {0:?}", delta),
                        );
                        res
                    }),
                ),
            ],
        );
    }
    fn draw(&mut self) {
        let colour = Color::from_rgb(1.0, 0.0, 0.0);
        self.base_mut()
            .draw_line_ex(Vector2::new(0.0, 0.0), Vector2::new(100.0, 100.0), colour)
            .width(5.0)
            .antialiased(true)
            .done();
    }
    fn ready(&mut self) {
        ::godot_core::global::print(
            &[
                ::godot_core::builtin::Variant::from(
                    ::alloc::__export::must_use({
                        let res = ::alloc::fmt::format(format_args!("Player is ready"));
                        res
                    }),
                ),
            ],
        );
    }
    fn input(&mut self, event: Gd<InputEvent>) {
        ::godot_core::global::print(
            &[
                ::godot_core::builtin::Variant::from(
                    ::alloc::__export::must_use({
                        let res = ::alloc::fmt::format(
                            format_args!("Player input {0:?}", event),
                        );
                        res
                    }),
                ),
            ],
        );
    }
}
impl ::godot::obj::cap::GodotDefault for Player {
    fn __godot_user_init(base: ::godot::obj::Base<Self::Base>) -> Self {
        <Self as ISprite2D>::init(base)
    }
}
impl ::godot::private::You_forgot_the_attribute__godot_api for Player {}
impl ::godot::obj::cap::ImplementsGodotVirtual for Player {
    fn __virtual_call(name: &str) -> ::godot::sys::GDExtensionClassCallVirtual {
        use ::godot::obj::UserClass as _;
        match name {
            "_process" => {
                use ::godot::sys;
                type Sig = ((), f64);
                unsafe extern "C" fn virtual_fn(
                    instance_ptr: sys::GDExtensionClassInstancePtr,
                    args_ptr: *const sys::GDExtensionConstTypePtr,
                    ret: sys::GDExtensionTypePtr,
                ) {
                    let call_ctx = ::godot::meta::CallContext::func("Player", "process");
                    let _success = ::godot::private::handle_ptrcall_panic(
                        &call_ctx,
                        || <Sig as ::godot::meta::PtrcallSignatureTuple>::in_ptrcall(
                            instance_ptr,
                            &call_ctx,
                            args_ptr,
                            ret,
                            |instance_ptr, params| {
                                let (delta,) = params;
                                let storage = unsafe {
                                    ::godot::private::as_storage::<Player>(instance_ptr)
                                };
                                let mut instance = ::godot::private::Storage::get_mut(
                                    storage,
                                );
                                <Player as ISprite2D>::process(&mut instance, delta)
                            },
                            sys::PtrcallType::Virtual,
                        ),
                    );
                }
                Some(virtual_fn)
            }
            "_draw" => {
                use ::godot::sys;
                type Sig = ((),);
                unsafe extern "C" fn virtual_fn(
                    instance_ptr: sys::GDExtensionClassInstancePtr,
                    args_ptr: *const sys::GDExtensionConstTypePtr,
                    ret: sys::GDExtensionTypePtr,
                ) {
                    let call_ctx = ::godot::meta::CallContext::func("Player", "draw");
                    let _success = ::godot::private::handle_ptrcall_panic(
                        &call_ctx,
                        || <Sig as ::godot::meta::PtrcallSignatureTuple>::in_ptrcall(
                            instance_ptr,
                            &call_ctx,
                            args_ptr,
                            ret,
                            |instance_ptr, params| {
                                let () = params;
                                let storage = unsafe {
                                    ::godot::private::as_storage::<Player>(instance_ptr)
                                };
                                let mut instance = ::godot::private::Storage::get_mut(
                                    storage,
                                );
                                <Player as ISprite2D>::draw(&mut instance)
                            },
                            sys::PtrcallType::Virtual,
                        ),
                    );
                }
                Some(virtual_fn)
            }
            "_ready" => {
                use ::godot::sys;
                type Sig = ((),);
                unsafe extern "C" fn virtual_fn(
                    instance_ptr: sys::GDExtensionClassInstancePtr,
                    args_ptr: *const sys::GDExtensionConstTypePtr,
                    ret: sys::GDExtensionTypePtr,
                ) {
                    let call_ctx = ::godot::meta::CallContext::func("Player", "ready");
                    let _success = ::godot::private::handle_ptrcall_panic(
                        &call_ctx,
                        || <Sig as ::godot::meta::PtrcallSignatureTuple>::in_ptrcall(
                            instance_ptr,
                            &call_ctx,
                            args_ptr,
                            ret,
                            |instance_ptr, params| {
                                let () = params;
                                let storage = unsafe {
                                    ::godot::private::as_storage::<Player>(instance_ptr)
                                };
                                let mut instance = ::godot::private::Storage::get_mut(
                                    storage,
                                );
                                instance.__before_ready();
                                <Player as ISprite2D>::ready(&mut instance)
                            },
                            sys::PtrcallType::Virtual,
                        ),
                    );
                }
                Some(virtual_fn)
            }
            "_input" => {
                use ::godot::sys;
                type Sig = ((), Gd<InputEvent>);
                unsafe extern "C" fn virtual_fn(
                    instance_ptr: sys::GDExtensionClassInstancePtr,
                    args_ptr: *const sys::GDExtensionConstTypePtr,
                    ret: sys::GDExtensionTypePtr,
                ) {
                    let call_ctx = ::godot::meta::CallContext::func("Player", "input");
                    let _success = ::godot::private::handle_ptrcall_panic(
                        &call_ctx,
                        || <Sig as ::godot::meta::PtrcallSignatureTuple>::in_ptrcall(
                            instance_ptr,
                            &call_ctx,
                            args_ptr,
                            ret,
                            |instance_ptr, params| {
                                let (event,) = params;
                                let storage = unsafe {
                                    ::godot::private::as_storage::<Player>(instance_ptr)
                                };
                                let mut instance = ::godot::private::Storage::get_mut(
                                    storage,
                                );
                                <Player as ISprite2D>::input(&mut instance, event)
                            },
                            sys::PtrcallType::Virtual,
                        ),
                    );
                }
                Some(virtual_fn)
            }
            _ => None,
        }
    }
}
const _: () = {
    #[allow(non_upper_case_globals)]
    #[used]
    #[link_section = ".CRT$XCU"]
    static __init: extern "C" fn() = {
        extern "C" fn __inner_init() {
            {
                let mut guard = ::godot::private::__godot_rust_plugin___GODOT_PLUGIN_REGISTRY
                    .lock()
                    .unwrap();
                guard
                    .push(::godot::private::ClassPlugin {
                        class_name: <Player as ::godot::obj::GodotClass>::class_name(),
                        item: ::godot::private::PluginItem::ITraitImpl {
                            user_register_fn: None,
                            user_create_fn: {
                                #[allow(unreachable_patterns)]
                                #[allow(clippy::match_single_binding)]
                                match () {
                                    () => Some(::godot::private::callbacks::create::<Player>),
                                    _ => None,
                                }
                            },
                            user_recreate_fn: {
                                #[allow(unreachable_patterns)]
                                #[allow(clippy::match_single_binding)]
                                match () {
                                    () => Some(::godot::private::callbacks::recreate::<Player>),
                                    _ => None,
                                }
                            },
                            user_to_string_fn: None,
                            user_on_notification_fn: None,
                            user_set_fn: None,
                            user_get_fn: None,
                            user_get_property_list_fn: None,
                            user_free_property_list_fn: None,
                            user_property_get_revert_fn: None,
                            user_property_can_revert_fn: None,
                            get_virtual_fn: ::godot::private::callbacks::get_virtual::<
                                Player,
                            >,
                            virtual_method_docs: "",
                        },
                        init_level: <Player as ::godot::obj::GodotClass>::INIT_LEVEL,
                    });
            }
        }
        __inner_init
    };
};
impl Player {
    fn some_weird_func() {
        ::godot_core::global::print(
            &[
                ::godot_core::builtin::Variant::from(
                    ::alloc::__export::must_use({
                        let res = ::alloc::fmt::format(format_args!("Some weird func"));
                        res
                    }),
                ),
            ],
        );
    }
}
#[allow(non_upper_case_globals)]
#[doc(hidden)]
static __registration_methods_Player: std::sync::Mutex<Vec<fn()>> = std::sync::Mutex::new(
    Vec::new(),
);
#[allow(non_upper_case_globals)]
#[doc(hidden)]
static __registration_constants_Player: std::sync::Mutex<Vec<fn()>> = std::sync::Mutex::new(
    Vec::new(),
);
impl ::godot::obj::cap::ImplementsGodotApi for Player {
    fn __register_methods() {
        let guard = __registration_methods_Player.lock().unwrap();
        for f in guard.iter() {
            f();
        }
    }
    fn __register_constants() {
        let guard = __registration_constants_Player.lock().unwrap();
        for f in guard.iter() {
            f();
        }
    }
}
const _: () = {
    #[allow(non_upper_case_globals)]
    #[used]
    #[link_section = ".CRT$XCU"]
    static __init: extern "C" fn() = {
        extern "C" fn __inner_init() {
            {
                __registration_methods_Player.lock().unwrap().push(|| {});
                __registration_constants_Player.lock().unwrap().push(|| {});
            }
        }
        __inner_init
    };
};
const _: () = {
    #[allow(non_upper_case_globals)]
    #[used]
    #[link_section = ".CRT$XCU"]
    static __init: extern "C" fn() = {
        extern "C" fn __inner_init() {
            {
                let mut guard = ::godot::private::__godot_rust_plugin___GODOT_PLUGIN_REGISTRY
                    .lock()
                    .unwrap();
                guard
                    .push(::godot::private::ClassPlugin {
                        class_name: <Player as ::godot::obj::GodotClass>::class_name(),
                        item: ::godot::private::PluginItem::InherentImpl(::godot::private::InherentImpl {
                            register_methods_constants_fn: ::godot::private::ErasedRegisterFn {
                                raw: ::godot::private::callbacks::register_user_methods_constants::<
                                    Player,
                                >,
                            },
                            register_rpcs_fn: Some(::godot::private::ErasedRegisterRpcsFn {
                                raw: ::godot::private::callbacks::register_user_rpcs::<
                                    Player,
                                >,
                            }),
                            docs: ::godot::docs::InherentImplDocs {
                                methods: "",
                                signals_block: "",
                                constants_block: "",
                            },
                        }),
                        init_level: <Player as ::godot::obj::GodotClass>::INIT_LEVEL,
                    });
            }
        }
        __inner_init
    };
};
trait TraitA {
    fn process(&mut self, delta: f64);
}
impl TraitA for Player {
    fn process(&mut self, delta: f64) {}
}

@adalinesimonian adalinesimonian changed the title fix: ambiguous virtual method calls #858 fix: ambiguous virtual method calls Jan 20, 2025
@Bromeon Bromeon added bug c: register Register classes, functions and other symbols to GDScript labels Jan 20, 2025
@GodotRust
Copy link

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

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! The implementation looks good from what I can see 🙂

So I am not 100% of if this fix is entirely proper or not.

Yep, I think the case you're handling is the important one. To verify that it works, could you construct an #[itest] with a simple example like in #858? Maybe func_test.rs is a good location; you can possibly reuse the existing classes and just add extra traits.

godot-macros/src/class/data_models/func.rs Outdated Show resolved Hide resolved
godot-macros/src/class/data_models/func.rs Outdated Show resolved Hide resolved
Some(api_trait) => {
let instance_ref = match signature_info.receiver_type {
ReceiverType::Mut => quote! { &mut instance },
_ => quote! { &instance },
Copy link
Member

Choose a reason for hiding this comment

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

do we want to just silently fail here if an incorrect receiver is passed somehow? it should generate a compile error either way, but maybe we'd wanna explicitly pass a compile_error! instead?

Copy link
Author

@adalinesimonian adalinesimonian Jan 22, 2025

Choose a reason for hiding this comment

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

This is inside of a match arm against receiver_type that matches ReceiverType::Mut and ReceiverType::Ref, so it is impossible in this case for it to be anything else.

@adalinesimonian adalinesimonian force-pushed the fix-858-ambiguous-virtual-methods branch from 917858c to ddf30e6 Compare January 22, 2025 07:48
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.

Ambiguous method call in #[godot_api] generated code
4 participants