Skip to content

Document runtime class in editor requirement #1168

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

Open
wants to merge 4 commits into
base: master
Choose a base branch
from

Conversation

Yarwin
Copy link
Contributor

@Yarwin Yarwin commented May 19, 2025

Closes: #755 (comment)

Document requirement of #[class(tool)], the error will inform user about given requirement (identical to https://github.com/godotengine/godot/pull/94511/files)

@Yarwin Yarwin added documentation Improvements or additions to documentation c: core Core components labels May 19, 2025
@GodotRust
Copy link

API docs are being generated and will be shortly available at: https://godot-rust.github.io/docs/gdext/pr-1168

Copy link
Member

@Bromeon Bromeon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot! 👍

if crate::classes::Engine::singleton().is_editor_hint() {
panic!(
"Class {} -- null instance; does the class have a Godot creator function? \
Ensure that given Class is runtime class (#[class_tool]) if it is being accessed in the editor.",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Ensure that given Class is runtime class (#[class_tool]) if it is being accessed in the editor.",
Ensure that the given class is a tool class with #[class(tool)], if it is being accessed in the editor.",

// Non-runtime classes can't be instantiated in the editor.
if crate::classes::Engine::singleton().is_editor_hint() {
panic!(
"Class {} -- null instance; does the class have a Godot creator function? \
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Newline would be nice:

Suggested change
"Class {} -- null instance; does the class have a Godot creator function? \
"Class {} -- null instance; does the class have a Godot creator function?\n\

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought so, but it weirdly splits the warning into two which is a bit too confusing:

image

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, but we already use multiple lines in many other error messages. Then we should rather fix how it's represented in Godot error printing (outside this PR), no?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That definitively makes sense

Comment on lines 441 to 442
let binding = interface_fn!(object_get_instance_binding)(self.obj_sys(), token, &callbacks)
as sys::GDExtensionClassInstancePtr;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please avoid as casts, they're the crowbar of the type system.
(I should probably look for clippy lints at some point...)

Use <*mut T>::cast() instead, maybe in a separate variable.

return;
}

// Non-runtime classes can't be instantiated in the editor.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean "non-tool classes", no?

- Add safety note.
- Make `resolve_instance_ptr` safe. Returning (and casting) null ptrs is totally safe.
Comment on lines 451 to 456
// SAFETY: library is already initialized.
let token = unsafe { sys::get_library() };
// SAFETY: ensured that `self.obj` is non-null and valid.
let binding = unsafe {
interface_fn!(object_get_instance_binding)(self.obj_sys(), token.cast(), &callbacks)
};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please empty lines before "groups" of comment+code:

Suggested change
// SAFETY: library is already initialized.
let token = unsafe { sys::get_library() };
// SAFETY: ensured that `self.obj` is non-null and valid.
let binding = unsafe {
interface_fn!(object_get_instance_binding)(self.obj_sys(), token.cast(), &callbacks)
};
// SAFETY: library is already initialized.
let token = unsafe { sys::get_library() };
// SAFETY: ensured that `self.obj` is non-null and valid.
let binding = unsafe {
interface_fn!(object_get_instance_binding)(self.obj_sys(), token.cast(), &callbacks)
};

But more importantly, type inference for dangerous pointer conversions like .cast() is really an antipattern in my opinion. It also bears the risk that if the signature changes, then our conversion semantics are silently updated, which may be unintended. Not a big deal here because GDExtension APIs are stable, but still... Naming the type ensures that our mental model is correct.

Could you declare token with the explicit converted-to type, e.g.:

        // SAFETY: library is already initialized.
        let token = unsafe { sys::get_library() };
        let token = token.cast::<???>();

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we are going to establish a convention, I would go with

let variable: T = ptr.cast();

due to type aliases (as far as I'm aware .cast::<*#$%>!@#(&!!()-| (choose the right one or none) TypeAlias>() is not an option 🤔? I've never seen it)

example:

(...)
pub type GDExtensionClassInstancePtr = *mut __GdextClassInstance;

// Silly – `sys::__GdextClassInstance` is not mentioned anywhere else, and we can't cast directly to `sys::GDExtensionClassInstancePtr`.
let ptr: sys::GDExtensionClassInstancePtr = binding.cast::<sys::__GdextClassInstance>();

Copy link
Member

@Bromeon Bromeon May 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense here! But there are also cases where you have the pointee type and would need to write an extra *mut or *const around it, if used on the left-hand-side.

We should probably just use whichever is easier, main thing is that a type is involved somewhere 🙂

Yarwin added 2 commits May 19, 2025 16:51
- Explicitly declare type while `cast()`ing, so type inference won't try anything funny
- Code tweaks – improve readability when specifying a type we cast our pointer to
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
c: core Core components documentation Improvements or additions to documentation
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Gd<XXX>.bind() cause crash, with api-custom feature in custom build godot.
3 participants