Skip to content

Commit 3b7c674

Browse files
authored
Merge pull request #603 from lilizoey/refactor/minor-cleanups
Some cleanup and more accurate tracking of safety invariants
2 parents 154529e + de439bc commit 3b7c674

File tree

11 files changed

+256
-116
lines changed

11 files changed

+256
-116
lines changed

godot-codegen/src/context.rs

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -246,17 +246,6 @@ impl<'a> Context<'a> {
246246
self.singletons.contains(class_name)
247247
}
248248

249-
pub fn is_exportable(&self, class_name: &TyName) -> bool {
250-
if class_name.godot_ty == "Resource" || class_name.godot_ty == "Node" {
251-
return true;
252-
}
253-
254-
self.inheritance_tree
255-
.collect_all_bases(class_name)
256-
.iter()
257-
.any(|ty| ty.godot_ty == "Resource" || ty.godot_ty == "Node")
258-
}
259-
260249
pub fn inheritance_tree(&self) -> &InheritanceTree {
261250
&self.inheritance_tree
262251
}

godot-codegen/src/generator/builtins.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,21 @@ fn make_special_builtin_methods(class_name: &TyName, _ctx: &Context) -> TokenStr
163163
}
164164
}
165165

166+
/// Get the safety docs of an unsafe method, or `None` if it is safe.
167+
fn method_safety_doc(class_name: &TyName, method: &BuiltinMethod) -> Option<TokenStream> {
168+
if class_name.godot_ty == "Array"
169+
&& &method.return_value().type_tokens().to_string() == "VariantArray"
170+
{
171+
return Some(quote! {
172+
/// # Safety
173+
///
174+
/// You must ensure that the returned array fulfils the safety invariants of [`Array`](crate::builtin::Array).
175+
});
176+
}
177+
178+
None
179+
}
180+
166181
fn make_builtin_method_definition(
167182
builtin_class: &BuiltinClass,
168183
method: &BuiltinMethod,
@@ -220,12 +235,15 @@ fn make_builtin_method_definition(
220235
)*/
221236
};
222237

238+
let safety_doc = method_safety_doc(builtin_class.name(), method);
239+
223240
functions_common::make_function_definition(
224241
method,
225242
&FnCode {
226243
receiver,
227244
varcall_invocation,
228245
ptrcall_invocation,
229246
},
247+
safety_doc,
230248
)
231249
}

godot-codegen/src/generator/classes.rs

Lines changed: 22 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ fn make_class(class: &Class, ctx: &mut Context, view: &ApiView) -> GeneratedClas
8686
let base = ident(&conv::to_pascal_case(base));
8787
(quote! { crate::engine::#base }, Some(base))
8888
}
89-
None => (quote! { () }, None),
89+
None => (quote! { crate::obj::NoBase }, None),
9090
};
9191

9292
let (constructor, godot_default_impl) = make_constructor_and_default(class, ctx);
@@ -100,8 +100,7 @@ fn make_class(class: &Class, ctx: &mut Context, view: &ApiView) -> GeneratedClas
100100

101101
let enums = enums::make_enums(&class.enums);
102102
let constants = constants::make_constants(&class.constants);
103-
let inherits_macro = format_ident!("inherits_transitive_{}", class_name.rust_ty);
104-
let (exportable_impl, exportable_macro_impl) = make_exportable_impl(class_name, ctx);
103+
let inherits_macro = format_ident!("unsafe_inherits_transitive_{}", class_name.rust_ty);
105104
let deref_impl = make_deref_impl(class_name, &base_ty);
106105

107106
let all_bases = ctx.inheritance_tree().collect_all_bases(class_name);
@@ -140,8 +139,18 @@ fn make_class(class: &Class, ctx: &mut Context, view: &ApiView) -> GeneratedClas
140139
let instance_id = rtti.check_type::<Self>();
141140
Some(instance_id)
142141
}
142+
143+
#[doc(hidden)]
144+
pub fn __object_ptr(&self) -> sys::GDExtensionObjectPtr {
145+
self.object_ptr
146+
}
143147
};
144148

149+
let inherits_macro_safety_doc = format!(
150+
"The provided class must be a subclass of all the superclasses of [`{}`]",
151+
class_name.rust_ty
152+
);
153+
145154
// mod re_export needed, because class should not appear inside the file module, and we can't re-export private struct as pub.
146155
let imports = util::make_imports();
147156
let tokens = quote! {
@@ -187,31 +196,26 @@ fn make_class(class: &Class, ctx: &mut Context, view: &ApiView) -> GeneratedClas
187196
type DynMemory = crate::obj::bounds::#assoc_dyn_memory;
188197
type Declarer = crate::obj::bounds::DeclEngine;
189198
}
190-
impl crate::obj::EngineClass for #class_name {
191-
fn as_object_ptr(&self) -> sys::GDExtensionObjectPtr {
192-
self.object_ptr
193-
}
194-
fn as_type_ptr(&self) -> sys::GDExtensionTypePtr {
195-
std::ptr::addr_of!(self.object_ptr) as sys::GDExtensionTypePtr
196-
}
197-
}
199+
198200
#(
199-
impl crate::obj::Inherits<crate::engine::#all_bases> for #class_name {}
201+
// SAFETY: #all_bases is a list of classes provided by Godot such that #class_name is guaranteed a subclass of all of them.
202+
unsafe impl crate::obj::Inherits<crate::engine::#all_bases> for #class_name {}
200203
)*
201204

202-
#exportable_impl
203205
#godot_default_impl
204206
#deref_impl
205207

208+
/// # Safety
209+
///
210+
#[doc = #inherits_macro_safety_doc]
206211
#[macro_export]
207212
#[allow(non_snake_case)]
208213
macro_rules! #inherits_macro {
209214
($Class:ident) => {
210-
impl ::godot::obj::Inherits<::godot::engine::#class_name> for $Class {}
215+
unsafe impl ::godot::obj::Inherits<::godot::engine::#class_name> for $Class {}
211216
#(
212-
impl ::godot::obj::Inherits<::godot::engine::#all_bases> for $Class {}
217+
unsafe impl ::godot::obj::Inherits<::godot::engine::#all_bases> for $Class {}
213218
)*
214-
#exportable_macro_impl
215219
}
216220
}
217221
}
@@ -342,26 +346,8 @@ fn make_constructor_and_default(class: &Class, ctx: &Context) -> (TokenStream, T
342346
(constructor, godot_default_impl)
343347
}
344348

345-
fn make_exportable_impl(class_name: &TyName, ctx: &mut Context) -> (TokenStream, TokenStream) {
346-
let (exportable_impl, exportable_macro_impl);
347-
348-
if ctx.is_exportable(class_name) {
349-
exportable_impl = quote! {
350-
impl crate::obj::ExportableObject for #class_name {}
351-
};
352-
exportable_macro_impl = quote! {
353-
impl ::godot::obj::ExportableObject for $Class {}
354-
};
355-
} else {
356-
exportable_impl = TokenStream::new();
357-
exportable_macro_impl = TokenStream::new();
358-
};
359-
360-
(exportable_impl, exportable_macro_impl)
361-
}
362-
363349
fn make_deref_impl(class_name: &TyName, base_ty: &TokenStream) -> TokenStream {
364-
// The base_ty of `Object` is `()`, and we dont want every engine class to deref to `()`.
350+
// The base_ty of `Object` is `NoBase`, and we dont want every engine class to deref to `NoBase`.
365351
if class_name.rust_ty == "Object" {
366352
return TokenStream::new();
367353
}
@@ -484,5 +470,6 @@ fn make_class_method_definition(
484470
varcall_invocation,
485471
ptrcall_invocation,
486472
},
473+
None,
487474
)
488475
}

godot-codegen/src/generator/functions_common.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,11 @@ impl FnDefinitions {
8282
}
8383
}
8484

85-
pub fn make_function_definition(sig: &dyn Function, code: &FnCode) -> FnDefinition {
85+
pub fn make_function_definition(
86+
sig: &dyn Function,
87+
code: &FnCode,
88+
safety_doc: Option<TokenStream>,
89+
) -> FnDefinition {
8690
let has_default_params = default_parameters::function_uses_default_params(sig);
8791
let vis = if has_default_params {
8892
// Public API mapped by separate function.
@@ -92,7 +96,9 @@ pub fn make_function_definition(sig: &dyn Function, code: &FnCode) -> FnDefiniti
9296
make_vis(sig.is_private())
9397
};
9498

95-
let (maybe_unsafe, safety_doc) = if function_uses_pointers(sig) {
99+
let (maybe_unsafe, safety_doc) = if let Some(safety_doc) = safety_doc {
100+
(quote! { unsafe }, safety_doc)
101+
} else if function_uses_pointers(sig) {
96102
(
97103
quote! { unsafe },
98104
quote! {

godot-codegen/src/generator/utility_functions.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ pub(crate) fn make_utility_function_definition(function: &UtilityFunction) -> To
7878
varcall_invocation,
7979
ptrcall_invocation,
8080
},
81+
None,
8182
);
8283

8384
// Utility functions have no builders.

godot-codegen/src/generator/virtual_traits.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ fn make_virtual_method(method: &ClassMethod) -> Option<TokenStream> {
124124
varcall_invocation: TokenStream::new(),
125125
ptrcall_invocation: TokenStream::new(),
126126
},
127+
None,
127128
);
128129

129130
// Virtual methods have no builders.

0 commit comments

Comments
 (0)