my understanding of fundamental traits are that a downstream crate may impl it for another foreign type as long as it itself has a generic argument that is set to a local type.
enum LocalMarker{}
trait Realisation: FundamentalTrait<LocalMarker> + ToString{}
impl<T: Realisation> FundamentalTrait<LocalMarker> for T{}
impl Realisation for u8{}
impl Realisation for u16{}
With other words in my understanding this should be valid code, but it is not:
type parameter T must be covered by another type when it appears before the first local type (LocalMarker) [E0210] Note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type Note: in this case, 'before' refers to the following order: impl<..> ForeignTrait<T1, ..., Tn> for T0, where T0 is the first and Tn is the last
Am I correct that this is a bug in the compiler?
Doing it manually for each type instead of a generic impl works correctly, however i realised that using a generic impl gives better compiler errors in a downdownstream crate
No, that’s not right. That’s a misinterpretation, somewhat transferring the concept of #[fundamental]-marked type to the trait case, even though IMO the two features have not so much too do with each other at all.
Instead, what the page you linked already says in an answer:
traits can also be marked with #[fundamental]. This has a dual meaning to fundamental types. If any type doesn't currently implement a fundamental trait, it can be assumed that that type won't implement it in the future (again, barring a breaking change).
so, no compiler-bug, just a misunderstanding on your end of what it is that the feature does.
What #[fundamental]on a trait allows is get rid of some instances of “upstream crate might add such-and-such impl in the future” error cases. I might add a concrete example to this reply shortly. Here’s some example code
trait MyTrait {}
impl<T: Iterator> MyTrait for T {}
trait MyTrait2 {}
impl<T: FnOnce()> MyTrait2 for T {}
//impl MyTrait for u8 {} // not okay; compilation error if uncommented
// error message contants the following:
// note: upstream crates may add a new impl of trait `std::iter::Iterator` for type `u8` in future versions
// okay, because #[fundamental] on FnOnce, which guarantees that types
// such as `u8` that don't currently implement FnOnce will never implement
// FnOnce in the future
impl MyTrait2 for u8 {}
I don't think the fundamental-ness plays a role in these discussions anymore. Blanket impls, including ones “just” of the form
impl<U> FundamentalTrait<U> for AnyConcreteType{}
do in fact constitute technically-breaking changes (when added later) for ordinary traits, too.
The general issue with downstream
impl<T> Trait<LocalType> for T {}
is that it overlaps with
impl<T> Trait<T> for OtherLocalType {}
in other independent downstream crates. The orphan rules give an order/hierarchy to Self and generic argument, and arbitrarily choose to allow the latter and disallow the former, in order to avoid this conflict.
Your example further limits T to T: Realisation (so OtherLocalType from a completely independent downstream crate wouldn’t implement 'Realisation; unless a blanket impl in our same crate that defines Realisation` does this) but the trait solver and its rules aren’t … let’s say “smart enough” … to handle such reasoning, and really, formulating good rules for supporting this seems non-trivial.
Feel free to check out the reference for a write-down of the full rule.
I assume you meant struct LocalMarker {}. Visibility isn’t really a huge hindrance to trait implementations. If you write a generic impl<T> …something…, then all types allowed for T also include types that are not visible at the place the impl was given.[1]
I concede. I am sure there were good reasons why this was exactly decided as it is now for now.
Anyway as concrete impl are still possible my abi still works, and i have been doing all this marker type bending just because we do not have associated traits/metatraits yet.
trait MetaTrait<trait T>: T{}
anyway thanks for taking you time and writing out answers