Skip to content

Commit 6010819

Browse files
BromeonTitanNano
authored andcommitted
Repurpose ParamType for by-value/by-ref distinction
1 parent 1075c12 commit 6010819

File tree

8 files changed

+76
-76
lines changed

8 files changed

+76
-76
lines changed

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

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,9 @@ use std::{cmp, fmt};
1111
use crate::builtin::*;
1212
use crate::meta;
1313
use crate::meta::error::{ConvertError, FromGodotError, FromVariantError};
14-
#[expect(deprecated)]
1514
use crate::meta::{
16-
element_godot_type_name, element_variant_type, ArrayElement, ArrayTypeInfo, AsArg, ClassName,
17-
CowArg, FromGodot, GodotConvert, GodotFfiVariant, GodotType, ParamType, PropertyHintInfo,
15+
element_godot_type_name, element_variant_type, ArrayElement, ArrayTypeInfo, AsArg, ByRef,
16+
ClassName, FromGodot, GodotConvert, GodotFfiVariant, GodotType, ParamType, PropertyHintInfo,
1817
RefArg, ToGodot,
1918
};
2019
use crate::obj::{bounds, Bounds, DynGd, Gd, GodotClass};
@@ -676,7 +675,7 @@ impl<T: ArrayElement> Array<T> {
676675
// We need one dummy element of type T, because Godot's bsearch_custom() checks types (so Variant::nil() can't be passed).
677676
// Optimization: roundtrip Variant -> T -> Variant could be avoided, but anyone needing speed would use Rust binary search...
678677
let ignored_value = self.at(0);
679-
let ignored_value = AsArg::into_arg(&ignored_value);
678+
let ignored_value = meta::val_into_arg(ignored_value); //AsArg::into_arg(&ignored_value);
680679

681680
let godot_comparator = |args: &[&Variant]| {
682681
let value = T::from_variant(args[0]);
@@ -1119,13 +1118,8 @@ unsafe impl<T: ArrayElement> GodotFfi for Array<T> {
11191118
// Only implement for untyped arrays; typed arrays cannot be nested in Godot.
11201119
impl ArrayElement for VariantArray {}
11211120

1122-
#[expect(deprecated)]
11231121
impl<T: ArrayElement> ParamType for Array<T> {
1124-
type Arg<'v> = CowArg<'v, Self>;
1125-
1126-
fn owned_to_arg<'v>(self) -> Self::Arg<'v> {
1127-
CowArg::Owned(self)
1128-
}
1122+
type ArgPassing = ByRef;
11291123
}
11301124

11311125
impl<T: ArrayElement> GodotConvert for Array<T> {
@@ -1429,7 +1423,8 @@ impl<T: ArrayElement> Extend<T> for Array<T> {
14291423
// A faster implementation using `resize()` and direct pointer writes might still be possible.
14301424
// Note that this could technically also use iter(), since no moves need to happen (however Extend requires IntoIterator).
14311425
for item in iter.into_iter() {
1432-
self.push(AsArg::into_arg(&item));
1426+
// self.push(AsArg::into_arg(&item));
1427+
self.push(meta::val_into_arg(item));
14331428
}
14341429
}
14351430
}

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

Lines changed: 46 additions & 36 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, ToGodot};
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.
@@ -64,7 +65,19 @@ where
6465
Self: 'r;
6566
}
6667

67-
impl<T: ToGodot> AsArg<T> for &T {
68+
/// Generic abstraction over `T` and `&T` that should be passed as `AsArg<T>`.
69+
#[doc(hidden)]
70+
pub fn val_into_arg<'r, T>(arg: T) -> impl AsArg<T> + 'r
71+
where
72+
T: ToGodot + 'r,
73+
{
74+
CowArg::Owned(arg)
75+
}
76+
77+
impl<T> AsArg<T> for &T
78+
where
79+
T: ToGodot + ParamType<ArgPassing = ByRef>,
80+
{
6881
fn into_arg<'r>(self) -> CowArg<'r, T>
6982
where
7083
Self: 'r,
@@ -73,7 +86,10 @@ impl<T: ToGodot> AsArg<T> for &T {
7386
}
7487
}
7588

76-
impl<T: ToGodot + Copy> AsArg<T> for T {
89+
impl<T> AsArg<T> for T
90+
where
91+
T: ToGodot + ParamType<ArgPassing = ByValue>,
92+
{
7793
fn into_arg<'r>(self) -> CowArg<'r, T>
7894
where
7995
Self: 'r,
@@ -128,27 +144,17 @@ macro_rules! arg_into_owned {
128144
#[macro_export]
129145
macro_rules! impl_asarg_by_value {
130146
($T:ty) => {
131-
#[expect(deprecated)]
132147
impl $crate::meta::ParamType for $T {
133-
type Arg<'v> = $T;
134-
135-
fn owned_to_arg<'v>(self) -> Self::Arg<'v> {
136-
self
137-
}
148+
type ArgPassing = $crate::meta::ByValue;
138149
}
139150
};
140151
}
141152

142153
#[macro_export]
143154
macro_rules! impl_asarg_by_ref {
144155
($T:ty) => {
145-
#[expect(deprecated)]
146156
impl $crate::meta::ParamType for $T {
147-
type Arg<'v> = $crate::meta::CowArg<'v, $T>;
148-
149-
fn owned_to_arg<'v>(self) -> Self::Arg<'v> {
150-
$crate::meta::CowArg::Owned(self)
151-
}
157+
type ArgPassing = $crate::meta::ByRef;
152158
}
153159
};
154160
}
@@ -255,27 +261,31 @@ impl AsArg<NodePath> for &String {
255261
/// Implemented for all parameter types `T` that are allowed to receive [impl `AsArg<T>`][AsArg].
256262
// ParamType used to be a subtrait of GodotType, but this can be too restrictive. For example, DynGd is not a "Godot canonical type"
257263
// (GodotType), however it's still useful to store it in arrays -- which requires AsArg and subsequently ParamType.
258-
#[deprecated(
259-
since = "0.3.2",
260-
note = "This trait is no longer needed and will be removed in 0.4"
261-
)]
262-
pub trait ParamType: sealed::Sealed + Sized + 'static + ToGodot
264+
//
265+
// TODO(v0.4): merge ParamType::ArgPassing into ToGodot::ToVia, reducing redundancy on user side.
266+
pub trait ParamType: ToGodot + Sized + 'static
263267
// GodotType bound not required right now, but conceptually should always be the case.
264268
{
265-
/// Canonical argument passing type, either `T` or an internally-used CoW type.
266-
///
267-
/// The general rule is that `Copy` types are passed by value, while the rest is passed by reference.
268-
///
269-
/// This associated type is closely related to [`ToGodot::ToVia<'v>`][crate::meta::ToGodot::ToVia] and may be reorganized in the future.
270-
#[doc(hidden)]
271-
type Arg<'v>: AsArg<Self>
272-
where
273-
Self: 'v;
274-
275-
/// Converts an owned value to the canonical argument type, which can be passed to [`impl AsArg<T>`][AsArg].
276-
///
277-
/// Useful in generic contexts where only a value is available, and one doesn't want to dispatch between value/reference.
278-
///
279-
/// You should not rely on the exact return type, as it may change in future versions; treat it like `impl AsArg<Self>`.
280-
fn owned_to_arg<'v>(self) -> Self::Arg<'v>;
269+
type ArgPassing: ArgPassing;
270+
271+
#[deprecated(
272+
since = "0.3.2",
273+
note = "This method is no longer needed and will be removed in 0.4"
274+
)]
275+
fn owned_to_arg(self) -> impl AsArg<Self> {
276+
val_into_arg(self)
277+
}
281278
}
279+
280+
// ----------------------------------------------------------------------------------------------------------------------------------------------
281+
// Argument passing (mutually exclusive by-val or by-ref).
282+
283+
pub trait ArgPassing: Sealed {}
284+
285+
pub enum ByValue {}
286+
impl ArgPassing for ByValue {}
287+
impl Sealed for ByValue {}
288+
289+
pub enum ByRef {}
290+
impl ArgPassing for ByRef {}
291+
impl Sealed for ByRef {}

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

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

16-
#[expect(deprecated)]
17-
pub use as_arg::{AsArg, ParamType};
16+
pub use as_arg::{val_into_arg, ArgPassing, AsArg, ByRef, ByValue, ParamType};
1817
pub use object_arg::AsObjectArg;
1918
pub use ref_arg::RefArg;
2019

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

godot-core/src/meta/traits.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
use crate::builtin::{Variant, VariantType};
99
use crate::global::PropertyUsageFlags;
1010
use crate::meta::error::ConvertError;
11-
#[expect(deprecated)]
1211
use crate::meta::{
1312
sealed, ClassName, FromGodot, GodotConvert, ParamType, PropertyHintInfo, PropertyInfo, ToGodot,
1413
};
@@ -170,7 +169,6 @@ pub trait GodotType: GodotConvert<Via = Self> + sealed::Sealed + Sized + 'static
170169
message = "`Array<T>` can only store element types supported in Godot arrays (no nesting).",
171170
label = "has invalid element type"
172171
)]
173-
#[expect(deprecated)]
174172
pub trait ArrayElement: ToGodot + FromGodot + sealed::Sealed + ParamType + 'static {
175173
// Note: several indirections in ArrayElement and the global `element_*` functions go through `GodotConvert::Via`,
176174
// to not require Self: GodotType. What matters is how array elements map to Godot on the FFI level (GodotType trait).

godot-core/src/obj/dyn_gd.rs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -547,17 +547,12 @@ where
547547
}
548548
*/
549549

550-
#[expect(deprecated)]
551550
impl<T, D> meta::ParamType for DynGd<T, D>
552551
where
553552
T: GodotClass,
554553
D: ?Sized + 'static,
555554
{
556-
type Arg<'v> = meta::CowArg<'v, DynGd<T, D>>;
557-
558-
fn owned_to_arg<'v>(self) -> Self::Arg<'v> {
559-
meta::CowArg::Owned(self)
560-
}
555+
type ArgPassing = meta::ByRef;
561556
}
562557

563558
impl<T, D> meta::ArrayElement for DynGd<T, D>

godot-core/src/obj/gd.rs

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,8 @@ use sys::{static_assert_eq_size_align, SysPtr as _};
1313

1414
use crate::builtin::{Callable, NodePath, StringName, Variant};
1515
use crate::meta::error::{ConvertError, FromFfiError};
16-
17-
#[expect(deprecated)]
1816
use crate::meta::{
19-
ArrayElement, AsArg, CallContext, ClassName, CowArg, FromGodot, GodotConvert, GodotType,
17+
ArrayElement, AsArg, ByRef, CallContext, ClassName, CowArg, FromGodot, GodotConvert, GodotType,
2018
ParamType, PropertyHintInfo, RefArg, ToGodot,
2119
};
2220
use crate::obj::{
@@ -868,13 +866,8 @@ where
868866
}
869867
*/
870868

871-
#[expect(deprecated)]
872869
impl<T: GodotClass> ParamType for Gd<T> {
873-
type Arg<'v> = CowArg<'v, Gd<T>>;
874-
875-
fn owned_to_arg<'v>(self) -> Self::Arg<'v> {
876-
CowArg::Owned(self)
877-
}
870+
type ArgPassing = ByRef;
878871
}
879872

880873
impl<T: GodotClass> AsArg<Option<Gd<T>>> for Option<&Gd<T>> {
@@ -887,13 +880,8 @@ impl<T: GodotClass> AsArg<Option<Gd<T>>> for Option<&Gd<T>> {
887880
}
888881
}
889882

890-
#[expect(deprecated)]
891883
impl<T: GodotClass> ParamType for Option<Gd<T>> {
892-
type Arg<'v> = CowArg<'v, Option<Gd<T>>>;
893-
894-
fn owned_to_arg<'v>(self) -> Self::Arg<'v> {
895-
CowArg::Owned(self)
896-
}
884+
type ArgPassing = ByRef;
897885
}
898886

899887
impl<T> Default for Gd<T>

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

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,12 @@ use crate::framework::itest;
99
use godot::builtin::{vslice, GString, Signal, StringName};
1010
use godot::classes::object::ConnectFlags;
1111
use godot::classes::{Node, Node3D, Object, RefCounted};
12-
use godot::meta::{FromGodot, GodotConvert, ToGodot};
12+
use godot::meta::{FromGodot, GodotConvert, ParamType, ToGodot};
1313
use godot::obj::{Base, Gd, InstanceId, NewAlloc, NewGd};
1414
use godot::prelude::ConvertError;
1515
use godot::register::{godot_api, GodotClass};
16-
use godot::sys;
1716
use godot::sys::Global;
17+
use godot::{meta, sys};
1818
use std::cell::{Cell, RefCell};
1919
use std::rc::Rc;
2020

@@ -495,8 +495,12 @@ fn enums_as_signal_args() {
495495
}
496496
}
497497

498+
impl ParamType for EventType {
499+
type ArgPassing = meta::ByValue;
500+
}
501+
498502
impl FromGodot for EventType {
499-
fn try_from_godot(via: Self::Via) -> Result<Self, godot::prelude::ConvertError> {
503+
fn try_from_godot(via: Self::Via) -> Result<Self, ConvertError> {
500504
match via {
501505
0 => Ok(Self::Ready),
502506
_ => Err(ConvertError::new("value out of range")),
@@ -519,7 +523,7 @@ fn enums_as_signal_args() {
519523
let object = SignalObject::new_gd();
520524
let event = EventType::Ready;
521525

522-
object.signals().game_event().emit(&event);
526+
object.signals().game_event().emit(event);
523527
}
524528

525529
// ----------------------------------------------------------------------------------------------------------------------------------------------

0 commit comments

Comments
 (0)