-
-
Notifications
You must be signed in to change notification settings - Fork 182
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
GodotClass
should be an unsafe trait
#345
Comments
This trait is not intended to be implemented manually. You need several internal symbols, which means it's not a supported workflow. The documentation also mentions it; could be worded even more strictly:
If we ever want to support manual implementation, this needs to be fleshed out alongside #4. As to the unsafeness, maybe. But it's an implementation detail that will likely confuse users when they see it. |
Whether we support manually implementing it or not is kinda irrelevant. The trait can be implemented manually, and cause UB even while upholding all other safety invariants (perhaps not the blanket In fact "you should not implement this trait manually" when implementing the trait manually can cause UB is definitely a good reason to make it an unsafe trait. Because that is a safety invariant for implementing the trait.
I've definitely seen traits before that are marked unsafe, but are safe to implement using derives. |
Here is an example: // imports...
struct Testing;
#[gdextension]
unsafe impl ExtensionLibrary for Testing {}
#[derive(Debug, Copy, Clone)]
pub struct Foo {
a: u64,
b: u64,
c: u64,
}
impl ::godot::obj::GodotClass for Foo {
type Base = ::godot::engine::RefCounted;
type Declarer = ::godot::obj::dom::EngineDomain;
type Mem = <Self::Base as ::godot::obj::GodotClass>::Mem;
const CLASS_NAME: &'static str = "Foo";
}
// The rest that `#[derive(GodotClass)]` would generate with generated `init`. (contains no unsafe)
#[godot_api]
impl Foo {
#[func]
fn do_ub() {
let object_ptr = unsafe { callbacks::create::<Foo>(std::ptr::null_mut()) };
let foo: Gd<Foo> = unsafe { Gd::from_obj_sys(object_ptr) };
let bar: Foo = *foo;
godot_print!("{bar:?}");
}
} This code triggers UB, but which unsafe block here is it we violate safety invariants in?
Or maybe it is that we shouldn't have implemented
But why does whether we choose to implement In the end it seems most natural to me that we make it a safety invariant of Full Codeuse godot::{
prelude::{meta::ClassName, *},
private::callbacks,
sys,
};
struct Testing;
#[gdextension]
unsafe impl ExtensionLibrary for Testing {}
#[derive(Debug, Copy, Clone)]
pub struct Foo {
a: u64,
b: u64,
c: u64,
}
impl ::godot::obj::GodotClass for Foo {
type Base = ::godot::engine::RefCounted;
type Declarer = ::godot::obj::dom::EngineDomain;
type Mem = <Self::Base as ::godot::obj::GodotClass>::Mem;
const CLASS_NAME: &'static str = "Foo";
}
impl ::godot::obj::cap::GodotInit for Foo {
fn __godot_init(base: ::godot::obj::Base<Self::Base>) -> Self {
Self {
a: ::std::default::Default::default(),
b: ::std::default::Default::default(),
c: ::std::default::Default::default(),
}
}
}
impl Foo {}
impl ::godot::obj::cap::ImplementsGodotExports for Foo {
fn __register_exports() {}
}
::godot::sys::plugin_add!(__GODOT_PLUGIN_REGISTRY in::godot::private;
::godot::private::ClassPlugin {
class_name:"Foo",component: ::godot::private::PluginComponent::ClassDef {
base_class_name: < ::godot::engine::RefCounted as ::godot::obj::GodotClass> ::CLASS_NAME,generated_create_fn:Some(::godot::private::callbacks::create:: <Foo>),free_fn: ::godot::private::callbacks::free:: <Foo> ,
},
});
::godot::private::class_macros::inherits_transitive_RefCounted!(Foo);
#[godot_api]
impl Foo {
#[func]
fn do_ub() {
let object_ptr = unsafe { callbacks::create::<Foo>(std::ptr::null_mut()) };
let foo: Gd<Foo> = unsafe { Gd::from_obj_sys(object_ptr) };
let bar: Foo = *foo;
godot_print!("{bar:?}");
}
} |
What can be done doesn't matter if the documentation advises against it -- I reworded this to make it clearer. Thanks for the elaborated example! I think the To be clear though, this does not legitimate a) manual implementation of |
If you implement
GodotClass
forT
withDeclarer = EngineDomain
, then you can useDeref
to transmute a&OpaqueObject
to a&T
. It is the responsibility of the implementer ofGodotClass
to ensure that this is a safe transmute.See:
The text was updated successfully, but these errors were encountered: