Skip to content

Commit 5fed5cc

Browse files
committed
Transform process/physics_process methods to support f32 signatures
1 parent 4a95bbf commit 5fed5cc

File tree

4 files changed

+88
-14
lines changed

4 files changed

+88
-14
lines changed

godot-core/src/obj/traits.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@ pub trait WithBaseField: GodotClass + Bounds<Declarer = bounds::DeclUser> {
293293
///
294294
/// #[godot_api]
295295
/// impl INode for MyClass {
296-
/// fn process(&mut self, _delta: f64) {
296+
/// fn process(&mut self, _delta: f32) {
297297
/// let name = self.base().get_name();
298298
/// godot_print!("name is {name}");
299299
/// }
@@ -319,7 +319,7 @@ pub trait WithBaseField: GodotClass + Bounds<Declarer = bounds::DeclUser> {
319319
///
320320
/// #[godot_api]
321321
/// impl INode for MyClass {
322-
/// fn process(&mut self, _delta: f64) {
322+
/// fn process(&mut self, _delta: f32) {
323323
/// let node = Node::new_alloc();
324324
/// // fails because `add_child` requires a mutable reference.
325325
/// self.base().add_child(&node);
@@ -362,7 +362,7 @@ pub trait WithBaseField: GodotClass + Bounds<Declarer = bounds::DeclUser> {
362362
///
363363
/// #[godot_api]
364364
/// impl INode for MyClass {
365-
/// fn process(&mut self, _delta: f64) {
365+
/// fn process(&mut self, _delta: f32) {
366366
/// let node = Node::new_alloc();
367367
/// self.base_mut().add_child(&node);
368368
/// }
@@ -387,7 +387,7 @@ pub trait WithBaseField: GodotClass + Bounds<Declarer = bounds::DeclUser> {
387387
///
388388
/// #[godot_api]
389389
/// impl INode for MyClass {
390-
/// fn process(&mut self, _delta: f64) {
390+
/// fn process(&mut self, _delta: f32) {
391391
/// self.base_mut().call("other_method", &[]);
392392
/// }
393393
/// }

godot-macros/src/class/data_models/func.rs

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -187,8 +187,14 @@ pub struct SignatureInfo {
187187
pub method_name: Ident,
188188
pub receiver_type: ReceiverType,
189189
pub param_idents: Vec<Ident>,
190+
/// Parameter types *without* receiver.
190191
pub param_types: Vec<venial::TypeExpr>,
191192
pub return_type: TokenStream,
193+
194+
/// `(original index, new type)` only for changed parameters; empty if no changes.
195+
///
196+
/// Index points into original venial tokens (i.e. takes into account potential receiver params).
197+
pub modified_param_types: Vec<(usize, venial::TypeExpr)>,
192198
}
193199

194200
impl SignatureInfo {
@@ -199,6 +205,7 @@ impl SignatureInfo {
199205
param_idents: vec![],
200206
param_types: vec![],
201207
return_type: quote! { () },
208+
modified_param_types: vec![],
202209
}
203210
}
204211

@@ -359,7 +366,8 @@ pub(crate) fn into_signature_info(
359366
};
360367

361368
let mut next_unnamed_index = 0;
362-
for (arg, _) in signature.params.inner {
369+
let mut modified_param_types = vec![];
370+
for (index, (arg, _)) in signature.params.inner.into_iter().enumerate() {
363371
match arg {
364372
venial::FnParam::Receiver(recv) => {
365373
if receiver_type == ReceiverType::GdSelf {
@@ -377,8 +385,17 @@ pub(crate) fn into_signature_info(
377385
}
378386
venial::FnParam::Typed(arg) => {
379387
let ident = maybe_rename_parameter(arg.name, &mut next_unnamed_index);
380-
let ty = venial::TypeExpr {
381-
tokens: map_self_to_class_name(arg.ty.tokens, class_name),
388+
let ty = match maybe_change_parameter_type(arg.ty, &method_name, index) {
389+
// Parameter type was modified.
390+
Ok(ty) => {
391+
modified_param_types.push((index, ty.clone()));
392+
ty
393+
}
394+
395+
// Not an error, just unchanged.
396+
Err(ty) => venial::TypeExpr {
397+
tokens: map_self_to_class_name(ty.tokens, class_name),
398+
},
382399
};
383400

384401
param_types.push(ty);
@@ -393,6 +410,28 @@ pub(crate) fn into_signature_info(
393410
param_idents,
394411
param_types,
395412
return_type: ret_type,
413+
modified_param_types,
414+
}
415+
}
416+
417+
/// If `f32` is used for a delta parameter in a virtual process function, transparently use `f64` behind the scenes.
418+
fn maybe_change_parameter_type(
419+
param_ty: venial::TypeExpr,
420+
method_name: &Ident,
421+
param_index: usize,
422+
) -> Result<venial::TypeExpr, venial::TypeExpr> {
423+
// A bit hackish, but TokenStream APIs are also notoriously annoying to work with. Not even PartialEq...
424+
425+
if param_index == 1
426+
&& (method_name == "process" || method_name == "physics_process")
427+
&& param_ty.tokens.len() == 1
428+
&& param_ty.tokens[0].to_string() == "f32"
429+
{
430+
Ok(venial::TypeExpr {
431+
tokens: vec![TokenTree::Ident(ident("f64"))],
432+
})
433+
} else {
434+
Err(param_ty)
396435
}
397436
}
398437

godot-macros/src/class/data_models/interface_trait_impl.rs

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use crate::class::{into_signature_info, make_virtual_callback, BeforeKind, Signa
99
use crate::util::ident;
1010
use crate::{util, ParseResult};
1111

12-
use proc_macro2::{Ident, TokenStream};
12+
use proc_macro2::{Group, Ident, TokenStream};
1313
use quote::{quote, ToTokens};
1414

1515
/// Codegen for `#[godot_api] impl ISomething for MyType`.
@@ -71,18 +71,27 @@ pub fn transform_trait_impl(mut original_impl: venial::Impl) -> ParseResult<Toke
7171
}
7272
regular_virtual_fn => {
7373
// Break borrow chain to allow handle_regular_virtual_fn() to mutably borrow `method` and modify `original_impl` through it.
74-
let cfg_attrs = cfg_attrs.iter().cloned().collect();
74+
// let cfg_attrs = cfg_attrs.iter().cloned().collect()
7575

7676
// All the non-special engine ones: ready(), process(), etc.
7777
// Can modify original_impl, concretely the fn body for f64->f32 conversions.
78-
handle_regular_virtual_fn(
78+
let changed_function = handle_regular_virtual_fn(
7979
&class_name,
8080
&trait_path,
8181
method,
8282
regular_virtual_fn,
8383
cfg_attrs,
8484
&mut decls,
8585
);
86+
87+
// If the function is modified (e.g. process() declared with f32), apply changes here.
88+
// Borrow-checker: we cannot reassign whole function due to shared borrow on `method.attributes`.
89+
// Thus, separately update signature + body when needed.
90+
if let Some((new_params, new_body)) = changed_function {
91+
method.params = new_params;
92+
method.body = Some(new_body);
93+
//panic!("modify params: {}", method.params.to_token_stream().to_string());
94+
}
8695
}
8796
}
8897
}
@@ -445,14 +454,14 @@ fn handle_property_get_revert<'a>(
445454
fn handle_regular_virtual_fn<'a>(
446455
class_name: &Ident,
447456
trait_path: &venial::TypeExpr,
448-
method: &venial::Function,
457+
original_method: &venial::Function,
449458
method_name: &str,
450459
cfg_attrs: Vec<&'a venial::Attribute>,
451460
decls: &mut IDecls<'a>,
452-
) {
461+
) -> Option<(venial::Punctuated<venial::FnParam>, Group)> {
453462
#[cfg(since_api = "4.4")]
454-
let method_name_ident = method.name.clone();
455-
let method = util::reduce_to_signature(method);
463+
let method_name_ident = original_method.name.clone();
464+
let method = util::reduce_to_signature(original_method);
456465

457466
// Godot-facing name begins with underscore.
458467
//
@@ -466,6 +475,27 @@ fn handle_regular_virtual_fn<'a>(
466475

467476
let signature_info = into_signature_info(method, class_name, false);
468477

478+
let mut updated_function = None;
479+
480+
// If there was a signature change (e.g. f32 -> f64 in process/physics_process), apply to new function tokens.
481+
if !signature_info.modified_param_types.is_empty() {
482+
let mut new_params = original_method.params.clone();
483+
for (index, new_ty) in signature_info.modified_param_types.iter() {
484+
let venial::FnParam::Typed(typed) = &mut new_params.inner[*index].0 else {
485+
panic!("Unexpected parameter type: {new_params:?}");
486+
};
487+
488+
typed.ty = new_ty.clone();
489+
}
490+
491+
let body = original_method
492+
.body
493+
.clone()
494+
.expect("function must have a body");
495+
496+
updated_function = Some((new_params, body));
497+
}
498+
469499
// Overridden ready() methods additionally have an additional `__before_ready()` call (for OnReady inits).
470500
let before_kind = if method_name == "ready" {
471501
BeforeKind::WithBefore
@@ -489,6 +519,8 @@ fn handle_regular_virtual_fn<'a>(
489519
before_kind,
490520
interface_trait: Some(trait_path.clone()),
491521
});
522+
523+
updated_function
492524
}
493525

494526
// ----------------------------------------------------------------------------------------------------------------------------------------------

itest/rust/src/engine_tests/codegen_test.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,9 @@ impl IHttpRequest for CodegenTest {
9595

9696
// Test unnamed parameter in virtual function
9797
fn process(&mut self, _: f64) {}
98+
99+
// Test auto-cast to f32 parameter in virtual function
100+
fn physics_process(&mut self, _delta: f32) {}
98101
}
99102

100103
// ----------------------------------------------------------------------------------------------------------------------------------------------

0 commit comments

Comments
 (0)