Skip to content

Commit d1874f3

Browse files
authored
Merge pull request #1174 from godot-rust/qol/rename-unsafe-virtuals
Add `_rawptr` suffix to all unsafe virtual functions
2 parents 98dcdda + 4c192e8 commit d1874f3

File tree

9 files changed

+274
-233
lines changed

9 files changed

+274
-233
lines changed

godot-codegen/src/conv/name_conversions.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,8 @@
77

88
//! Identifier renamings (Godot -> Rust)
99
10-
use proc_macro2::Ident;
11-
1210
use crate::util::ident;
11+
use proc_macro2::Ident;
1312

1413
// ----------------------------------------------------------------------------------------------------------------------------------------------
1514
// Case conversions
@@ -104,6 +103,13 @@ pub fn shout_to_pascal(shout_case: &str) -> String {
104103
result
105104
}
106105

106+
// ----------------------------------------------------------------------------------------------------------------------------------------------
107+
// Virtual functions
108+
109+
pub fn make_unsafe_virtual_fn_name(rust_fn_name: &str) -> String {
110+
format!("{rust_fn_name}_rawptr")
111+
}
112+
107113
// ----------------------------------------------------------------------------------------------------------------------------------------------
108114
// Enum conversions
109115

godot-codegen/src/generator/functions_common.rs

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ pub fn make_function_definition(
122122
// Thus, let's keep things simple and more conservative.
123123
let (maybe_unsafe, maybe_safety_doc) = if let Some(safety_doc) = safety_doc {
124124
(quote! { unsafe }, safety_doc)
125-
} else if function_uses_pointers(sig) {
125+
} else if sig.common().is_unsafe {
126126
(
127127
quote! { unsafe },
128128
quote! {
@@ -614,7 +614,7 @@ pub(crate) fn make_params_exprs_virtual<'a>(
614614
RustTy::EngineClass { .. }
615615
if !special_cases::is_class_method_param_required(
616616
function_sig.surrounding_class().unwrap(),
617-
function_sig.name(),
617+
function_sig.godot_name(),
618618
param_name,
619619
) =>
620620
{
@@ -637,15 +637,3 @@ pub(crate) fn make_params_exprs_virtual<'a>(
637637

638638
ret
639639
}
640-
641-
fn function_uses_pointers(sig: &dyn Function) -> bool {
642-
let has_pointer_params = sig
643-
.params()
644-
.iter()
645-
.any(|param| matches!(param.type_, RustTy::RawPointer { .. }));
646-
647-
let has_pointer_return = matches!(sig.return_value().type_, Some(RustTy::RawPointer { .. }));
648-
649-
// No short-circuiting due to variable decls, but that's fine.
650-
has_pointer_params || has_pointer_return
651-
}

godot-codegen/src/generator/virtual_traits.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -212,8 +212,10 @@ fn make_all_virtual_methods(
212212
for method in base_class.methods.iter() {
213213
// Certain derived classes in Godot implement a virtual method declared in a base class, thus no longer
214214
// making it required. This isn't advertised in the extension_api, but instead manually tracked via special cases.
215-
let derived_presence =
216-
special_cases::get_derived_virtual_method_presence(class.name(), method.name());
215+
let derived_presence = special_cases::get_derived_virtual_method_presence(
216+
class.name(),
217+
method.godot_name(),
218+
);
217219

218220
// Collect all changes in a Markdown table.
219221
let new = match derived_presence {

godot-codegen/src/models/domain.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,8 @@ pub struct FunctionCommon {
284284
pub is_vararg: bool,
285285
pub is_private: bool,
286286
pub is_virtual_required: bool,
287+
/// Whether raw pointers appear in signature. Affects safety, and in case of virtual methods, the name.
288+
pub is_unsafe: bool,
287289
pub direction: FnDirection,
288290
}
289291

godot-codegen/src/models/domain_mapping.rs

Lines changed: 42 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use crate::models::domain::{
1010
BuildConfiguration, BuiltinClass, BuiltinMethod, BuiltinSize, BuiltinVariant, Class,
1111
ClassCommons, ClassConstant, ClassConstantValue, ClassMethod, ClassSignal, Constructor, Enum,
1212
Enumerator, EnumeratorValue, ExtensionApi, FnDirection, FnParam, FnQualifier, FnReturn,
13-
FunctionCommon, GodotApiVersion, ModName, NativeStructure, Operator, Singleton, TyName,
13+
FunctionCommon, GodotApiVersion, ModName, NativeStructure, Operator, RustTy, Singleton, TyName,
1414
UtilityFunction,
1515
};
1616
use crate::models::json::{
@@ -374,6 +374,7 @@ impl BuiltinMethod {
374374
is_vararg: method.is_vararg,
375375
is_private: false, // See 'exposed' below. Could be special_cases::is_method_private(builtin_name, &method.name),
376376
is_virtual_required: false,
377+
is_unsafe: false, // Builtin methods don't use raw pointers.
377378
direction: FnDirection::Outbound {
378379
hash: method.hash.expect("hash absent for builtin method"),
379380
},
@@ -456,6 +457,7 @@ impl ClassMethod {
456457
},
457458
};
458459

460+
// May still be renamed further, for unsafe methods. Not done here because data to determine safety is not available yet.
459461
let rust_method_name = Self::make_virtual_method_name(class_name, &method.name);
460462

461463
Self::from_json_inner(method, rust_method_name, class_name, direction, ctx)
@@ -488,7 +490,7 @@ impl ClassMethod {
488490
// Since Godot 4.4, GDExtension advertises whether virtual methods have a default implementation or are required to be overridden.
489491
#[cfg(before_api = "4.4")]
490492
let is_virtual_required =
491-
special_cases::is_virtual_method_required(&class_name, rust_method_name);
493+
special_cases::is_virtual_method_required(&class_name, &method.name);
492494

493495
#[cfg(since_api = "4.4")]
494496
#[allow(clippy::let_and_return)]
@@ -507,15 +509,29 @@ impl ClassMethod {
507509
is_required_in_json
508510
};
509511

512+
let parameters = FnParam::new_range(&method.arguments, ctx);
513+
let return_value = FnReturn::new(&method.return_value, ctx);
514+
let is_unsafe = Self::function_uses_pointers(&parameters, &return_value);
515+
516+
// Future note: if further changes are made to the virtual method name, make sure to make it reversible so that #[godot_api]
517+
// can match on the Godot name of the virtual method.
518+
let rust_method_name = if is_unsafe && method.is_virtual {
519+
// If the method is unsafe, we need to rename it to avoid conflicts with the safe version.
520+
conv::make_unsafe_virtual_fn_name(rust_method_name)
521+
} else {
522+
rust_method_name.to_string()
523+
};
524+
510525
Some(Self {
511526
common: FunctionCommon {
512-
name: rust_method_name.to_string(),
527+
name: rust_method_name,
513528
godot_name: godot_method_name,
514-
parameters: FnParam::new_range(&method.arguments, ctx),
515-
return_value: FnReturn::new(&method.return_value, ctx),
529+
parameters,
530+
return_value,
516531
is_vararg: method.is_vararg,
517532
is_private,
518533
is_virtual_required,
534+
is_unsafe,
519535
direction,
520536
},
521537
qualifier,
@@ -524,12 +540,28 @@ impl ClassMethod {
524540
}
525541

526542
fn make_virtual_method_name<'m>(class_name: &TyName, godot_method_name: &'m str) -> &'m str {
527-
// Remove leading underscore from virtual method names.
528-
let method_name = godot_method_name
543+
// Hardcoded overrides.
544+
if let Some(rust_name) =
545+
special_cases::maybe_rename_virtual_method(class_name, godot_method_name)
546+
{
547+
return rust_name;
548+
}
549+
550+
// In general, just rlemove leading underscore from virtual method names.
551+
godot_method_name
529552
.strip_prefix('_')
530-
.unwrap_or(godot_method_name);
553+
.unwrap_or(godot_method_name)
554+
}
555+
556+
fn function_uses_pointers(parameters: &[FnParam], return_value: &FnReturn) -> bool {
557+
let has_pointer_params = parameters
558+
.iter()
559+
.any(|param| matches!(param.type_, RustTy::RawPointer { .. }));
560+
561+
let has_pointer_return = matches!(return_value.type_, Some(RustTy::RawPointer { .. }));
531562

532-
special_cases::maybe_rename_virtual_method(class_name, method_name)
563+
// No short-circuiting due to variable decls, but that's fine.
564+
has_pointer_params || has_pointer_return
533565
}
534566
}
535567

@@ -584,6 +616,7 @@ impl UtilityFunction {
584616
is_vararg: function.is_vararg,
585617
is_private,
586618
is_virtual_required: false,
619+
is_unsafe: false, // Utility functions don't use raw pointers.
587620
direction: FnDirection::Outbound {
588621
hash: function.hash,
589622
},

0 commit comments

Comments
 (0)