Description
In Rust-for-Linux, we wish to split our single kernel
crate into many smaller crates. There are various reasons for this:
- the ability to individually turn some crates off when they are not needed (for example in configurations that don't build drivers depending on that crate)
- splitting the code out of the
rust/
directory and into their respective subsystems. Then every subsystem should have their own crate(s). - refactoring core APIs into their own crate in order to publish them to crates.io
While I was splitting off the pin-init
crate from the kernel
crate in order to keep it in sync with the user-space version, I ran into a problem with the orphan rule. We also had previously anticipated that issues with the orphan rule would pop up if we were to split our kernel
crate. The issue that I am facing currently is not that bad, as I can solve it with an additional trait. But in the long run, it won't be feasible to always create a workaround for orphan rule conflicts.
Note that in the kernel
crate, we don't need to be protected by the orphan rule, as all 1 dependents (and dependencies except core
) of the kernel
crate are under our control directly in-tree. So we would like to have some way to relax the orphan rule for our kernel crates.
Here is the error that I am currently running into.
I have this trait in pin-init
(or the kernel
crate before splitting):
/// Marker trait for types that can be initialized by writing just zeroes.
///
/// # Safety
///
/// The bit pattern consisting of only zeroes is a valid bit pattern for this type. In other words,
/// this is not UB:
///
/// ```rust,ignore
/// let val: Self = unsafe { core::mem::zeroed() };
/// ```
pub unsafe trait Zeroable {}
And an impl in the kernel
crate for our own Box
type:
// SAFETY: All zeros is equivalent to `None` (option layout optimization guarantee).
//
// In this case we are allowed to use `T: ?Sized`, since all zeros is the `None` variant and there
// is no problem with a VTABLE pointer being null.
unsafe impl<T: ?Sized, A: Allocator> Zeroable for Option<Box<T, A>> {}
The error that I get is expected, as neither Zeroable
nor Option<T>
are declared by the kernel
crate:
error[E0117]: only traits defined in the current crate can be implemented for types defined outside of the crate
--> rust/kernel/alloc/kbox.rs:107:1
|
107 | unsafe impl<T: ?Sized, A: Allocator> Zeroable for Option<Box<T, A>> {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-----------------
| | |
| | `Option` is not defined in the current crate
| impl doesn't use only types from inside the current crate
|
= note: define and implement a trait or new type instead
error: aborting due to 1 previous error
In the latest RfL sync meeting, I brought this up and @ojeda had a good way of highlighting the problem: it's a chicken-and-egg situation, until we can relax the orphan rule, we're not able to split our crate due to these errors. And in order to know how the orphan rule should be relaxed, we have to split our crate. For this reason, it was suggested in the latest RfL sync meeting to use the following approach: introduce a nightly flag that disables the orphan rule wholesale (edit: for binary crates) and add a lint that warns on the orphan rule being violated. This way, we can split the kernel crate and see what types of orphan rule violations we would like to use without actually stabilizing anything. Miguel also added that you do not even need to ship it into nightly, you can just give us a modified compiler repository and we can test locally with that.
So essentially, this issue is for getting the ball rolling and having some nightly-only/custom-compiler-only flag to disable the orphan rule and see what types of violations we would like to have relaxed. Figuring out a stabilizable solution should come later, when we have an overview over all of the different kinds of relaxations we would like to have.
There also has been a zulip discussion about this topic.
cc @rust-lang/lang
Footnotes
-
while there are out-of-tree modules, they are always built for a pinned kernel version. When updating, they face the same issue on the C side, so it's OK to break them. ↩