Orphanck: Consider opaque types to never cover type parameters
This commit is contained in:
parent
5e6c2b6092
commit
874670399c
@ -924,11 +924,12 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ty::Alias(kind @ (ty::Projection | ty::Inherent | ty::Weak), ..) => {
|
// A rigid alias may normalize to anything.
|
||||||
if ty.has_type_flags(ty::TypeFlags::HAS_TY_PARAM) {
|
// * If it references an infer var, placeholder or bound ty, it may
|
||||||
bug!("unexpected ty param in alias ty");
|
// 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(
|
if ty.has_type_flags(
|
||||||
ty::TypeFlags::HAS_TY_PLACEHOLDER
|
ty::TypeFlags::HAS_TY_PLACEHOLDER
|
||||||
| ty::TypeFlags::HAS_TY_BOUND
|
| ty::TypeFlags::HAS_TY_BOUND
|
||||||
@ -948,7 +949,24 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} 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 @@ where
|
|||||||
// auto trait impl applies. There will never be multiple impls, so we can just
|
// auto trait impl applies. There will never be multiple impls, so we can just
|
||||||
// act as if it were a local type here.
|
// act as if it were a local type here.
|
||||||
ty::CoroutineWitness(..) => ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)),
|
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
|
// 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.
|
// 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
|
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<()> {}
|
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
|
//@ aux-build:foreign-crate.rs
|
||||||
|
//@ revisions: classic next
|
||||||
|
//@[next] compile-flags: -Znext-solver
|
||||||
#![feature(type_alias_impl_trait)]
|
#![feature(type_alias_impl_trait)]
|
||||||
|
|
||||||
extern crate foreign_crate;
|
extern crate foreign_crate;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user