-
Notifications
You must be signed in to change notification settings - Fork 12.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Replace current quasi-subtyping relationship for object types with coercions #18737
Comments
Nominating for P-backcompat-lang, 1.0. |
P-backcompat-lang, 1.0, but its possible that this is lesser priority than other P-backcompat-lang issues (i.e. we may be able to hold off on fixing this until the beta period). |
If push came to shove, we could live with this behavior. |
Nominating. It sounds like this should not be on the -beta milestone, but we should discuss. |
Assigning 1.0-beta milestone based on potential for breaking lots of code according to nrc. |
An example that this may fix (or maybe a good smoke test) pub trait Any: 'static {}
pub struct Box2<T: ?Sized>(Box<T>);
impl Box2<Any> {
pub fn foo(self) {}
}
fn t2(t: Box2<Any + Send>) {
t.foo(); // type `Box2<Any + Send>` does not implement any method in scope named `foo`
} This is largely in relation to the |
This commit aims to stabilize the `TypeId` abstraction by moving it out of the `intrinsics` module into the `any` module of the standard library. Specifically, * `TypeId` is now defined at `std::any::TypeId` * `TypeId::hash` has been removed in favor of an implementation of `Hash`. This commit also performs a final pass over the `any` module, confirming the following: * `Any::get_type_id` remains unstable as *usage* of the `Any` trait will likely never require this, and the `Any` trait does not need to be implemented for any other types. As a result, this implementation detail can remain unstable until associated statics are implemented. * `Any::downcast_ref` is now stable * `Any::downcast_mut` is now stable * `BoxAny` remains unstable. While a direct impl on `Box<Any>` is allowed today it does not allow downcasting of trait objects like `Box<Any + Send>` (those returned from `Thread::join`). This is covered by rust-lang#18737. * `BoxAny::downcast` is now stable.
This commit aims to stabilize the `TypeId` abstraction by moving it out of the `intrinsics` module into the `any` module of the standard library. Specifically, * `TypeId` is now defined at `std::any::TypeId` * `TypeId::hash` has been removed in favor of an implementation of `Hash`. This commit also performs a final pass over the `any` module, confirming the following: * `Any::get_type_id` remains unstable as *usage* of the `Any` trait will likely never require this, and the `Any` trait does not need to be implemented for any other types. As a result, this implementation detail can remain unstable until associated statics are implemented. * `Any::downcast_ref` is now stable * `Any::downcast_mut` is now stable * `BoxAny` remains unstable. While a direct impl on `Box<Any>` is allowed today it does not allow downcasting of trait objects like `Box<Any + Send>` (those returned from `Thread::join`). This is covered by #18737. * `BoxAny::downcast` is now stable.
This commit aims to stabilize the `TypeId` abstraction by moving it out of the `intrinsics` module into the `any` module of the standard library. Specifically, * `TypeId` is now defined at `std::any::TypeId` * `TypeId::hash` has been removed in favor of an implementation of `Hash`. This commit also performs a final pass over the `any` module, confirming the following: * `Any::get_type_id` remains unstable as *usage* of the `Any` trait will likely never require this, and the `Any` trait does not need to be implemented for any other types. As a result, this implementation detail can remain unstable until associated statics are implemented. * `Any::downcast_ref` is now stable * `Any::downcast_mut` is now stable * `BoxAny` remains unstable. While a direct impl on `Box<Any>` is allowed today it does not allow downcasting of trait objects like `Box<Any + Send>` (those returned from `Thread::join`). This is covered by rust-lang#18737. * `BoxAny::downcast` is now stable.
This commit aims to stabilize the `TypeId` abstraction by moving it out of the `intrinsics` module into the `any` module of the standard library. Specifically, * `TypeId` is now defined at `std::any::TypeId` * `TypeId::hash` has been removed in favor of an implementation of `Hash`. This commit also performs a final pass over the `any` module, confirming the following: * `Any::get_type_id` remains unstable as *usage* of the `Any` trait will likely never require this, and the `Any` trait does not need to be implemented for any other types. As a result, this implementation detail can remain unstable until associated statics are implemented. * `Any::downcast_ref` is now stable * `Any::downcast_mut` is now stable * `BoxAny` remains unstable. While a direct impl on `Box<Any>` is allowed today it does not allow downcasting of trait objects like `Box<Any + Send>` (those returned from `Thread::join`). This is covered by #18737. * `BoxAny::downcast` is now stable.
OK, so I have a fix for this issue, but there is a bit of a complication. In particular, removing the subtyping relation means that methods implemented on (say) Here are people I think might have an opinion on this that are not already familiar with the details: @reem @alexcrichton Detailed explanationSo, after this patch, Method dispatch already makes some effort to accommodate unsizing. The idea is that it begins by creating a series of autoderef steps based on the type of the receiver (e.g., Besides the limitation around matching OK, I've laid the groundwork. Now let me tell you about two things. First, what I tried so far, and second, what I think we probably eventually want. What I implemented so far is a simple hack. Basically I extended the unsize support that we have today ( What I think we probably want to do is extend method dispatching beyond subtyping to also consider (at least some) implicit coercions. I'm not entirely sure how we want to do this, I see a lot of options. For example, we might consider all implicit coercions. We might try only fat pointer coercions. We might not even want to try all fat pointer coercions. There are lots of potential interactions and more than a few unknowns here. For example, will we have widening coercions in the future? How exactly will DST coercions work and what will they cover? Will people even care if these coercions are missing from method dispatch? (Oh, and also, these more general solutions are kind of a pain to implement right now, though feasible.) Let me give you one example of a potentially bad interaction I was concerned about. Imagine that at each step, where we now consider subtyping, we also considered fat pointer coercion. One of the fat pointer coercions is that This led me to two options:
So option 2 seems good, but it also seems like an extension we can toy with in the future, and in particular we can experiment around the precise set of coercions we want to consider. What I propose to doRemove the subtyping relation and add upcast coercions (implemented). Leave method dispatch as it is (implemented). This includes the This does affect the This may affect other APIs outside of std, but I am not sure. This is partly why I cc'd @reem, because I know he does various bits of magic, though I don't think the things I've seen would have their public APIs affected by these changes. |
I also posted the above comment to discuss: http://internals.rust-lang.org/t/impact-of-fixing-subtyping-for-object-types-18737/1709 |
Hmm, I encountered a bit of a footgun. It's kind of related to all of the method dispatch above actually (and suggests, in fact, that more general fat pointer coercion would make the struct S { .. }
fn foo(x: Box<Any+Send>) -> bool {
Any::is::<S>(&x)
} Presuming that This problem is somewhat specific to the |
If the
Hm that is... unfortunate! I do agree though that it is |
How would moving to UFCS solve it? Isn't the problem that with |
Yes.
I think I favor free fns only because I find it somewhat more ergonomic. For example, you can do
I don't see any clear sol'n -- but I did find forward porting some tests to be fairly annoying due to this. This was mostly a problem in various "torture tests" that seemed to have particularly confusing type casts going on though. |
Sorry, I'm not sure what comment of mine you are referring to, but:
|
@nikomatsakis sorry I didn't get to this sooner. Your approach sounds reasonable, but the footguns surrounding the use of |
@reem yes, I was wondering about what's the best solution here. Definitely I found working with Another possibility is that I could keep the simple hack that dropped all builtin bounds from method dispatch (i.e., step from |
Well, something else just occurred to me. One other simple fix here might be to implement the relevant methods for |
I implemented the approach of adding extra impls for |
This upcast coercion currently never requires vtable changes. It should be generalized. This is a [breaking-change] -- if you have an impl on an object type like `impl SomeTrait`, then this will no longer be applicable to object types like `SomeTrait+Send`. In the standard library, this primarily affected `Any`, and this PR adds impls for `Any+Send` as to keep the API the same in practice. An alternate workaround is to use UFCS form or standalone fns. For more details, see <#18737 (comment)>. r? @nrc
Currently the type checker considers
A+Send
to be a subtype ofA
, but not for the purposes of inference. Part of the DST / coercion plan was to generalize this into a coercion relationship so that it can accommodate arbitrary traits. We should do this before 1.0 as it will cause various minor bits of breakage. Among other things, method resolution will have to be adapted to accommodate this change.cc @nick29581
The text was updated successfully, but these errors were encountered: