Skip to content

Commit a7b1023

Browse files
committed
Builder lifetimes, small fixes
1 parent 14f19fa commit a7b1023

File tree

4 files changed

+48
-52
lines changed

4 files changed

+48
-52
lines changed

godot-codegen/src/generator/default_parameters.rs

Lines changed: 42 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,12 @@ pub fn make_function_definition_with_defaults(
4444
builder_fields,
4545
builder_args,
4646
builder_inits,
47-
} = make_extender(sig, object_fn_param, default_fn_params);
47+
} = make_extender(
48+
sig.name(),
49+
object_fn_param,
50+
&required_fn_params,
51+
&default_fn_params,
52+
);
4853

4954
// ExBuilder::new() constructor signature.
5055
let FnParamTokens {
@@ -61,19 +66,6 @@ pub fn make_function_definition_with_defaults(
6166
);
6267
let required_params_builder_constructor = &required_params_class_methods;
6368

64-
// Parameters for some_function() and some_function_ex().
65-
// TODO collapse with next assignment. Maybe next 2
66-
/*let FnParamTokens {
67-
params: required_params_class_methods,
68-
..
69-
} = functions_common::make_params_exprs(
70-
required_fn_params.iter().cloned(),
71-
true,
72-
true,
73-
false,
74-
false,
75-
);*/
76-
7769
// Forwarded args by some_function() and some_function_ex().
7870
let FnParamTokens {
7971
arg_names: required_args_internal,
@@ -92,13 +84,15 @@ pub fn make_function_definition_with_defaults(
9284
// then we need to annotate the _ex() function with an explicit lifetime. Also adjust &self -> &'a self.
9385
let receiver_self = &code.receiver.self_prefix;
9486

95-
let (builder_ret_lifetime, receiver_param);
96-
if let Some(func_lifetime) = func_general_lifetime.as_ref() {
97-
builder_ret_lifetime = Some(func_lifetime);
98-
receiver_param = &code.receiver.param_lifetime_a;
87+
let (simple_receiver_param, extended_receiver_param, func_or_builder_lifetime);
88+
if let Some(func_lt) = func_general_lifetime.as_ref().or(builder_lifetime.as_ref()) {
89+
simple_receiver_param = &code.receiver.param;
90+
extended_receiver_param = &code.receiver.param_lifetime_a;
91+
func_or_builder_lifetime = Some(func_lt);
9992
} else {
100-
builder_ret_lifetime = None;
101-
receiver_param = &code.receiver.param;
93+
simple_receiver_param = &code.receiver.param;
94+
extended_receiver_param = &code.receiver.param;
95+
func_or_builder_lifetime = None;
10296
};
10397

10498
// Technically, the builder would not need a lifetime -- it could just maintain an `object_ptr` copy.
@@ -146,7 +140,7 @@ pub fn make_function_definition_with_defaults(
146140
#[doc = #default_parameter_usage]
147141
#[inline]
148142
#vis fn #simple_fn_name #func_general_lifetime (
149-
#receiver_param
143+
#simple_receiver_param
150144
#( #required_params_class_methods, )*
151145
) #return_decl {
152146
#receiver_self #extended_fn_name(
@@ -157,10 +151,10 @@ pub fn make_function_definition_with_defaults(
157151
// _ex() function:
158152
// Lifetime is set if any parameter is a reference OR if the method is not static/global (and thus can refer to self).
159153
#[inline]
160-
#vis fn #extended_fn_name #func_general_lifetime (
161-
#receiver_param
154+
#vis fn #extended_fn_name #func_or_builder_lifetime (
155+
#extended_receiver_param
162156
#( #required_params_class_methods, )*
163-
) -> #builder_ty #builder_ret_lifetime {
157+
) -> #builder_ty #builder_lifetime {
164158
#builder_ty::new(
165159
#object_arg
166160
#( #required_args_internal, )*
@@ -278,26 +272,34 @@ fn make_extender_receiver(sig: &dyn Function) -> ExtenderReceiver {
278272
}
279273

280274
fn make_extender(
281-
sig: &dyn Function,
282-
object_fn_param: Option<FnParam>,
283-
default_fn_params: Vec<&FnParam>,
275+
fn_name: &str,
276+
receiver_param: Option<FnParam>,
277+
required_params: &[&FnParam],
278+
default_params: &[&FnParam],
284279
) -> Extender {
285280
// Note: could build a documentation string with default values here, but the Rust tokens are not very readable,
286281
// and often not helpful, such as Enum::from_ord(13). Maybe one day those could be resolved and curated.
287282

288-
let lifetime = if sig.qualifier().is_static_or_global() {
289-
None
290-
} else {
283+
let all_fn_params = receiver_param
284+
.iter()
285+
.chain(required_params.iter().cloned())
286+
.chain(default_params.iter().cloned());
287+
let len = all_fn_params.size_hint().0;
288+
289+
// If builder is a method with a receiver OR any *required* parameter is by-ref, use lifetime.
290+
// Default parameters cannot be by-ref, since they need to store a default value. Potential optimization later.
291+
let lifetime = if receiver_param.is_some() // !sig.qualifier().is_static_or_global()
292+
|| required_params.iter().any(|p| p.type_.is_pass_by_ref())
293+
{
291294
Some(quote! { <'a> })
295+
} else {
296+
None
292297
};
293298

294-
let all_fn_params = object_fn_param.iter().chain(sig.params().iter());
295-
let len = all_fn_params.size_hint().0;
296-
297299
let mut result = Extender {
298-
builder_ty: format_ident!("Ex{}", conv::to_pascal_case(sig.name())),
300+
builder_ty: format_ident!("Ex{}", conv::to_pascal_case(fn_name)),
299301
builder_lifetime: lifetime,
300-
builder_methods: Vec::with_capacity(default_fn_params.len()),
302+
builder_methods: Vec::with_capacity(default_params.len()),
301303
builder_fields: Vec::with_capacity(len),
302304
builder_args: Vec::with_capacity(len),
303305
builder_inits: Vec::with_capacity(len),
@@ -316,7 +318,6 @@ fn make_extender(
316318
let init = if let Some(value) = default_value {
317319
make_field_init(name, Some(value), conversion)
318320
} else {
319-
//quote! { #name }
320321
make_field_init(name, None, conversion)
321322
};
322323

@@ -336,7 +337,7 @@ fn make_extender(
336337
result.builder_inits.push(init);
337338
}
338339

339-
for param in default_fn_params {
340+
for param in default_params {
340341
let FnParam { name, type_, .. } = param;
341342
let param_type = type_.param_decl();
342343
let (_, field_needs_conversion) = default_extender_field_decl(type_, true);
@@ -397,9 +398,9 @@ fn make_field_init(
397398
match (conversion, expr) {
398399
(Conv::ObjectArg, Some(expr)) => quote! { #name: #expr.consume_object() },
399400
(Conv::ObjectArg, None) /*. */ => quote! { #name: #name.consume_object() },
400-
(Conv::Reference, Some(expr)) => quote! { #name: & #expr },
401-
(Conv::Reference, None) /*. */ => quote! { #name: & #name },
402-
(Conv::None | Conv::ReferenceWithDefault, Some(expr)) => quote! { #name: #expr },
403-
(Conv::None | Conv::ReferenceWithDefault, None) /*. */ => quote! { #name },
401+
402+
// Currently no differentiation between None|Reference|ReferenceWithDefault; this may change...
403+
(Conv::None | Conv::Reference | Conv::ReferenceWithDefault, Some(expr)) => quote! { #name: #expr },
404+
(Conv::None | Conv::Reference | Conv::ReferenceWithDefault, None) /*. */ => quote! { #name },
404405
}
405406
}

godot-core/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ compile_error!("Generating editor docs for Rust symbols requires at least Godot
4848
#[allow(clippy::let_unit_value)] // let args = ();
4949
#[allow(clippy::wrong_self_convention)] // to_string() is const
5050
#[allow(clippy::upper_case_acronyms)] // TODO remove this line once we transform names
51+
#[allow(clippy::needless_lifetimes)] // the following explicit lifetimes could be elided: 'a
5152
#[allow(unreachable_code, clippy::unimplemented)] // TODO remove once #153 is implemented
5253
mod gen {
5354
include!(concat!(env!("OUT_DIR"), "/mod.rs"));

godot-core/src/meta/godot_convert/mod.rs

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -95,18 +95,12 @@ pub trait FromGodot: Sized + GodotConvert {
9595
}
9696
}
9797

98-
// pub(crate) fn into_ffi<'v, T: ToGodot >(value: T) -> <T::ToVia<'v> as GodotType>::Ffi {
99-
// let by_ref = value.to_godot();
100-
// let ffi = by_ref.to_ffi();
101-
//
102-
// ffi
103-
// }
104-
98+
// Note: removing the implicit lifetime (by taking value: T instead of &T) causes issues due to allegedly returning a lifetime
99+
// to a local variable, even though the result Ffi is 'static by definition.
100+
#[allow(clippy::needless_lifetimes)] // eliding causes error: missing generics for associated type `godot_convert::ToGodot::ToVia`
105101
pub(crate) fn into_ffi<'v, T: ToGodot>(value: &'v T) -> <T::ToVia<'v> as GodotType>::Ffi {
106102
let by_ref = value.to_godot();
107-
let ffi = by_ref.to_ffi();
108-
109-
ffi
103+
by_ref.to_ffi()
110104
}
111105

112106
pub(crate) fn into_ffi_variant<T: ToGodot>(value: &T) -> Variant {

itest/rust/src/builtin_tests/containers/array_test.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ fn array_from_packed_array() {
5353
// This tests that the resulting array doesn't secretly have a runtime type assigned to it,
5454
// which is not reflected in our static type. It would make sense if it did, but Godot decided
5555
// otherwise: we get an untyped array.
56-
array.push(&GString::from("hi").to_variant());
56+
array.push(GString::from("hi").to_variant());
5757
assert_eq!(array, varray![42, "hi"]);
5858
}
5959

0 commit comments

Comments
 (0)