Skip to content

experiment with relaxing the orphan rule #136979

Open
@y86-dev

Description

@y86-dev

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

  1. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-coherenceArea: CoherenceA-rust-for-linuxRelevant for the Rust-for-Linux projectC-discussionCategory: Discussion or questions that doesn't represent real issues.I-lang-radarItems that are on lang's radar and will need eventual work or consideration.I-types-nominatedNominated for discussion during a types team meeting.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.T-langRelevant to the language teamT-typesRelevant to the types team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions