Skip to content

Commit 6b99bc9

Browse files
authored
Merge pull request #500 from godot-rust/qol/useless-impl
Empty `#[godot_api] impl` blocks are no longer needed
2 parents c5ad021 + 560cdee commit 6b99bc9

File tree

15 files changed

+63
-132
lines changed

15 files changed

+63
-132
lines changed

godot-core/src/lib.rs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,6 @@ mod gen;
4545

4646
#[doc(hidden)]
4747
pub mod private {
48-
// If someone forgets #[godot_api], this causes a compile error, rather than virtual functions not being called at runtime.
49-
#[allow(non_camel_case_types)]
50-
pub trait You_forgot_the_attribute__godot_api {}
51-
pub use crate::property::Cannot_export_without_godot_api_impl;
52-
5348
use std::sync::{Arc, Mutex};
5449

5550
pub use crate::gen::classes::class_macros;
@@ -59,6 +54,10 @@ pub mod private {
5954

6055
use crate::{log, sys};
6156

57+
// If someone forgets #[godot_api], this causes a compile error, rather than virtual functions not being called at runtime.
58+
#[allow(non_camel_case_types)]
59+
pub trait You_forgot_the_attribute__godot_api {}
60+
6261
sys::plugin_registry!(pub __GODOT_PLUGIN_REGISTRY: ClassPlugin);
6362

6463
pub(crate) fn iterate_plugins(mut visitor: impl FnMut(&ClassPlugin)) {

godot-core/src/obj/traits.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ where
3838
/// During which initialization level this class is available/should be initialized with Godot.
3939
///
4040
/// Is `None` if the class has complicated initialization requirements, and generally cannot be inherited
41-
/// from.
41+
/// from (currently only for `()`, the "base" of `Object`).
4242
const INIT_LEVEL: Option<InitLevel>;
4343

4444
/// The name of the class, under which it is registered in Godot.

godot-core/src/property.rs

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -106,23 +106,6 @@ impl PropertyHintInfo {
106106
}
107107
}
108108

109-
/// To export properties to Godot, you must have an impl-block with the `#[godot_api]` attribute, even if
110-
/// it is empty.
111-
///
112-
/// This trait is automatically implemented when such an impl-block is present. If Rust complains that it is
113-
/// not implemented, then you can usually fix this by adding:
114-
///
115-
/// ```ignore
116-
/// #[godot_api]
117-
/// impl MyClass {}
118-
/// ```
119-
///
120-
/// Where you replace `MyClass` with the name of your class.
121-
#[allow(non_camel_case_types)]
122-
pub trait Cannot_export_without_godot_api_impl {
123-
const EXISTS: () = ();
124-
}
125-
126109
/// Functions used to translate user-provided arguments into export hints.
127110
pub mod export_info_functions {
128111
use crate::builtin::GString;

godot-core/src/registry.rs

Lines changed: 46 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ static LOADED_CLASSES: Mutex<Option<HashMap<InitLevel, Vec<ClassName>>>> = Mutex
3939
pub struct ClassPlugin {
4040
pub class_name: ClassName,
4141
pub component: PluginComponent,
42+
43+
// Init-level is per ClassPlugin and not per PluginComponent, because all components of all classes are mixed together in one
44+
// huge linker list. There is no per-class aggregation going on, so this allows to easily filter relevant classes.
4245
pub init_level: Option<InitLevel>,
4346
}
4447

@@ -60,11 +63,11 @@ impl fmt::Debug for ErasedRegisterFn {
6063
/// Represents the data part of a [`ClassPlugin`] instance.
6164
#[derive(Clone, Debug)]
6265
pub enum PluginComponent {
63-
/// Class definition itself, must always be available
66+
/// Class definition itself, must always be available.
6467
ClassDef {
6568
base_class_name: ClassName,
6669

67-
/// Godot low-level`create` function, wired up to library-generated `init`
70+
/// Godot low-level `create` function, wired up to library-generated `init`.
6871
generated_create_fn: Option<
6972
unsafe extern "C" fn(
7073
_class_userdata: *mut std::ffi::c_void, //
@@ -78,6 +81,9 @@ pub enum PluginComponent {
7881
) -> sys::GDExtensionClassInstancePtr,
7982
>,
8083

84+
/// Callback to library-generated function which registers properties in the `struct` definition.
85+
register_properties_fn: ErasedRegisterFn,
86+
8187
free_fn: unsafe extern "C" fn(
8288
_class_user_data: *mut std::ffi::c_void,
8389
instance: sys::GDExtensionClassInstancePtr,
@@ -86,18 +92,18 @@ pub enum PluginComponent {
8692

8793
/// Collected from `#[godot_api] impl MyClass`
8894
UserMethodBinds {
89-
/// Callback to library-generated function which registers functions in the `impl`
95+
/// Callback to library-generated function which registers functions and constants in the `impl` block.
9096
///
9197
/// Always present since that's the entire point of this `impl` block.
92-
generated_register_fn: ErasedRegisterFn,
98+
register_methods_constants_fn: ErasedRegisterFn,
9399
},
94100

95101
/// Collected from `#[godot_api] impl GodotExt for MyClass`
96102
UserVirtuals {
97-
/// Callback to user-defined `register_class` function
103+
/// Callback to user-defined `register_class` function.
98104
user_register_fn: Option<ErasedRegisterFn>,
99105

100-
/// Godot low-level`create` function, wired up to the user's `init`
106+
/// Godot low-level `create` function, wired up to the user's `init`.
101107
user_create_fn: Option<
102108
unsafe extern "C" fn(
103109
_class_userdata: *mut std::ffi::c_void, //
@@ -111,7 +117,7 @@ pub enum PluginComponent {
111117
) -> sys::GDExtensionClassInstancePtr,
112118
>,
113119

114-
/// User-defined `to_string` function
120+
/// User-defined `to_string` function.
115121
user_to_string_fn: Option<
116122
unsafe extern "C" fn(
117123
p_instance: sys::GDExtensionClassInstancePtr,
@@ -120,7 +126,7 @@ pub enum PluginComponent {
120126
),
121127
>,
122128

123-
/// User-defined `on_notification` function
129+
/// User-defined `on_notification` function.
124130
#[cfg(before_api = "4.2")]
125131
user_on_notification_fn: Option<
126132
unsafe extern "C" fn(
@@ -137,7 +143,7 @@ pub enum PluginComponent {
137143
),
138144
>,
139145

140-
/// Callback for other virtuals
146+
/// Callback for other virtuals.
141147
get_virtual_fn: unsafe extern "C" fn(
142148
p_userdata: *mut std::os::raw::c_void,
143149
p_name: sys::GDExtensionConstStringNamePtr,
@@ -154,8 +160,12 @@ pub enum PluginComponent {
154160
struct ClassRegistrationInfo {
155161
class_name: ClassName,
156162
parent_class_name: Option<ClassName>,
157-
generated_register_fn: Option<ErasedRegisterFn>,
163+
// Following functions are stored separately, since their order matters.
164+
register_methods_constants_fn: Option<ErasedRegisterFn>,
165+
register_properties_fn: Option<ErasedRegisterFn>,
158166
user_register_fn: Option<ErasedRegisterFn>,
167+
168+
/// Godot low-level class creation parameters.
159169
#[cfg(before_api = "4.2")]
160170
godot_params: sys::GDExtensionClassCreationInfo,
161171
#[cfg(since_api = "4.2")]
@@ -208,7 +218,8 @@ pub fn register_class<
208218
register_class_raw(ClassRegistrationInfo {
209219
class_name: T::class_name(),
210220
parent_class_name: Some(T::Base::class_name()),
211-
generated_register_fn: None,
221+
register_methods_constants_fn: None,
222+
register_properties_fn: None,
212223
user_register_fn: Some(ErasedRegisterFn {
213224
raw: callbacks::register_class_by_builder::<T>,
214225
}),
@@ -232,13 +243,15 @@ pub fn auto_register_classes(init_level: InitLevel) {
232243

233244
crate::private::iterate_plugins(|elem: &ClassPlugin| {
234245
//out!("* Plugin: {elem:#?}");
246+
247+
// Filter per ClassPlugin and not PluginComponent, because all components of all classes are mixed together in one huge list.
235248
match elem.init_level {
236249
None => {
237250
log::godot_error!("Unknown initialization level for class {}", elem.class_name);
238251
return;
239252
}
240253
Some(elem_init_level) if elem_init_level != init_level => return,
241-
_ => (),
254+
_ => { /* Nothing */ }
242255
}
243256

244257
let name = elem.class_name;
@@ -304,6 +317,7 @@ fn fill_class_info(component: PluginComponent, c: &mut ClassRegistrationInfo) {
304317
base_class_name,
305318
generated_create_fn,
306319
generated_recreate_fn,
320+
register_properties_fn,
307321
free_fn,
308322
} => {
309323
c.parent_class_name = Some(base_class_name);
@@ -335,12 +349,13 @@ fn fill_class_info(component: PluginComponent, c: &mut ClassRegistrationInfo) {
335349
assert!(generated_recreate_fn.is_none()); // not used
336350

337351
c.godot_params.free_instance_func = Some(free_fn);
352+
c.register_properties_fn = Some(register_properties_fn);
338353
}
339354

340355
PluginComponent::UserMethodBinds {
341-
generated_register_fn,
356+
register_methods_constants_fn,
342357
} => {
343-
c.generated_register_fn = Some(generated_register_fn);
358+
c.register_methods_constants_fn = Some(register_methods_constants_fn);
344359
}
345360

346361
PluginComponent::UserVirtuals {
@@ -433,11 +448,18 @@ fn register_class_raw(info: ClassRegistrationInfo) {
433448
//let mut class_builder = crate::builder::ClassBuilder::<?>::new();
434449
let mut class_builder = 0; // TODO dummy argument; see callbacks
435450

436-
// First call generated (proc-macro) registration function, then user-defined one.
437-
// This mimics the intuition that proc-macros are running "before" normal runtime code.
438-
if let Some(register_fn) = info.generated_register_fn {
451+
// Order of the following registrations is crucial:
452+
// 1. Methods and constants.
453+
// 2. Properties (they may depend on get/set methods).
454+
// 3. User-defined registration function (intuitively, user expects their own code to run after proc-macro generated code).
455+
if let Some(register_fn) = info.register_methods_constants_fn {
456+
(register_fn.raw)(&mut class_builder);
457+
}
458+
459+
if let Some(register_fn) = info.register_properties_fn {
439460
(register_fn.raw)(&mut class_builder);
440461
}
462+
441463
if let Some(register_fn) = info.user_register_fn {
442464
(register_fn.raw)(&mut class_builder);
443465
}
@@ -639,7 +661,11 @@ pub mod callbacks {
639661
T::__godot_register_class(&mut class_builder);
640662
}
641663

642-
pub fn register_user_binds<T: cap::ImplementsGodotApi + cap::ImplementsGodotExports>(
664+
pub fn register_user_properties<T: cap::ImplementsGodotExports>(_class_builder: &mut dyn Any) {
665+
T::__register_exports();
666+
}
667+
668+
pub fn register_user_methods_constants<T: cap::ImplementsGodotApi>(
643669
_class_builder: &mut dyn Any,
644670
) {
645671
// let class_builder = class_builder
@@ -649,7 +675,6 @@ pub mod callbacks {
649675
//T::register_methods(class_builder);
650676
T::__register_methods();
651677
T::__register_constants();
652-
T::__register_exports();
653678
}
654679
}
655680

@@ -662,7 +687,8 @@ fn default_registration_info(class_name: ClassName) -> ClassRegistrationInfo {
662687
ClassRegistrationInfo {
663688
class_name,
664689
parent_class_name: None,
665-
generated_register_fn: None,
690+
register_methods_constants_fn: None,
691+
register_properties_fn: None,
666692
user_register_fn: None,
667693
godot_params: default_creation_info(),
668694
init_level: InitLevel::Scene,

godot-macros/src/class/data_models/property.rs

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -203,18 +203,8 @@ pub fn make_property_impl(class_name: &Ident, fields: &Fields) -> TokenStream {
203203
});
204204
}
205205

206-
let enforce_godot_api_impl = if !export_tokens.is_empty() {
207-
quote! {
208-
const MUST_HAVE_GODOT_API_IMPL: () = <#class_name as ::godot::private::Cannot_export_without_godot_api_impl>::EXISTS;
209-
}
210-
} else {
211-
TokenStream::new()
212-
};
213-
214206
quote! {
215207
impl #class_name {
216-
#enforce_godot_api_impl
217-
218208
#(#getter_setter_impls)*
219209
}
220210

godot-macros/src/class/derive_godot_class.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,9 @@ pub fn derive_godot_class(decl: Declaration) -> ParseResult<TokenStream> {
103103
base_class_name: #base_class_name_obj,
104104
generated_create_fn: #create_fn,
105105
generated_recreate_fn: #recreate_fn,
106+
register_properties_fn: #prv::ErasedRegisterFn {
107+
raw: #prv::callbacks::register_user_properties::<#class_name>,
108+
},
106109
free_fn: #prv::callbacks::free::<#class_name>,
107110
},
108111
init_level: <#class_name as ::godot::obj::GodotClass>::INIT_LEVEL,

godot-macros/src/class/godot_api.rs

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -77,10 +77,10 @@ struct SignalDefinition {
7777
}
7878

7979
/// Codegen for `#[godot_api] impl MyType`
80-
fn transform_inherent_impl(mut decl: Impl) -> Result<TokenStream, Error> {
81-
let class_name = util::validate_impl(&decl, None, "godot_api")?;
80+
fn transform_inherent_impl(mut original_impl: Impl) -> Result<TokenStream, Error> {
81+
let class_name = util::validate_impl(&original_impl, None, "godot_api")?;
8282
let class_name_obj = util::class_name_obj(&class_name);
83-
let (funcs, signals) = process_godot_fns(&mut decl)?;
83+
let (funcs, signals) = process_godot_fns(&mut original_impl)?;
8484

8585
let mut signal_cfg_attrs: Vec<Vec<&Attribute>> = Vec::new();
8686
let mut signal_name_strs: Vec<String> = Vec::new();
@@ -135,7 +135,7 @@ fn transform_inherent_impl(mut decl: Impl) -> Result<TokenStream, Error> {
135135
.into_iter()
136136
.map(|func_def| make_method_registration(&class_name, func_def));
137137

138-
let consts = process_godot_constants(&mut decl)?;
138+
let consts = process_godot_constants(&mut original_impl)?;
139139
let mut integer_constant_cfg_attrs = Vec::new();
140140
let mut integer_constant_names = Vec::new();
141141
let mut integer_constant_values = Vec::new();
@@ -184,7 +184,7 @@ fn transform_inherent_impl(mut decl: Impl) -> Result<TokenStream, Error> {
184184
};
185185

186186
let result = quote! {
187-
#decl
187+
#original_impl
188188

189189
impl ::godot::obj::cap::ImplementsGodotApi for #class_name {
190190
fn __register_methods() {
@@ -222,13 +222,11 @@ fn transform_inherent_impl(mut decl: Impl) -> Result<TokenStream, Error> {
222222
}
223223
}
224224

225-
impl ::godot::private::Cannot_export_without_godot_api_impl for #class_name {}
226-
227225
::godot::sys::plugin_add!(__GODOT_PLUGIN_REGISTRY in #prv; #prv::ClassPlugin {
228226
class_name: #class_name_obj,
229227
component: #prv::PluginComponent::UserMethodBinds {
230-
generated_register_fn: #prv::ErasedRegisterFn {
231-
raw: #prv::callbacks::register_user_binds::<#class_name>,
228+
register_methods_constants_fn: #prv::ErasedRegisterFn {
229+
raw: #prv::callbacks::register_user_methods_constants::<#class_name>,
232230
},
233231
},
234232
init_level: <#class_name as ::godot::obj::GodotClass>::INIT_LEVEL,

0 commit comments

Comments
 (0)