Orphanck: Consider opaque types to never cover type parameters
This commit is contained in:
parent
5e6c2b6092
commit
874670399c
@ -924,11 +924,12 @@ fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
|
||||
}
|
||||
}
|
||||
|
||||
ty::Alias(kind @ (ty::Projection | ty::Inherent | ty::Weak), ..) => {
|
||||
if ty.has_type_flags(ty::TypeFlags::HAS_TY_PARAM) {
|
||||
bug!("unexpected ty param in alias ty");
|
||||
}
|
||||
|
||||
// A rigid alias may normalize to anything.
|
||||
// * If it references an infer var, placeholder or bound ty, it may
|
||||
// normalize to that, so we have to treat it as an uncovered ty param.
|
||||
// * Otherwise it may normalize to any non-type-generic type
|
||||
// be it local or non-local.
|
||||
ty::Alias(kind, _) => {
|
||||
if ty.has_type_flags(
|
||||
ty::TypeFlags::HAS_TY_PLACEHOLDER
|
||||
| ty::TypeFlags::HAS_TY_BOUND
|
||||
@ -948,7 +949,24 @@ fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ControlFlow::Continue(())
|
||||
// Regarding *opaque types* specifically, we choose to treat them as non-local,
|
||||
// even those that appear within the same crate. This seems somewhat surprising
|
||||
// at first, but makes sense when you consider that opaque types are supposed
|
||||
// to hide the underlying type *within the same crate*. When an opaque type is
|
||||
// used from outside the module where it is declared, it should be impossible to
|
||||
// observe anything about it other than the traits that it implements.
|
||||
//
|
||||
// The alternative would be to look at the underlying type to determine whether
|
||||
// or not the opaque type itself should be considered local.
|
||||
//
|
||||
// However, this could make it a breaking change to switch the underlying hidden
|
||||
// type from a local type to a remote type. This would violate the rule that
|
||||
// opaque types should be completely opaque apart from the traits that they
|
||||
// implement, so we don't use this behavior.
|
||||
// Addendum: Moreover, revealing the underlying type is likely to cause cycle
|
||||
// errors as we rely on coherence / the specialization graph during typeck.
|
||||
|
||||
self.found_non_local_ty(ty)
|
||||
}
|
||||
}
|
||||
|
||||
@ -990,35 +1008,6 @@ fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
|
||||
// auto trait impl applies. There will never be multiple impls, so we can just
|
||||
// act as if it were a local type here.
|
||||
ty::CoroutineWitness(..) => ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)),
|
||||
ty::Alias(ty::Opaque, ..) => {
|
||||
// This merits some explanation.
|
||||
// Normally, opaque types are not involved when performing
|
||||
// coherence checking, since it is illegal to directly
|
||||
// implement a trait on an opaque type. However, we might
|
||||
// end up looking at an opaque type during coherence checking
|
||||
// if an opaque type gets used within another type (e.g. as
|
||||
// the type of a field) when checking for auto trait or `Sized`
|
||||
// impls. This requires us to decide whether or not an opaque
|
||||
// type should be considered 'local' or not.
|
||||
//
|
||||
// We choose to treat all opaque types as non-local, even
|
||||
// those that appear within the same crate. This seems
|
||||
// somewhat surprising at first, but makes sense when
|
||||
// you consider that opaque types are supposed to hide
|
||||
// the underlying type *within the same crate*. When an
|
||||
// opaque type is used from outside the module
|
||||
// where it is declared, it should be impossible to observe
|
||||
// anything about it other than the traits that it implements.
|
||||
//
|
||||
// The alternative would be to look at the underlying type
|
||||
// to determine whether or not the opaque type itself should
|
||||
// be considered local. However, this could make it a breaking change
|
||||
// to switch the underlying ('defining') type from a local type
|
||||
// to a remote type. This would violate the rule that opaque
|
||||
// types should be completely opaque apart from the traits
|
||||
// that they implement, so we don't use this behavior.
|
||||
self.found_non_local_ty(ty)
|
||||
}
|
||||
};
|
||||
// A bit of a hack, the `OrphanChecker` is only used to visit a `TraitRef`, so
|
||||
// the first type we visit is always the self type.
|
||||
|
@ -0,0 +1,21 @@
|
||||
error[E0210]: type parameter `T` must be covered by another type when it appears before the first local type (`Local`)
|
||||
--> $DIR/orphan-check-opaque-types-not-covering.rs:17:6
|
||||
|
|
||||
LL | impl<T> foreign::Trait0<Local, T, ()> for Identity<T> {}
|
||||
| ^ type parameter `T` must be covered by another type when it appears before the first local type (`Local`)
|
||||
|
|
||||
= 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
|
||||
|
||||
error[E0210]: type parameter `T` must be covered by another type when it appears before the first local type (`Local`)
|
||||
--> $DIR/orphan-check-opaque-types-not-covering.rs:26:6
|
||||
|
|
||||
LL | impl<T> foreign::Trait1<Local, T> for Opaque<T> {}
|
||||
| ^ type parameter `T` must be covered by another type when it appears before the first local type (`Local`)
|
||||
|
|
||||
= 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
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0210`.
|
@ -0,0 +1,21 @@
|
||||
error[E0210]: type parameter `T` must be covered by another type when it appears before the first local type (`Local`)
|
||||
--> $DIR/orphan-check-opaque-types-not-covering.rs:17:6
|
||||
|
|
||||
LL | impl<T> foreign::Trait0<Local, T, ()> for Identity<T> {}
|
||||
| ^ type parameter `T` must be covered by another type when it appears before the first local type (`Local`)
|
||||
|
|
||||
= 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
|
||||
|
||||
error[E0210]: type parameter `T` must be covered by another type when it appears before the first local type (`Local`)
|
||||
--> $DIR/orphan-check-opaque-types-not-covering.rs:26:6
|
||||
|
|
||||
LL | impl<T> foreign::Trait1<Local, T> for Opaque<T> {}
|
||||
| ^ type parameter `T` must be covered by another type when it appears before the first local type (`Local`)
|
||||
|
|
||||
= 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
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0210`.
|
31
tests/ui/coherence/orphan-check-opaque-types-not-covering.rs
Normal file
31
tests/ui/coherence/orphan-check-opaque-types-not-covering.rs
Normal file
@ -0,0 +1,31 @@
|
||||
// Opaque types never cover type parameters.
|
||||
|
||||
//@ revisions: classic next
|
||||
//@[next] compile-flags: -Znext-solver
|
||||
|
||||
//@ aux-crate:foreign=parametrized-trait.rs
|
||||
//@ edition:2021
|
||||
|
||||
#![feature(type_alias_impl_trait)]
|
||||
|
||||
type Identity<T> = impl Sized;
|
||||
|
||||
fn define_identity<T>(x: T) -> Identity<T> {
|
||||
x
|
||||
}
|
||||
|
||||
impl<T> foreign::Trait0<Local, T, ()> for Identity<T> {}
|
||||
//~^ ERROR type parameter `T` must be covered by another type
|
||||
|
||||
type Opaque<T> = impl Sized;
|
||||
|
||||
fn define_local<T>() -> Opaque<T> {
|
||||
Local
|
||||
}
|
||||
|
||||
impl<T> foreign::Trait1<Local, T> for Opaque<T> {}
|
||||
//~^ ERROR type parameter `T` must be covered by another type
|
||||
|
||||
struct Local;
|
||||
|
||||
fn main() {}
|
@ -1,5 +1,5 @@
|
||||
error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
|
||||
--> $DIR/coherence.rs:14:1
|
||||
--> $DIR/coherence.rs:16:1
|
||||
|
|
||||
LL | impl foreign_crate::ForeignTrait for AliasOfForeignType<()> {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^----------------------
|
14
tests/ui/type-alias-impl-trait/coherence.next.stderr
Normal file
14
tests/ui/type-alias-impl-trait/coherence.next.stderr
Normal file
@ -0,0 +1,14 @@
|
||||
error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
|
||||
--> $DIR/coherence.rs:16:1
|
||||
|
|
||||
LL | impl foreign_crate::ForeignTrait for AliasOfForeignType<()> {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^----------------------
|
||||
| | |
|
||||
| | `AliasOfForeignType<()>` 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
|
||||
|
||||
For more information about this error, try `rustc --explain E0117`.
|
@ -1,4 +1,6 @@
|
||||
//@ aux-build:foreign-crate.rs
|
||||
//@ revisions: classic next
|
||||
//@[next] compile-flags: -Znext-solver
|
||||
#![feature(type_alias_impl_trait)]
|
||||
|
||||
extern crate foreign_crate;
|
||||
|
Loading…
Reference in New Issue
Block a user