I have a case where I'd like to accept either a reference to a stack-allocated type, which could use ToOwned by way of the blanket impl of that trait for T: Clone, or when the alloc feature is enabled, I have a slice-like type whose ToOwned impl turns it into a heap-allocated type.
Unfortunately, for ToOwned to even be available, liballoc must be linked, and AFAICT it has no actual dependencies on anything heap-related, it's just by convention it's only used with heap-allocated types.
Is there a specific reason it's in liballoc instead of libcore alongside core::borrow::Borrow(Mut)?
Its not that easy - just turning it off would be unsound. For example with trait objects, you can write code that strictly relies on no new (i.e. additional) downstream impls appearing for an already known[1] trait on already known types.
The rule exists for good reason, so probably isn't going away. After all, alloc is "just" a crate, so if it can impl core::ToOwned for core::str {, then so could everyone on crates.io and they could all stomp on each other.
Note that there's an important polarity difference to the rule, though.
If we had core::FromOwned, then it'd be perfectly fine to have impl<T> core::FromBorrowed for alloc::Vec<T> { type Borrowed = [T]; } in alloc, and also to have other things like impl<T> FromBorrowed for some_crate::SmallVec<T> { ... } too. (Maybe even FromBorrowed<[T]> -- I haven't thought through all the implications yet.)
So what I really wish for is a better way to move from one trait to another without breaking everything and without blanket overlap rules preventing it from working.
This problem also exists for users with multi-crate workspaces. They can have assurances that technically orphan impls are coherent, but there's no way to explain that to rustc.
(There are more pressing issues with workspaces, though, like how dead code lints break down across module-like crate boundaries)