Skip to content

Commit 2dd05c0

Browse files
Merge branch 'master' into rust_vs_godot_hashes
2 parents 6622ede + 7079ec0 commit 2dd05c0

File tree

11 files changed

+213
-82
lines changed

11 files changed

+213
-82
lines changed

godot-codegen/src/generator/functions_common.rs

Lines changed: 32 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -603,6 +603,32 @@ pub(crate) fn make_params_exprs<'a>(
603603
ret
604604
}
605605

606+
/// Returns the type for a virtual method parameter.
607+
///
608+
/// Generates `Option<Gd<T>>` instead of `Gd<T>` for object parameters (which are currently all nullable).
609+
///
610+
/// Used for consistency between virtual trait definitions and `type Sig = ...` type-safety declarations
611+
/// (which are used to improve compile-time errors on mismatch).
612+
pub(crate) fn make_virtual_param_type(
613+
param_ty: &RustTy,
614+
param_name: &Ident,
615+
function_sig: &dyn Function,
616+
) -> TokenStream {
617+
match param_ty {
618+
// Virtual methods accept Option<Gd<T>>, since we don't know whether objects are nullable or required.
619+
RustTy::EngineClass { .. }
620+
if !special_cases::is_class_method_param_required(
621+
function_sig.surrounding_class().unwrap(),
622+
function_sig.godot_name(),
623+
param_name,
624+
) =>
625+
{
626+
quote! { Option<#param_ty> }
627+
}
628+
_ => quote! { #param_ty },
629+
}
630+
}
631+
606632
/// For virtual functions, returns the parameter declarations, type tokens, and names.
607633
pub(crate) fn make_params_exprs_virtual<'a>(
608634
method_args: impl Iterator<Item = &'a FnParam>,
@@ -614,30 +640,13 @@ pub(crate) fn make_params_exprs_virtual<'a>(
614640
let param_name = &param.name;
615641
let param_ty = &param.type_;
616642

617-
match &param.type_ {
618-
// Virtual methods accept Option<Gd<T>>, since we don't know whether objects are nullable or required.
619-
RustTy::EngineClass { .. }
620-
if !special_cases::is_class_method_param_required(
621-
function_sig.surrounding_class().unwrap(),
622-
function_sig.godot_name(),
623-
param_name,
624-
) =>
625-
{
626-
ret.param_decls
627-
.push(quote! { #param_name: Option<#param_ty> });
628-
ret.arg_exprs.push(quote! { #param_name });
629-
ret.callsig_param_types.push(quote! { #param_ty });
630-
}
643+
// Map parameter types (e.g. virtual functions need Option<Gd> instead of Gd).
644+
let param_ty_tokens = make_virtual_param_type(param_ty, param_name, function_sig);
631645

632-
// All other methods and parameter types: standard handling.
633-
// For now, virtual methods always receive their parameter by value.
634-
//_ => ret.push_regular(param_name, param_ty, true, false, false),
635-
_ => {
636-
ret.param_decls.push(quote! { #param_name: #param_ty });
637-
ret.arg_exprs.push(quote! { #param_name });
638-
ret.callsig_param_types.push(quote! { #param_ty });
639-
}
640-
}
646+
ret.param_decls
647+
.push(quote! { #param_name: #param_ty_tokens });
648+
ret.arg_exprs.push(quote! { #param_name });
649+
ret.callsig_param_types.push(quote! { #param_ty });
641650
}
642651

643652
ret

godot-codegen/src/generator/mod.rs

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ pub mod notifications;
3030
pub mod signals;
3131
pub mod sys;
3232
pub mod utility_functions;
33-
pub mod virtual_definition_consts;
33+
pub mod virtual_definitions;
3434
pub mod virtual_traits;
3535

3636
// ----------------------------------------------------------------------------------------------------------------------------------------------
@@ -57,7 +57,6 @@ pub fn generate_sys_module_file(sys_gen_path: &Path, submit_fn: &mut SubmitFn) {
5757
pub mod central;
5858
pub mod gdextension_interface;
5959
pub mod interface;
60-
pub mod virtual_consts;
6160
};
6261

6362
submit_fn(sys_gen_path.join("mod.rs"), code);
@@ -87,12 +86,6 @@ pub fn generate_sys_classes_file(
8786
submit_fn(sys_gen_path.join(filename), code);
8887
watch.record(format!("generate_classes_{}_file", api_level.lower()));
8988
}
90-
91-
// From 4.4 onward, generate table that maps all virtual methods to their known hashes.
92-
// This allows Godot to fall back to an older compatibility function if one is not supported.
93-
let code = virtual_definition_consts::make_virtual_consts_file(api, ctx);
94-
submit_fn(sys_gen_path.join("virtual_consts.rs"), code);
95-
watch.record("generate_virtual_consts_file");
9689
}
9790

9891
pub fn generate_sys_utilities_file(
@@ -132,6 +125,7 @@ pub fn generate_core_mod_file(gen_path: &Path, submit_fn: &mut SubmitFn) {
132125
pub mod builtin_classes;
133126
pub mod utilities;
134127
pub mod native;
128+
pub mod virtuals;
135129
};
136130

137131
submit_fn(gen_path.join("mod.rs"), code);

godot-codegen/src/generator/virtual_definition_consts.rs renamed to godot-codegen/src/generator/virtual_definitions.rs

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,13 @@
66
*/
77

88
use proc_macro2::TokenStream;
9-
use quote::quote;
9+
use quote::{format_ident, quote};
1010

1111
use crate::context::Context;
12+
use crate::generator::functions_common::make_virtual_param_type;
1213
use crate::models::domain::{Class, ClassLike, ExtensionApi, FnDirection, Function};
1314

14-
pub fn make_virtual_consts_file(api: &ExtensionApi, ctx: &mut Context) -> TokenStream {
15+
pub fn make_virtual_definitions_file(api: &ExtensionApi, ctx: &mut Context) -> TokenStream {
1516
make_virtual_hashes_for_all_classes(&api.classes, ctx)
1617
}
1718

@@ -21,7 +22,7 @@ fn make_virtual_hashes_for_all_classes(all_classes: &[Class], ctx: &mut Context)
2122
.map(|class| make_virtual_hashes_for_class(class, ctx));
2223

2324
quote! {
24-
#![allow(non_snake_case, non_upper_case_globals, unused_imports)]
25+
#![allow(non_snake_case, non_camel_case_types, non_upper_case_globals, unused_imports)]
2526

2627
#( #modules )*
2728
}
@@ -34,6 +35,12 @@ fn make_virtual_hashes_for_class(class: &Class, ctx: &mut Context) -> TokenStrea
3435
let use_base_class = if let Some(base_class) = ctx.inheritance_tree().direct_base(class_name) {
3536
quote! {
3637
pub use super::#base_class::*;
38+
39+
// For type references in `Sig_*` signature tuples:
40+
pub use crate::builtin::*;
41+
pub use crate::classes::native::*;
42+
pub use crate::obj::Gd;
43+
pub use std::ffi::c_void;
3744
}
3845
} else {
3946
TokenStream::new()
@@ -51,13 +58,27 @@ fn make_virtual_hashes_for_class(class: &Class, ctx: &mut Context) -> TokenStrea
5158
let rust_name = method.name_ident();
5259
let godot_name_str = method.godot_name();
5360

61+
// Generate parameter types, wrapping EngineClass in Option<> just like the trait does
62+
let param_types = method
63+
.params()
64+
.iter()
65+
.map(|param| make_virtual_param_type(&param.type_, &param.name, method));
66+
67+
let rust_sig_name = format_ident!("Sig_{rust_name}");
68+
let sig_decl = quote! {
69+
// Pub to allow "inheritance" from other modules.
70+
pub type #rust_sig_name = ( #(#param_types,)* );
71+
};
72+
5473
#[cfg(since_api = "4.4")]
5574
let constant = quote! {
56-
pub const #rust_name: (&'static str, u32) = (#godot_name_str, #hash);
75+
pub const #rust_name: (&str, u32) = (#godot_name_str, #hash);
76+
#sig_decl
5777
};
5878
#[cfg(before_api = "4.4")]
5979
let constant = quote! {
60-
pub const #rust_name: &'static str = #godot_name_str;
80+
pub const #rust_name: &str = #godot_name_str;
81+
#sig_decl
6182
};
6283

6384
Some(constant)

godot-codegen/src/lib.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ use crate::generator::utility_functions::generate_utilities_file;
3737
use crate::generator::{
3838
generate_core_central_file, generate_core_mod_file, generate_sys_builtin_lifecycle_file,
3939
generate_sys_builtin_methods_file, generate_sys_central_file, generate_sys_classes_file,
40-
generate_sys_module_file, generate_sys_utilities_file,
40+
generate_sys_module_file, generate_sys_utilities_file, virtual_definitions,
4141
};
4242
use crate::models::domain::{ApiView, ExtensionApi};
4343
use crate::models::json::{load_extension_api, JsonExtensionApi};
@@ -173,6 +173,13 @@ pub fn generate_core_files(core_gen_path: &Path) {
173173
generate_utilities_file(&api, core_gen_path, &mut submit_fn);
174174
watch.record("generate_utilities_file");
175175

176+
// From 4.4 onward, generate table that maps all virtual methods to their known hashes.
177+
// This allows Godot to fall back to an older compatibility function if one is not supported.
178+
// Also expose tuple signatures of virtual methods.
179+
let code = virtual_definitions::make_virtual_definitions_file(&api, &mut ctx);
180+
submit_fn(core_gen_path.join("virtuals.rs"), code);
181+
watch.record("generate_virtual_definitions");
182+
176183
// Class files -- currently output in godot-core; could maybe be separated cleaner
177184
// Note: deletes entire generated directory!
178185
generate_class_files(

godot-core/src/meta/args/as_arg.rs

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use crate::builtin::{GString, NodePath, StringName, Variant};
99
use crate::meta::sealed::Sealed;
1010
use crate::meta::traits::{GodotFfiVariant, GodotNullableFfi};
1111
use crate::meta::{CowArg, FfiArg, GodotType, ObjectArg, ToGodot};
12-
use crate::obj::{bounds, Bounds, DynGd, Gd, GodotClass, Inherits};
12+
use crate::obj::{DynGd, Gd, GodotClass, Inherits};
1313

1414
/// Implicit conversions for arguments passed to Godot APIs.
1515
///
@@ -263,7 +263,7 @@ where
263263
impl<T, Base> AsArg<Option<Gd<Base>>> for &Gd<T>
264264
where
265265
T: Inherits<Base>,
266-
Base: GodotClass + Bounds<Declarer = bounds::DeclEngine>,
266+
Base: GodotClass,
267267
{
268268
fn into_arg<'arg>(self) -> CowArg<'arg, Option<Gd<Base>>>
269269
where
@@ -286,7 +286,7 @@ where
286286
impl<T, Base> AsArg<Option<Gd<Base>>> for Option<&Gd<T>>
287287
where
288288
T: Inherits<Base>,
289-
Base: GodotClass + Bounds<Declarer = bounds::DeclEngine>,
289+
Base: GodotClass,
290290
{
291291
fn into_arg<'arg>(self) -> CowArg<'arg, Option<Gd<Base>>>
292292
where
@@ -313,7 +313,7 @@ impl<T, D, Base> AsArg<Option<DynGd<Base, D>>> for &DynGd<T, D>
313313
where
314314
T: Inherits<Base>,
315315
D: ?Sized,
316-
Base: GodotClass + Bounds<Declarer = bounds::DeclEngine>,
316+
Base: GodotClass,
317317
{
318318
fn into_arg<'arg>(self) -> CowArg<'arg, Option<DynGd<Base, D>>>
319319
where
@@ -337,7 +337,7 @@ impl<T, D, Base> AsArg<Option<Gd<Base>>> for &DynGd<T, D>
337337
where
338338
T: Inherits<Base>,
339339
D: ?Sized,
340-
Base: GodotClass + Bounds<Declarer = bounds::DeclEngine>,
340+
Base: GodotClass,
341341
{
342342
fn into_arg<'arg>(self) -> CowArg<'arg, Option<Gd<Base>>>
343343
where
@@ -361,7 +361,7 @@ impl<T, D, Base> AsArg<Option<DynGd<Base, D>>> for Option<&DynGd<T, D>>
361361
where
362362
T: Inherits<Base>,
363363
D: ?Sized,
364-
Base: GodotClass + Bounds<Declarer = bounds::DeclEngine>,
364+
Base: GodotClass,
365365
{
366366
fn into_arg<'arg>(self) -> CowArg<'arg, Option<DynGd<Base, D>>>
367367
where
@@ -764,3 +764,35 @@ where
764764

765765
#[doc(hidden)] // Easier for internal use.
766766
pub type ToArg<'r, Via, Pass> = <Pass as ArgPassing>::Output<'r, Via>;
767+
768+
/// This type exists only as a place to add doctests for `AsArg`, which do not need to be in the public documentation.
769+
///
770+
/// `AsArg<Option<Gd<UserClass>>` can be used with signals correctly:
771+
///
772+
/// ```no_run
773+
/// # use godot::prelude::*;
774+
/// #[derive(GodotClass)]
775+
/// #[class(init, base = Node)]
776+
/// struct MyClass {
777+
/// base: Base<Node>
778+
/// }
779+
///
780+
/// #[godot_api]
781+
/// impl MyClass {
782+
/// #[signal]
783+
/// fn signal_optional_user_obj(arg1: Option<Gd<MyClass>>);
784+
///
785+
/// fn foo(&mut self) {
786+
/// let arg = self.to_gd();
787+
/// // Directly:
788+
/// self.signals().signal_optional_user_obj().emit(&arg);
789+
/// // Via Some:
790+
/// self.signals().signal_optional_user_obj().emit(Some(&arg));
791+
/// // With None (Note: Gd::null_arg() is restricted to engine classes):
792+
/// self.signals().signal_optional_user_obj().emit(None::<Gd<MyClass>>.as_ref());
793+
/// }
794+
/// }
795+
/// ```
796+
///
797+
#[allow(dead_code)]
798+
struct PhantomAsArgDoctests;

godot-core/src/private.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ mod reexport_pub {
2525
#[cfg(all(since_api = "4.3", feature = "register-docs"))]
2626
pub use crate::docs::{DocsItem, DocsPlugin, InherentImplDocs, StructDocs};
2727
pub use crate::gen::classes::class_macros;
28+
pub use crate::gen::virtuals; // virtual fn names, hashes, signatures
2829
#[cfg(feature = "trace")]
2930
pub use crate::meta::trace;
3031
pub use crate::obj::rtti::ObjectRtti;

godot-ffi/src/lib.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,6 @@ pub use gen::table_editor_classes::*;
8686
pub use gen::table_scene_classes::*;
8787
pub use gen::table_servers_classes::*;
8888
pub use gen::table_utilities::*;
89-
pub use gen::virtual_consts as godot_virtual_consts;
9089
pub use global::*;
9190
pub use string_cache::StringCache;
9291
pub use toolbox::*;

0 commit comments

Comments
 (0)