Skip to content

Commit 5789b22

Browse files
authored
Merge pull request #1193 from TitanNano/jovan/eliminate_param_type
Allow custom types to be passed as `impl AsArg<T>`
2 parents 46f275a + 90103c4 commit 5789b22

File tree

10 files changed

+193
-206
lines changed

10 files changed

+193
-206
lines changed

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

Lines changed: 7 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ use crate::builtin::*;
1212
use crate::meta;
1313
use crate::meta::error::{ConvertError, FromGodotError, FromVariantError};
1414
use crate::meta::{
15-
element_godot_type_name, element_variant_type, ArrayElement, ArrayTypeInfo, AsArg, ClassName,
16-
CowArg, ExtVariantType, FromGodot, GodotConvert, GodotFfiVariant, GodotType, ParamType,
15+
element_godot_type_name, element_variant_type, ArrayElement, ArrayTypeInfo, AsArg, ByRef,
16+
ClassName, ExtVariantType, FromGodot, GodotConvert, GodotFfiVariant, GodotType, ParamType,
1717
PropertyHintInfo, RefArg, ToGodot,
1818
};
1919
use crate::obj::{bounds, Bounds, DynGd, Gd, GodotClass};
@@ -675,7 +675,7 @@ impl<T: ArrayElement> Array<T> {
675675
// We need one dummy element of type T, because Godot's bsearch_custom() checks types (so Variant::nil() can't be passed).
676676
// Optimization: roundtrip Variant -> T -> Variant could be avoided, but anyone needing speed would use Rust binary search...
677677
let ignored_value = self.at(0);
678-
let ignored_value = <T as ParamType>::owned_to_arg(ignored_value);
678+
let ignored_value = meta::val_into_arg(ignored_value); //AsArg::into_arg(&ignored_value);
679679

680680
let godot_comparator = |args: &[&Variant]| {
681681
let value = T::from_variant(args[0]);
@@ -1118,29 +1118,8 @@ unsafe impl<T: ArrayElement> GodotFfi for Array<T> {
11181118
// Only implement for untyped arrays; typed arrays cannot be nested in Godot.
11191119
impl ArrayElement for VariantArray {}
11201120

1121-
impl<'r, T: ArrayElement> AsArg<Array<T>> for &'r Array<T> {
1122-
fn into_arg<'cow>(self) -> CowArg<'cow, Array<T>>
1123-
where
1124-
'r: 'cow, // Original reference must be valid for at least as long as the returned cow.
1125-
{
1126-
CowArg::Borrowed(self)
1127-
}
1128-
}
1129-
11301121
impl<T: ArrayElement> ParamType for Array<T> {
1131-
type Arg<'v> = CowArg<'v, Self>;
1132-
1133-
fn owned_to_arg<'v>(self) -> Self::Arg<'v> {
1134-
CowArg::Owned(self)
1135-
}
1136-
1137-
fn arg_to_ref<'r>(arg: &'r Self::Arg<'_>) -> &'r Self {
1138-
arg.cow_as_ref()
1139-
}
1140-
1141-
fn arg_into_owned(arg: Self::Arg<'_>) -> Self {
1142-
arg.cow_into_owned()
1143-
}
1122+
type ArgPassing = ByRef;
11441123
}
11451124

11461125
impl<T: ArrayElement> GodotConvert for Array<T> {
@@ -1436,15 +1415,16 @@ impl<T: ArrayElement + ToGodot> FromIterator<T> for Array<T> {
14361415
}
14371416

14381417
/// Extends a `Array` with the contents of an iterator.
1439-
impl<T: ArrayElement + ToGodot> Extend<T> for Array<T> {
1418+
impl<T: ArrayElement> Extend<T> for Array<T> {
14401419
fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
14411420
// Unfortunately the GDExtension API does not offer the equivalent of `Vec::reserve`.
14421421
// Otherwise, we could use it to pre-allocate based on `iter.size_hint()`.
14431422
//
14441423
// A faster implementation using `resize()` and direct pointer writes might still be possible.
14451424
// Note that this could technically also use iter(), since no moves need to happen (however Extend requires IntoIterator).
14461425
for item in iter.into_iter() {
1447-
self.push(ParamType::owned_to_arg(item));
1426+
// self.push(AsArg::into_arg(&item));
1427+
self.push(meta::val_into_arg(item));
14481428
}
14491429
}
14501430
}

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

Lines changed: 79 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
*/
77

88
use crate::builtin::{GString, NodePath, StringName};
9-
use crate::meta::{sealed, CowArg};
9+
use crate::meta::sealed::Sealed;
10+
use crate::meta::{CowArg, ToGodot};
1011
use std::ffi::CStr;
1112

1213
/// Implicit conversions for arguments passed to Godot APIs.
@@ -15,19 +16,17 @@ use std::ffi::CStr;
1516
/// this trait is implemented more conservatively.
1617
///
1718
/// As a result, `AsArg<T>` is currently only implemented for certain argument types:
18-
/// - `T` for by-value builtins (typically `Copy`): `i32`, `bool`, `Vector3`, `Transform2D`, ...
19-
/// - `&T` for by-ref builtins: `GString`, `Array`, `Dictionary`, `Packed*Array`, `Variant`...
19+
/// - `T` for by-value built-ins (typically `Copy`): `i32`, `bool`, `Vector3`, `Transform2D`, ...
20+
/// - `&T` for by-ref built-ins: `GString`, `Array`, `Dictionary`, `Packed*Array`, `Variant`...
2021
/// - `&str`, `&String` additionally for string types `GString`, `StringName`, `NodePath`.
2122
///
2223
/// See also the [`AsObjectArg`][crate::meta::AsObjectArg] trait which is specialized for object arguments. It may be merged with `AsArg`
2324
/// in the future.
2425
///
2526
/// # Pass by value
26-
/// Implicitly converting from `T` for by-ref builtins is explicitly not supported. This emphasizes that there is no need to consume the object,
27+
/// Implicitly converting from `T` for by-ref built-ins is explicitly not supported. This emphasizes that there is no need to consume the object,
2728
/// thus discourages unnecessary cloning.
2829
///
29-
/// If you need to pass owned values in generic code, you can use [`ParamType::owned_to_arg()`].
30-
///
3130
/// # Performance for strings
3231
/// Godot has three string types: [`GString`], [`StringName`] and [`NodePath`]. Conversions between those three, as well as between `String` and
3332
/// them, is generally expensive because of allocations, re-encoding, validations, hashing, etc. While this doesn't matter for a few strings
@@ -45,24 +44,59 @@ use std::ffi::CStr;
4544
/// `AsArg` is meant to be used from the function call site, not the declaration site. If you declare a parameter as `impl AsArg<...>` yourself,
4645
/// you can only forward it as-is to a Godot API -- there are no stable APIs to access the inner object yet.
4746
///
48-
/// Furthermore, there is currently no benefit in implementing `AsArg` for your own types, as it's only used by Godot APIs which don't accept
49-
/// custom types. Classes are already supported through upcasting and [`AsObjectArg`][crate::meta::AsObjectArg].
47+
/// If you want to pass your own types to a Godot API i.e. to emit a signal, you should implement the [`ParamType`] trait.
5048
#[diagnostic::on_unimplemented(
5149
message = "Argument of type `{Self}` cannot be passed to an `impl AsArg<{T}>` parameter",
5250
note = "if you pass by value, consider borrowing instead.",
5351
note = "GString/StringName/NodePath aren't implicitly convertible for performance reasons; use their `arg()` method.",
5452
note = "see also `AsArg` docs: https://godot-rust.github.io/docs/gdext/master/godot/meta/trait.AsArg.html"
5553
)]
56-
pub trait AsArg<T: ParamType>
54+
pub trait AsArg<T: ToGodot>
5755
where
5856
Self: Sized,
5957
{
58+
// The usage of the CowArg return type introduces a small runtime penalty for values that implement Copy. Currently, the usage
59+
// ergonomics out weigh the runtime cost. Using the CowArg allows us to create a blanket implementation of the trait for all types that
60+
// implement ToGodot.
6061
#[doc(hidden)]
61-
fn into_arg<'r>(self) -> <T as ParamType>::Arg<'r>
62+
fn into_arg<'r>(self) -> CowArg<'r, T>
6263
where
6364
Self: 'r;
6465
}
6566

67+
/// Generic abstraction over `T` and `&T` that should be passed as `AsArg<T>`.
68+
#[doc(hidden)]
69+
pub fn val_into_arg<'r, T>(arg: T) -> impl AsArg<T> + 'r
70+
where
71+
T: ToGodot + 'r,
72+
{
73+
CowArg::Owned(arg)
74+
}
75+
76+
impl<T> AsArg<T> for &T
77+
where
78+
T: ToGodot + ParamType<ArgPassing = ByRef>,
79+
{
80+
fn into_arg<'r>(self) -> CowArg<'r, T>
81+
where
82+
Self: 'r,
83+
{
84+
CowArg::Borrowed(self)
85+
}
86+
}
87+
88+
impl<T> AsArg<T> for T
89+
where
90+
T: ToGodot + ParamType<ArgPassing = ByValue>,
91+
{
92+
fn into_arg<'r>(self) -> CowArg<'r, T>
93+
where
94+
Self: 'r,
95+
{
96+
CowArg::Owned(self)
97+
}
98+
}
99+
66100
// ----------------------------------------------------------------------------------------------------------------------------------------------
67101
// Blanket impls
68102

@@ -81,7 +115,7 @@ macro_rules! arg_into_ref {
81115
};
82116
($arg_variable:ident: $T:ty) => {
83117
let $arg_variable = $arg_variable.into_arg();
84-
let $arg_variable: &$T = $crate::meta::ParamType::arg_to_ref(&$arg_variable);
118+
let $arg_variable: &$T = $arg_variable.cow_as_ref();
85119
};
86120
}
87121

@@ -102,71 +136,24 @@ macro_rules! arg_into_owned {
102136
};
103137
(infer $arg_variable:ident) => {
104138
let $arg_variable = $arg_variable.into_arg();
105-
let $arg_variable = $crate::meta::ParamType::arg_into_owned($arg_variable);
139+
let $arg_variable = $arg_variable.cow_into_owned();
106140
};
107141
}
108142

109143
#[macro_export]
110144
macro_rules! impl_asarg_by_value {
111145
($T:ty) => {
112-
impl $crate::meta::AsArg<$T> for $T {
113-
fn into_arg<'r>(self) -> <$T as $crate::meta::ParamType>::Arg<'r> {
114-
// Moves value (but typically a Copy type).
115-
self
116-
}
117-
}
118-
119146
impl $crate::meta::ParamType for $T {
120-
type Arg<'v> = $T;
121-
122-
fn owned_to_arg<'v>(self) -> Self::Arg<'v> {
123-
self
124-
}
125-
126-
fn arg_to_ref<'r>(arg: &'r Self::Arg<'_>) -> &'r Self {
127-
arg
128-
}
129-
130-
fn arg_into_owned(arg: Self::Arg<'_>) -> Self {
131-
arg
132-
}
147+
type ArgPassing = $crate::meta::ByValue;
133148
}
134149
};
135150
}
136151

137152
#[macro_export]
138153
macro_rules! impl_asarg_by_ref {
139154
($T:ty) => {
140-
impl<'r> $crate::meta::AsArg<$T> for &'r $T {
141-
// 1 rustfmt + 1 rustc problems (bugs?) here:
142-
// - formatting doesn't converge; `where` keeps being further indented on each run.
143-
// - a #[rustfmt::skip] annotation over the macro causes a compile error when mentioning `crate::impl_asarg_by_ref`.
144-
// "macro-expanded `macro_export` macros from the current crate cannot be referred to by absolute paths"
145-
// Thus, keep `where` on same line.
146-
// type ArgType<'v> = &'v $T where Self: 'v;
147-
148-
fn into_arg<'cow>(self) -> <$T as $crate::meta::ParamType>::Arg<'cow>
149-
where
150-
'r: 'cow, // Original reference must be valid for at least as long as the returned cow.
151-
{
152-
$crate::meta::CowArg::Borrowed(self)
153-
}
154-
}
155-
156155
impl $crate::meta::ParamType for $T {
157-
type Arg<'v> = $crate::meta::CowArg<'v, $T>;
158-
159-
fn owned_to_arg<'v>(self) -> Self::Arg<'v> {
160-
$crate::meta::CowArg::Owned(self)
161-
}
162-
163-
fn arg_to_ref<'r>(arg: &'r Self::Arg<'_>) -> &'r Self {
164-
arg.cow_as_ref()
165-
}
166-
167-
fn arg_into_owned(arg: Self::Arg<'_>) -> Self {
168-
arg.cow_into_owned()
169-
}
156+
type ArgPassing = $crate::meta::ByRef;
170157
}
171158
};
172159
}
@@ -182,7 +169,7 @@ macro_rules! declare_arg_method {
182169
pub fn arg<T>(&self) -> impl $crate::meta::AsArg<T>
183170
where
184171
for<'a> T: From<&'a Self>
185-
+ $crate::meta::ParamType<Arg<'a> = $crate::meta::CowArg<'a, T>>
172+
+ $crate::meta::ToGodot
186173
+ 'a,
187174
{
188175
$crate::meta::CowArg::Owned(T::from(self))
@@ -199,7 +186,7 @@ macro_rules! declare_arg_method {
199186
/// This is necessary for packed array dispatching to different "inner" backend signatures.
200187
impl<T> AsArg<T> for CowArg<'_, T>
201188
where
202-
for<'r> T: ParamType<Arg<'r> = CowArg<'r, T>> + 'r,
189+
for<'r> T: ToGodot,
203190
{
204191
fn into_arg<'r>(self) -> CowArg<'r, T>
205192
where
@@ -271,37 +258,37 @@ impl AsArg<NodePath> for &String {
271258
// ----------------------------------------------------------------------------------------------------------------------------------------------
272259

273260
/// Implemented for all parameter types `T` that are allowed to receive [impl `AsArg<T>`][AsArg].
261+
///
262+
/// **Deprecated**: This trait is considered deprecated and will be removed in 0.4. It is still required to be implemented by types that should
263+
/// be passed `AsArg` in the current version, though.
264+
//
274265
// ParamType used to be a subtrait of GodotType, but this can be too restrictive. For example, DynGd is not a "Godot canonical type"
275266
// (GodotType), however it's still useful to store it in arrays -- which requires AsArg and subsequently ParamType.
276-
pub trait ParamType: sealed::Sealed + Sized + 'static
267+
//
268+
// TODO(v0.4): merge ParamType::ArgPassing into ToGodot::ToVia, reducing redundancy on user side.
269+
pub trait ParamType: ToGodot + Sized + 'static
277270
// GodotType bound not required right now, but conceptually should always be the case.
278271
{
279-
/// Canonical argument passing type, either `T` or an internally-used CoW type.
280-
///
281-
/// The general rule is that `Copy` types are passed by value, while the rest is passed by reference.
282-
///
283-
/// This associated type is closely related to [`ToGodot::ToVia<'v>`][crate::meta::ToGodot::ToVia] and may be reorganized in the future.
284-
#[doc(hidden)]
285-
type Arg<'v>: AsArg<Self>
286-
where
287-
Self: 'v;
288-
289-
/// Converts an owned value to the canonical argument type, which can be passed to [`impl AsArg<T>`][AsArg].
290-
///
291-
/// Useful in generic contexts where only a value is available, and one doesn't want to dispatch between value/reference.
292-
///
293-
/// You should not rely on the exact return type, as it may change in future versions; treat it like `impl AsArg<Self>`.
294-
fn owned_to_arg<'v>(self) -> Self::Arg<'v>;
295-
296-
/// Converts an argument to a shared reference.
297-
///
298-
/// Useful in generic contexts where you need to extract a reference of an argument, independently of how it is passed.
299-
#[doc(hidden)] // for now, users are encouraged to use only call-site of impl AsArg; declaration-site may still develop.
300-
fn arg_to_ref<'r>(arg: &'r Self::Arg<'_>) -> &'r Self;
301-
302-
/// Clones an argument into an owned value.
303-
///
304-
/// Useful in generic contexts where you need to extract a value of an argument, independently of how it is passed.
305-
#[doc(hidden)] // for now, users are encouraged to use only call-site of impl AsArg; declaration-site may still develop.
306-
fn arg_into_owned(arg: Self::Arg<'_>) -> Self;
272+
type ArgPassing: ArgPassing;
273+
274+
#[deprecated(
275+
since = "0.3.2",
276+
note = "This method is no longer needed and will be removed in 0.4"
277+
)]
278+
fn owned_to_arg(self) -> impl AsArg<Self> {
279+
val_into_arg(self)
280+
}
307281
}
282+
283+
// ----------------------------------------------------------------------------------------------------------------------------------------------
284+
// Argument passing (mutually exclusive by-val or by-ref).
285+
286+
pub trait ArgPassing: Sealed {}
287+
288+
pub enum ByValue {}
289+
impl ArgPassing for ByValue {}
290+
impl Sealed for ByValue {}
291+
292+
pub enum ByRef {}
293+
impl ArgPassing for ByRef {}
294+
impl Sealed for ByRef {}

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use crate::meta::{FromGodot, GodotConvert, GodotFfiVariant, RefArg, ToGodot};
1111
use crate::sys;
1212
use godot_ffi::{ExtVariantType, GodotFfi, GodotNullableFfi, PtrcallType};
1313
use std::fmt;
14+
use std::ops::Deref;
1415

1516
/// Owned or borrowed value, used when passing arguments through `impl AsArg` to Godot APIs.
1617
#[doc(hidden)]
@@ -182,3 +183,14 @@ where
182183
self.cow_as_ref().is_null()
183184
}
184185
}
186+
187+
impl<T> Deref for CowArg<'_, T> {
188+
type Target = T;
189+
190+
fn deref(&self) -> &Self::Target {
191+
match self {
192+
CowArg::Owned(value) => value,
193+
CowArg::Borrowed(value) => value,
194+
}
195+
}
196+
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ mod ref_arg;
1313
// ----------------------------------------------------------------------------------------------------------------------------------------------
1414
// Public APIs
1515

16-
pub use as_arg::{AsArg, ParamType};
16+
pub use as_arg::{val_into_arg, ArgPassing, AsArg, ByRef, ByValue, ParamType};
1717
pub use object_arg::AsObjectArg;
1818
pub use ref_arg::RefArg;
1919

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,17 @@ pub trait ToGodot: Sized + GodotConvert {
6060
where
6161
Self: 'v;
6262

63+
/*
64+
// TODO(v0.4): add this type, to replace ParamType::ArgPassing and possibly Self::ToVia<'v>.
65+
/// Whether this type is passed by value or by reference in `AsArg` contexts.
66+
///
67+
/// Can be either [`ArgPassing::ByValue`] or [`ArgPassing::ByRef`]. For types implementing `Copy`, by-value is strongly recommended.
68+
///
69+
/// Will auto-implement `AsArg<T>` for either `T` (by-value) or for `&T` (by-reference). This has an influence on contexts such as
70+
/// [`Array::push()`](crate::builtin::Array::push) or generated signal `emit()` signatures.
71+
type ArgPassing: ArgPassing;
72+
*/
73+
6374
/// Converts this type to the Godot type by reference, usually by cloning.
6475
fn to_godot(&self) -> Self::ToVia<'_>;
6576

0 commit comments

Comments
 (0)