Skip to content

Commit f33fe1f

Browse files
authored
Merge pull request #900 from godot-rust/qol/engine-pass-by-ref
Pass-by-ref for non-`Copy` builtins (frontend)
2 parents 79ead60 + 5a626c5 commit f33fe1f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+687
-331
lines changed

examples/dodge-the-creeps/rust/src/player.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ impl Player {
2424
.base()
2525
.get_node_as::<CollisionShape2D>("CollisionShape2D");
2626

27-
collision_shape.set_deferred("disabled".into(), true.to_variant());
27+
collision_shape.set_deferred("disabled".into(), &true.to_variant());
2828
}
2929

3030
#[func]

godot-codegen/src/context.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,14 +64,19 @@ impl<'a> Context<'a> {
6464
}
6565

6666
// Populate class lookup by name
67-
println!("-- add engine class {}", class_name.description());
6867
engine_classes.insert(class_name.clone(), class);
6968

7069
// Populate derived-to-base relations
7170
if let Some(base) = class.inherits.as_ref() {
7271
let base_name = TyName::from_godot(base);
73-
println!(" -- inherits {}", base_name.description());
72+
println!(
73+
"* Add engine class {} <- inherits {}",
74+
class_name.description(),
75+
base_name.description()
76+
);
7477
ctx.inheritance_tree.insert(class_name.clone(), base_name);
78+
} else {
79+
println!("* Add engine class {}", class_name.description());
7580
}
7681

7782
// Populate notification constants (first, only for classes that declare them themselves).
@@ -238,6 +243,12 @@ impl<'a> Context<'a> {
238243
self.builtin_types.contains(ty_name)
239244
}
240245

246+
pub fn is_builtin_copy(&self, ty_name: &str) -> bool {
247+
debug_assert!(!ty_name.starts_with("Packed")); // Already handled separately.
248+
249+
!matches!(ty_name, "Variant" | "VariantArray" | "Dictionary")
250+
}
251+
241252
pub fn is_native_structure(&self, ty_name: &str) -> bool {
242253
self.native_structures_types.contains(ty_name)
243254
}

godot-codegen/src/conv/type_conversions.rs

Lines changed: 78 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use crate::util::ident;
2020
// ----------------------------------------------------------------------------------------------------------------------------------------------
2121
// Godot -> Rust types
2222

23+
/// Returns `(identifier, is_copy)` for a hardcoded Rust type, if it exists.
2324
fn to_hardcoded_rust_ident(full_ty: &GodotTy) -> Option<&str> {
2425
let ty = full_ty.ty.as_str();
2526
let meta = full_ty.meta.as_deref();
@@ -95,13 +96,25 @@ pub(crate) fn to_rust_type_abi(ty: &str, ctx: &mut Context) -> (RustTy, bool) {
9596
"Object*" => {
9697
is_obj = true;
9798
RustTy::RawPointer {
98-
inner: Box::new(RustTy::BuiltinIdent(ident("c_void"))),
99+
inner: Box::new(RustTy::BuiltinIdent {
100+
ty: ident("c_void"),
101+
is_copy: true,
102+
}),
99103
is_const: false,
100104
}
101105
}
102-
"int" => RustTy::BuiltinIdent(ident("i32")),
103-
"float" => RustTy::BuiltinIdent(ident("f32")),
104-
"double" => RustTy::BuiltinIdent(ident("f64")),
106+
"int" => RustTy::BuiltinIdent {
107+
ty: ident("i32"),
108+
is_copy: true,
109+
},
110+
"float" => RustTy::BuiltinIdent {
111+
ty: ident("f32"),
112+
is_copy: true,
113+
},
114+
"double" => RustTy::BuiltinIdent {
115+
ty: ident("f64"),
116+
is_copy: true,
117+
},
105118
_ => to_rust_type(ty, None, ctx),
106119
};
107120

@@ -137,6 +150,7 @@ fn to_rust_type_uncached(full_ty: &GodotTy, ctx: &mut Context) -> RustTy {
137150
if is_builtin_type_scalar(ty) {
138151
ident(ty)
139152
} else {
153+
// Convert as-is. Includes StringName and NodePath.
140154
TyName::from_godot(ty).rust_ty
141155
}
142156
}
@@ -163,7 +177,10 @@ fn to_rust_type_uncached(full_ty: &GodotTy, ctx: &mut Context) -> RustTy {
163177
// Only place where meta is relevant is here.
164178
if !ty.starts_with("typedarray::") {
165179
if let Some(hardcoded) = to_hardcoded_rust_ident(full_ty) {
166-
return RustTy::BuiltinIdent(ident(hardcoded));
180+
return RustTy::BuiltinIdent {
181+
ty: ident(hardcoded),
182+
is_copy: ctx.is_builtin_copy(hardcoded),
183+
};
167184
}
168185
}
169186

@@ -182,7 +199,10 @@ fn to_rust_type_uncached(full_ty: &GodotTy, ctx: &mut Context) -> RustTy {
182199
} else if let Some(packed_arr_ty) = ty.strip_prefix("Packed") {
183200
// Don't trigger on PackedScene ;P
184201
if packed_arr_ty.ends_with("Array") {
185-
return RustTy::BuiltinIdent(rustify_ty(ty));
202+
return RustTy::BuiltinIdent {
203+
ty: rustify_ty(ty),
204+
is_copy: false, // Packed arrays are not Copy.
205+
};
186206
}
187207
} else if let Some(elem_ty) = ty.strip_prefix("typedarray::") {
188208
let rust_elem_ty = to_rust_type(elem_ty, full_ty.meta.as_ref(), ctx);
@@ -200,8 +220,12 @@ fn to_rust_type_uncached(full_ty: &GodotTy, ctx: &mut Context) -> RustTy {
200220

201221
// Note: do not check if it's a known engine class, because that will not work in minimal mode (since not all classes are stored)
202222
if ctx.is_builtin(ty) || ctx.is_native_structure(ty) {
203-
// Unchanged
204-
RustTy::BuiltinIdent(rustify_ty(ty))
223+
// Unchanged.
224+
// Native structures might not all be Copy, but they should have value semantics.
225+
RustTy::BuiltinIdent {
226+
ty: rustify_ty(ty),
227+
is_copy: ctx.is_builtin_copy(ty),
228+
}
205229
} else {
206230
let ty = rustify_ty(ty);
207231
let qualified_class = quote! { crate::classes::#ty };
@@ -263,7 +287,9 @@ fn to_rust_expr_inner(expr: &str, ty: &RustTy, is_inner: bool) -> TokenStream {
263287
"{}" => return quote! { Dictionary::new() },
264288
"null" => {
265289
return match ty {
266-
RustTy::BuiltinIdent(ident) if ident == "Variant" => quote! { Variant::nil() },
290+
RustTy::BuiltinIdent { ty: ident, .. } if ident == "Variant" => {
291+
quote! { Variant::nil() }
292+
}
267293
RustTy::EngineClass { .. } => {
268294
quote! { Gd::null_arg() }
269295
}
@@ -273,8 +299,8 @@ fn to_rust_expr_inner(expr: &str, ty: &RustTy, is_inner: bool) -> TokenStream {
273299
// empty string appears only for Callable/Rid in 4.0; default ctor syntax in 4.1+
274300
"" | "RID()" | "Callable()" if !is_inner => {
275301
return match ty {
276-
RustTy::BuiltinIdent(ident) if ident == "Rid" => quote! { Rid::Invalid },
277-
RustTy::BuiltinIdent(ident) if ident == "Callable" => {
302+
RustTy::BuiltinIdent { ty: ident, .. } if ident == "Rid" => quote! { Rid::Invalid },
303+
RustTy::BuiltinIdent { ty: ident, .. } if ident == "Callable" => {
278304
quote! { Callable::invalid() }
279305
}
280306
_ => panic!("empty string not representable in target type {ty:?}"),
@@ -295,9 +321,11 @@ fn to_rust_expr_inner(expr: &str, ty: &RustTy, is_inner: bool) -> TokenStream {
295321
is_bitfield: false, ..
296322
} => quote! { crate::obj::EngineEnum::from_ord(#lit) },
297323

298-
RustTy::BuiltinIdent(ident) if ident == "Variant" => quote! { Variant::from(#lit) },
324+
RustTy::BuiltinIdent { ty: ident, .. } if ident == "Variant" => {
325+
quote! { Variant::from(#lit) }
326+
}
299327

300-
RustTy::BuiltinIdent(ident)
328+
RustTy::BuiltinIdent { ty: ident, .. }
301329
if ident == "i64" || ident == "f64" || unmap_meta(ty).is_some() =>
302330
{
303331
suffixed_lit(num, ident)
@@ -313,7 +341,9 @@ fn to_rust_expr_inner(expr: &str, ty: &RustTy, is_inner: bool) -> TokenStream {
313341
// Float literals (some floats already handled by integer literals)
314342
if let Ok(num) = expr.parse::<f64>() {
315343
return match ty {
316-
RustTy::BuiltinIdent(ident) if ident == "f64" || unmap_meta(ty).is_some() => {
344+
RustTy::BuiltinIdent { ty: ident, .. }
345+
if ident == "f64" || unmap_meta(ty).is_some() =>
346+
{
317347
suffixed_lit(num, ident)
318348
}
319349
_ if is_inner => {
@@ -331,7 +361,7 @@ fn to_rust_expr_inner(expr: &str, ty: &RustTy, is_inner: bool) -> TokenStream {
331361
quote! { #expr }
332362
} else {
333363
match ty {
334-
RustTy::BuiltinIdent(ident)
364+
RustTy::BuiltinIdent { ty: ident, .. }
335365
if ident == "GString" || ident == "StringName" || ident == "NodePath" =>
336366
{
337367
quote! { #ident::from(#expr) }
@@ -429,16 +459,28 @@ fn gdscript_to_rust_expr() {
429459
// The 'None' type is used to simulate absence of type information. Some tests are commented out, because this functionality is not
430460
// yet needed. If we ever want to reuse to_rust_expr() in other contexts, we could re-enable them.
431461

432-
let ty_int = RustTy::BuiltinIdent(ident("i64"));
462+
let ty_int = RustTy::BuiltinIdent {
463+
ty: ident("i64"),
464+
is_copy: true,
465+
};
433466
let ty_int = Some(&ty_int);
434467

435-
let ty_int_u16 = RustTy::BuiltinIdent(ident("u16"));
468+
let ty_int_u16 = RustTy::BuiltinIdent {
469+
ty: ident("u16"),
470+
is_copy: true,
471+
};
436472
let ty_int_u16 = Some(&ty_int_u16);
437473

438-
let ty_float = RustTy::BuiltinIdent(ident("f64"));
474+
let ty_float = RustTy::BuiltinIdent {
475+
ty: ident("f64"),
476+
is_copy: true,
477+
};
439478
let ty_float = Some(&ty_float);
440479

441-
let ty_float_f32 = RustTy::BuiltinIdent(ident("f32"));
480+
let ty_float_f32 = RustTy::BuiltinIdent {
481+
ty: ident("f32"),
482+
is_copy: true,
483+
};
442484
let ty_float_f32 = Some(&ty_float_f32);
443485

444486
let ty_enum = RustTy::EngineEnum {
@@ -455,7 +497,10 @@ fn gdscript_to_rust_expr() {
455497
};
456498
let ty_bitfield = Some(&ty_bitfield);
457499

458-
let ty_variant = RustTy::BuiltinIdent(ident("Variant"));
500+
let ty_variant = RustTy::BuiltinIdent {
501+
ty: ident("Variant"),
502+
is_copy: false,
503+
};
459504
let ty_variant = Some(&ty_variant);
460505

461506
// let ty_object = RustTy::EngineClass {
@@ -464,13 +509,22 @@ fn gdscript_to_rust_expr() {
464509
// };
465510
// let ty_object = Some(&ty_object);
466511

467-
let ty_string = RustTy::BuiltinIdent(ident("GString"));
512+
let ty_string = RustTy::BuiltinIdent {
513+
ty: ident("GString"),
514+
is_copy: true,
515+
};
468516
let ty_string = Some(&ty_string);
469517

470-
let ty_stringname = RustTy::BuiltinIdent(ident("StringName"));
518+
let ty_stringname = RustTy::BuiltinIdent {
519+
ty: ident("StringName"),
520+
is_copy: true,
521+
};
471522
let ty_stringname = Some(&ty_stringname);
472523

473-
let ty_nodepath = RustTy::BuiltinIdent(ident("NodePath"));
524+
let ty_nodepath = RustTy::BuiltinIdent {
525+
ty: ident("NodePath"),
526+
is_copy: true,
527+
};
474528
let ty_nodepath = Some(&ty_nodepath);
475529

476530
#[rustfmt::skip]
@@ -581,7 +635,7 @@ fn gdscript_to_rust_expr() {
581635
///
582636
/// Avoids dragging along the meta type through [`RustTy::BuiltinIdent`].
583637
pub(crate) fn unmap_meta(rust_ty: &RustTy) -> Option<Ident> {
584-
let RustTy::BuiltinIdent(rust_ty) = rust_ty else {
638+
let RustTy::BuiltinIdent { ty: rust_ty, .. } = rust_ty else {
585639
return None;
586640
};
587641

godot-codegen/src/generator/builtins.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,10 @@ fn make_builtin_class(
9595
) -> GeneratedBuiltin {
9696
let godot_name = &class.name().godot_ty;
9797

98-
let RustTy::BuiltinIdent(outer_class) = conv::to_rust_type(godot_name, None, ctx) else {
98+
let RustTy::BuiltinIdent {
99+
ty: outer_class, ..
100+
} = conv::to_rust_type(godot_name, None, ctx)
101+
else {
99102
panic!("Rust type `{}` categorized wrong", godot_name)
100103
};
101104
let inner_class = class.inner_name();

0 commit comments

Comments
 (0)