Skip to content

Commit 28e158c

Browse files
committed
Add GodotType::ToFfi<'f> to keep by-ref also in FFI layer
Refactors some related parts: * GodotNullableFfi now has a null() associated function. * Correct usage of as_arg() and move_return_ptr(). * into_ffi() helper needs marshal_args! macro, since it has transient borrowed state.
1 parent b26e114 commit 28e158c

File tree

12 files changed

+279
-109
lines changed

12 files changed

+279
-109
lines changed

godot-core/src/builtin/collections/array.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use crate::builtin::*;
1212
use crate::meta::error::{ConvertError, FromGodotError, FromVariantError};
1313
use crate::meta::{
1414
ArrayElement, ArrayTypeInfo, FromGodot, GodotConvert, GodotFfiVariant, GodotType,
15-
PropertyHintInfo, ToGodot,
15+
PropertyHintInfo, RefArg, ToGodot,
1616
};
1717
use crate::registry::property::{Export, Var};
1818
use godot_ffi as sys;
@@ -1047,10 +1047,11 @@ impl<T: ArrayElement> Drop for Array<T> {
10471047
impl<T: ArrayElement> GodotType for Array<T> {
10481048
type Ffi = Self;
10491049

1050-
fn to_ffi(&self) -> Self::Ffi {
1051-
// SAFETY: we may pass type-transmuted arrays to FFI (e.g. Array<T> as Array<Variant>). This would fail the regular
1052-
// type-check in clone(), so we disable it. Type invariants are upheld by the "front-end" APIs.
1053-
unsafe { self.clone_unchecked() }
1050+
type ToFfi<'f> = RefArg<'f, Array<T>>
1051+
where Self: 'f;
1052+
1053+
fn to_ffi(&self) -> Self::ToFfi<'_> {
1054+
RefArg::new(self)
10541055
}
10551056

10561057
fn into_ffi(self) -> Self::Ffi {

godot-core/src/builtin/variant/impls.rs

Lines changed: 47 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ use super::*;
99
use crate::builtin::*;
1010
use crate::global;
1111
use crate::meta::error::{ConvertError, FromVariantError};
12-
use crate::meta::{ArrayElement, GodotFfiVariant, GodotType, PropertyHintInfo, PropertyInfo};
12+
use crate::meta::{
13+
ArrayElement, GodotFfiVariant, GodotType, PropertyHintInfo, PropertyInfo, RefArg,
14+
};
1315
use godot_ffi as sys;
1416
// For godot-cpp, see https://github.com/godotengine/godot-cpp/blob/master/include/godot_cpp/core/type_info.hpp.
1517

@@ -22,7 +24,15 @@ use godot_ffi as sys;
2224
//
2325
// Therefore, we can use `init` to indicate when it must be initialized in 4.0.
2426
macro_rules! impl_ffi_variant {
25-
($T:ty, $from_fn:ident, $to_fn:ident $(; $godot_type_name:ident)?) => {
27+
(ref $T:ty, $from_fn:ident, $to_fn:ident $(; $GodotTy:ident)?) => {
28+
impl_ffi_variant!(@impls by_ref; $T, $from_fn, $to_fn $(; $GodotTy)?);
29+
};
30+
($T:ty, $from_fn:ident, $to_fn:ident $(; $GodotTy:ident)?) => {
31+
impl_ffi_variant!(@impls by_val; $T, $from_fn, $to_fn $(; $GodotTy)?);
32+
};
33+
34+
// Implementations
35+
(@impls $by_ref_or_val:ident; $T:ty, $from_fn:ident, $to_fn:ident $(; $GodotTy:ident)?) => {
2636
impl GodotFfiVariant for $T {
2737
fn ffi_to_variant(&self) -> Variant {
2838
let variant = unsafe {
@@ -58,10 +68,7 @@ macro_rules! impl_ffi_variant {
5868

5969
impl GodotType for $T {
6070
type Ffi = Self;
61-
62-
fn to_ffi(&self) -> Self::Ffi {
63-
self.clone()
64-
}
71+
impl_ffi_variant!(@assoc_to_ffi $by_ref_or_val);
6572

6673
fn into_ffi(self) -> Self::Ffi {
6774
self
@@ -71,7 +78,7 @@ macro_rules! impl_ffi_variant {
7178
Ok(ffi)
7279
}
7380

74-
impl_ffi_variant!(@godot_type_name $T $(, $godot_type_name)?);
81+
impl_ffi_variant!(@godot_type_name $T $(, $GodotTy)?);
7582
}
7683

7784
impl ArrayElement for $T {}
@@ -88,6 +95,22 @@ macro_rules! impl_ffi_variant {
8895
stringify!($godot_type_name).into()
8996
}
9097
};
98+
99+
(@assoc_to_ffi by_ref) => {
100+
type ToFfi<'a> = RefArg<'a, Self>;
101+
102+
fn to_ffi(&self) -> Self::ToFfi<'_> {
103+
RefArg::new(self)
104+
}
105+
};
106+
107+
(@assoc_to_ffi by_val) => {
108+
type ToFfi<'a> = Self;
109+
110+
fn to_ffi(&self) -> Self::ToFfi<'_> {
111+
self.clone()
112+
}
113+
};
91114
}
92115

93116
// ----------------------------------------------------------------------------------------------------------------------------------------------
@@ -116,17 +139,17 @@ mod impls {
116139
impl_ffi_variant!(GString, string_to_variant, string_from_variant; String);
117140
impl_ffi_variant!(StringName, string_name_to_variant, string_name_from_variant);
118141
impl_ffi_variant!(NodePath, node_path_to_variant, node_path_from_variant);
119-
impl_ffi_variant!(PackedByteArray, packed_byte_array_to_variant, packed_byte_array_from_variant);
120-
impl_ffi_variant!(PackedInt32Array, packed_int32_array_to_variant, packed_int32_array_from_variant);
121-
impl_ffi_variant!(PackedInt64Array, packed_int64_array_to_variant, packed_int64_array_from_variant);
122-
impl_ffi_variant!(PackedFloat32Array, packed_float32_array_to_variant, packed_float32_array_from_variant);
123-
impl_ffi_variant!(PackedFloat64Array, packed_float64_array_to_variant, packed_float64_array_from_variant);
124-
impl_ffi_variant!(PackedStringArray, packed_string_array_to_variant, packed_string_array_from_variant);
125-
impl_ffi_variant!(PackedVector2Array, packed_vector2_array_to_variant, packed_vector2_array_from_variant);
126-
impl_ffi_variant!(PackedVector3Array, packed_vector3_array_to_variant, packed_vector3_array_from_variant);
142+
impl_ffi_variant!(ref PackedByteArray, packed_byte_array_to_variant, packed_byte_array_from_variant);
143+
impl_ffi_variant!(ref PackedInt32Array, packed_int32_array_to_variant, packed_int32_array_from_variant);
144+
impl_ffi_variant!(ref PackedInt64Array, packed_int64_array_to_variant, packed_int64_array_from_variant);
145+
impl_ffi_variant!(ref PackedFloat32Array, packed_float32_array_to_variant, packed_float32_array_from_variant);
146+
impl_ffi_variant!(ref PackedFloat64Array, packed_float64_array_to_variant, packed_float64_array_from_variant);
147+
impl_ffi_variant!(ref PackedStringArray, packed_string_array_to_variant, packed_string_array_from_variant);
148+
impl_ffi_variant!(ref PackedVector2Array, packed_vector2_array_to_variant, packed_vector2_array_from_variant);
149+
impl_ffi_variant!(ref PackedVector3Array, packed_vector3_array_to_variant, packed_vector3_array_from_variant);
127150
#[cfg(since_api = "4.3")]
128-
impl_ffi_variant!(PackedVector4Array, packed_vector4_array_to_variant, packed_vector4_array_from_variant);
129-
impl_ffi_variant!(PackedColorArray, packed_color_array_to_variant, packed_color_array_from_variant);
151+
impl_ffi_variant!(ref PackedVector4Array, packed_vector4_array_to_variant, packed_vector4_array_from_variant);
152+
impl_ffi_variant!(ref PackedColorArray, packed_color_array_to_variant, packed_color_array_from_variant);
130153
impl_ffi_variant!(Plane, plane_to_variant, plane_from_variant);
131154
impl_ffi_variant!(Projection, projection_to_variant, projection_from_variant);
132155
impl_ffi_variant!(Rid, rid_to_variant, rid_from_variant; RID);
@@ -135,7 +158,7 @@ mod impls {
135158
impl_ffi_variant!(Signal, signal_to_variant, signal_from_variant);
136159
impl_ffi_variant!(Transform2D, transform_2d_to_variant, transform_2d_from_variant);
137160
impl_ffi_variant!(Transform3D, transform_3d_to_variant, transform_3d_from_variant);
138-
impl_ffi_variant!(Dictionary, dictionary_to_variant, dictionary_from_variant);
161+
impl_ffi_variant!(ref Dictionary, dictionary_to_variant, dictionary_from_variant);
139162

140163
}
141164

@@ -162,9 +185,10 @@ impl GodotFfiVariant for () {
162185
}
163186

164187
impl GodotType for () {
165-
type Ffi = Self;
188+
type Ffi = ();
189+
type ToFfi<'a> = ();
166190

167-
fn to_ffi(&self) -> Self::Ffi {}
191+
fn to_ffi(&self) -> Self::ToFfi<'_> {}
168192

169193
fn into_ffi(self) -> Self::Ffi {}
170194

@@ -189,9 +213,10 @@ impl GodotFfiVariant for Variant {
189213

190214
impl GodotType for Variant {
191215
type Ffi = Variant;
216+
type ToFfi<'a> = RefArg<'a, Variant>;
192217

193-
fn to_ffi(&self) -> Self::Ffi {
194-
self.clone()
218+
fn to_ffi(&self) -> Self::ToFfi<'_> {
219+
RefArg::new(self)
195220
}
196221

197222
fn into_ffi(self) -> Self::Ffi {

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

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,13 @@ impl<T> GodotType for Option<T>
2525
where
2626
T: GodotType,
2727
T::Ffi: GodotNullableFfi,
28+
for<'f> T::ToFfi<'f>: GodotNullableFfi,
2829
{
2930
type Ffi = T::Ffi;
3031

31-
fn to_ffi(&self) -> Self::Ffi {
32+
type ToFfi<'f> = T::ToFfi<'f>;
33+
34+
fn to_ffi(&self) -> Self::ToFfi<'_> {
3235
GodotNullableFfi::flatten_option(self.as_ref().map(|t| t.to_ffi()))
3336
}
3437

@@ -91,7 +94,11 @@ where
9194
impl<T: ToGodot> ToGodot for Option<T>
9295
where
9396
Option<T::Via>: GodotType,
94-
for<'f> T::ToVia<'f>: GodotType<Ffi: GodotNullableFfi>,
97+
for<'v, 'f> T::ToVia<'v>: GodotType<
98+
// Associated types need to be nullable.
99+
Ffi: GodotNullableFfi,
100+
ToFfi<'f>: GodotNullableFfi,
101+
>,
95102
{
96103
type ToVia<'v> = Option<T::ToVia<'v>>
97104
// type ToVia<'v> = Self::Via
@@ -155,8 +162,9 @@ macro_rules! impl_godot_scalar {
155162
($T:ty as $Via:ty, $err:path, $param_metadata:expr) => {
156163
impl GodotType for $T {
157164
type Ffi = $Via;
165+
type ToFfi<'f> = $Via;
158166

159-
fn to_ffi(&self) -> Self::Ffi {
167+
fn to_ffi(&self) -> Self::ToFfi<'_> {
160168
(*self).into()
161169
}
162170

@@ -188,8 +196,9 @@ macro_rules! impl_godot_scalar {
188196
($T:ty as $Via:ty, $param_metadata:expr; lossy) => {
189197
impl GodotType for $T {
190198
type Ffi = $Via;
199+
type ToFfi<'f> = $Via;
191200

192-
fn to_ffi(&self) -> Self::Ffi {
201+
fn to_ffi(&self) -> Self::ToFfi<'_> {
193202
*self as $Via
194203
}
195204

@@ -289,8 +298,9 @@ impl_godot_scalar!(
289298

290299
impl GodotType for u64 {
291300
type Ffi = i64;
301+
type ToFfi<'f> = i64;
292302

293-
fn to_ffi(&self) -> Self::Ffi {
303+
fn to_ffi(&self) -> Self::ToFfi<'_> {
294304
*self as i64
295305
}
296306

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

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ pub trait GodotConvert {
4040
///
4141
/// Please read the [`godot::meta` module docs][crate::meta] for further information about conversions.
4242
pub trait ToGodot: Sized + GodotConvert {
43+
/// Target type of [`to_godot()`](ToGodot::to_godot), which differs from [`Via`](GodotConvert::Via) for pass-by-reference types.
4344
type ToVia<'v>: GodotType
4445
where
4546
Self: 'v;
@@ -95,16 +96,10 @@ pub trait FromGodot: Sized + GodotConvert {
9596
}
9697
}
9798

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`
101-
pub(crate) fn into_ffi<'v, T: ToGodot>(value: &'v T) -> <T::ToVia<'v> as GodotType>::Ffi {
102-
let by_ref = value.to_godot();
103-
by_ref.to_ffi()
104-
}
105-
10699
pub(crate) fn into_ffi_variant<T: ToGodot>(value: &T) -> Variant {
107-
GodotFfiVariant::ffi_to_variant(&into_ffi(value))
100+
let via = value.to_godot();
101+
let ffi = via.to_ffi();
102+
GodotFfiVariant::ffi_to_variant(&ffi)
108103
}
109104

110105
pub(crate) fn try_from_ffi<T: FromGodot>(

godot-core/src/meta/ref_arg.rs

Lines changed: 104 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,35 @@
44
* License, v. 2.0. If a copy of the MPL was not distributed with this
55
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
66
*/
7+
use crate::builtin::Variant;
78
use crate::meta::error::ConvertError;
8-
use crate::meta::{FromGodot, GodotConvert, ToGodot};
9+
use crate::meta::{FromGodot, GodotConvert, GodotFfiVariant, ToGodot};
10+
use crate::sys;
11+
use godot_ffi::{GodotFfi, GodotNullableFfi, PtrcallType};
912
use std::fmt;
1013

1114
pub struct RefArg<'r, T> {
12-
pub(crate) shared_ref: &'r T,
15+
/// Only `None` if `T: GodotNullableFfi` and `T::is_null()` is true.
16+
shared_ref: Option<&'r T>,
1317
}
1418

1519
impl<'r, T> RefArg<'r, T> {
1620
pub fn new(shared_ref: &'r T) -> Self {
17-
RefArg { shared_ref }
21+
RefArg {
22+
shared_ref: Some(shared_ref),
23+
}
1824
}
1925
}
2026

27+
macro_rules! wrong_direction {
28+
($fn:ident) => {
29+
unreachable!(concat!(
30+
stringify!($fn),
31+
": RefArg should only be passed *to* Godot, not *from*."
32+
))
33+
};
34+
}
35+
2136
impl<'r, T> GodotConvert for RefArg<'r, T>
2237
where
2338
T: GodotConvert,
@@ -33,7 +48,11 @@ where
3348
where Self: 'v;
3449

3550
fn to_godot(&self) -> Self::ToVia<'_> {
36-
self.shared_ref.to_godot()
51+
let shared_ref = self
52+
.shared_ref
53+
.expect("Objects are currently mapped through ObjectArg; RefArg shouldn't be null");
54+
55+
shared_ref.to_godot()
3756
}
3857
}
3958

@@ -43,7 +62,7 @@ where
4362
T: FromGodot,
4463
{
4564
fn try_from_godot(_via: Self::Via) -> Result<Self, ConvertError> {
46-
unreachable!("RefArg should only be passed *to* Godot, not *from*.")
65+
wrong_direction!(try_from_godot)
4766
}
4867
}
4968

@@ -55,3 +74,83 @@ where
5574
write!(f, "&{:?}", self.shared_ref)
5675
}
5776
}
77+
78+
// SAFETY: delegated to T.
79+
unsafe impl<'r, T> GodotFfi for RefArg<'r, T>
80+
where
81+
T: GodotFfi,
82+
{
83+
fn variant_type() -> sys::VariantType {
84+
T::variant_type()
85+
}
86+
87+
unsafe fn new_from_sys(_ptr: sys::GDExtensionConstTypePtr) -> Self {
88+
wrong_direction!(new_from_sys)
89+
}
90+
91+
unsafe fn new_with_uninit(_init_fn: impl FnOnce(sys::GDExtensionUninitializedTypePtr)) -> Self {
92+
wrong_direction!(new_with_uninit)
93+
}
94+
95+
unsafe fn new_with_init(_init_fn: impl FnOnce(sys::GDExtensionTypePtr)) -> Self {
96+
wrong_direction!(new_with_init)
97+
}
98+
99+
fn sys(&self) -> sys::GDExtensionConstTypePtr {
100+
match self.shared_ref {
101+
Some(r) => r.sys(),
102+
None => std::ptr::null(),
103+
}
104+
}
105+
106+
fn sys_mut(&mut self) -> sys::GDExtensionTypePtr {
107+
unreachable!("RefArg::sys_mut() currently not used by FFI marshalling layer, but only by specific functions");
108+
}
109+
110+
// This function must be overridden; the default delegating to sys() is wrong for e.g. RawGd<T>.
111+
// See also other manual overrides of as_arg_ptr().
112+
fn as_arg_ptr(&self) -> sys::GDExtensionConstTypePtr {
113+
match self.shared_ref {
114+
Some(r) => r.as_arg_ptr(),
115+
None => std::ptr::null(),
116+
}
117+
}
118+
119+
unsafe fn from_arg_ptr(_ptr: sys::GDExtensionTypePtr, _call_type: PtrcallType) -> Self {
120+
wrong_direction!(from_arg_ptr)
121+
}
122+
123+
unsafe fn move_return_ptr(self, _dst: sys::GDExtensionTypePtr, _call_type: PtrcallType) {
124+
// This one is implemented, because it's used for return types implementing ToGodot.
125+
unreachable!("Calling RefArg::move_return_ptr is a mistake, as RefArg is intended only for arguments. Use the underlying value type.");
126+
}
127+
}
128+
129+
impl<'r, T> GodotFfiVariant for RefArg<'r, T>
130+
where
131+
T: GodotFfiVariant,
132+
{
133+
fn ffi_to_variant(&self) -> Variant {
134+
match self.shared_ref {
135+
Some(r) => r.ffi_to_variant(),
136+
None => Variant::nil(),
137+
}
138+
}
139+
140+
fn ffi_from_variant(_variant: &Variant) -> Result<Self, ConvertError> {
141+
wrong_direction!(ffi_from_variant)
142+
}
143+
}
144+
145+
impl<'r, T> GodotNullableFfi for RefArg<'r, T>
146+
where
147+
T: GodotNullableFfi,
148+
{
149+
fn null() -> Self {
150+
RefArg { shared_ref: None }
151+
}
152+
153+
fn is_null(&self) -> bool {
154+
self.shared_ref.map(|r| r.is_null()).unwrap_or(true)
155+
}
156+
}

0 commit comments

Comments
 (0)