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

Tracking issue for `arbitrary_self_types` #44874

Open
arielb1 opened this Issue Sep 26, 2017 · 43 comments

Comments

Projects
None yet
@arielb1
Contributor

arielb1 commented Sep 26, 2017

Tracking issue for #![feature(arbitrary_self_types)].

This needs an RFC before stabilization, and also requires the following issues to be handled:

  • figure out the object safety situation
  • figure out the handling of inference variables behind raw pointers
  • decide whether we want safe virtual raw pointer methods

Object Safety

See #27941 (comment)

Handling of inference variables

Calling a method on *const _ could now pick impls of the form

impl RandomType {
    fn foo(*const Self) {}
}

Because method dispatch wants to be "limited", this won't really work, and as with the existing situation on &_ we should be emitting an "the type of this value must be known in this context" error.

This feels like fairly standard inference breakage, but we need to check the impact of this before proceeding.

Safe virtual raw pointer methods

e.g. this is UB, so we might want to force the call <dyn Foo as Foo>::bar to be unsafe somehow - e.g. by not allowing dyn Foo to be object safe unless bar was an unsafe fn

trait Foo {
    fn bar(self: *const Self);
}

fn main() {
    // creates a raw pointer with a garbage vtable
    let foo: *const dyn Foo = unsafe { mem::transmute([0usize, 0x1000usize]) };
    // and call it
    foo.bar(); // this is UB
}

However, even today you could UB in safe code with mem::size_of_val(foo) on the above code, so this might not be actually a problem.

More information

There's no reason the self syntax has to be restricted to &T, &mut T and Box<T>, we should allow for more types there, e.g.

trait MyStuff {
    fn do_async_task(self: Rc<Self>);
}

impl MyStuff for () {
    fn do_async_task(self: Rc<Self>) {
        // ...
    }
}

Rc::new(()).do_async_stuff();

This doesn't have an RFC, but we want to experiment on this without one.

See #27941.

@arielb1 arielb1 changed the title from Allow methods with arbitrary self-types to Allow trait methods with arbitrary self-types Sep 26, 2017

@porky11

This comment has been minimized.

Show comment
Hide comment
@porky11

porky11 Sep 28, 2017

Why would you need this?
Why wouldn't you write an impl like this:

impl MyStuff for Rc<()> {
    fn do_async_task(self) {
        // ...
    }
}

I'd rather define the trait different. Maybe like this:

trait MyStuff: Rc {
    fn do_async_task(self);
}

In this case, Rc would be a trait type. If every generic type implemented a specific trait (this could be implemented automatically for generic types) this seems more understandable to me.

porky11 commented Sep 28, 2017

Why would you need this?
Why wouldn't you write an impl like this:

impl MyStuff for Rc<()> {
    fn do_async_task(self) {
        // ...
    }
}

I'd rather define the trait different. Maybe like this:

trait MyStuff: Rc {
    fn do_async_task(self);
}

In this case, Rc would be a trait type. If every generic type implemented a specific trait (this could be implemented automatically for generic types) this seems more understandable to me.

@cuviper

This comment has been minimized.

Show comment
Hide comment
@cuviper

cuviper Sep 28, 2017

Member

This could only be allowed for trait methods, right?

For inherent methods, I can't impl Rc<MyType>, but if impl MyType can add methods with self: Rc<Self>, it seems like that would enable weird method shadowing.

Member

cuviper commented Sep 28, 2017

This could only be allowed for trait methods, right?

For inherent methods, I can't impl Rc<MyType>, but if impl MyType can add methods with self: Rc<Self>, it seems like that would enable weird method shadowing.

@arielb1

This comment has been minimized.

Show comment
Hide comment
@arielb1

arielb1 Oct 1, 2017

Contributor

@cuviper

This is still pending lang team decisions (I hope there will be at least 1 RFC) but I think it will only be allowed for trait method impls.

Contributor

arielb1 commented Oct 1, 2017

@cuviper

This is still pending lang team decisions (I hope there will be at least 1 RFC) but I think it will only be allowed for trait method impls.

@arielb1

This comment has been minimized.

Show comment
Hide comment
@arielb1

arielb1 Oct 1, 2017

Contributor

@porky11

You can't implement anything for Rc<YourType> from a crate that does not own the trait.

Contributor

arielb1 commented Oct 1, 2017

@porky11

You can't implement anything for Rc<YourType> from a crate that does not own the trait.

@arielb1

This comment has been minimized.

Show comment
Hide comment
@arielb1

arielb1 Oct 2, 2017

Contributor

So changes needed:

  • remove the current error message for trait methods only, but still have a feature gate.
  • make sure fn(self: Rc<Self>) doesn't accidentally become object-safe
  • make sure method dispatch woks for Rc<Self> methods
  • add tests
Contributor

arielb1 commented Oct 2, 2017

So changes needed:

  • remove the current error message for trait methods only, but still have a feature gate.
  • make sure fn(self: Rc<Self>) doesn't accidentally become object-safe
  • make sure method dispatch woks for Rc<Self> methods
  • add tests

@arielb1 arielb1 added E-mentor and removed E-needs-mentor labels Oct 2, 2017

@SimonSapin

This comment has been minimized.

Show comment
Hide comment
@SimonSapin

SimonSapin Oct 2, 2017

Contributor

I’ll look into this.

Contributor

SimonSapin commented Oct 2, 2017

I’ll look into this.

@arielb1

This comment has been minimized.

Show comment
Hide comment
@arielb1

arielb1 Oct 2, 2017

Contributor

Note that this is only supported to work with trait methods (and trait impl methods), aka

trait Foo {
    fn foo(self: Rc<Self>);
}
impl Foo for () {
    fn foo(self: Rc<Self>) {}
}

and is NOT supposed to work for inherent impl methods:

struct Foo;
impl Foo {
    fn foo(self: Rc<Self>) {}
}
Contributor

arielb1 commented Oct 2, 2017

Note that this is only supported to work with trait methods (and trait impl methods), aka

trait Foo {
    fn foo(self: Rc<Self>);
}
impl Foo for () {
    fn foo(self: Rc<Self>) {}
}

and is NOT supposed to work for inherent impl methods:

struct Foo;
impl Foo {
    fn foo(self: Rc<Self>) {}
}
@SimonSapin

This comment has been minimized.

Show comment
Hide comment
@SimonSapin

SimonSapin Oct 6, 2017

Contributor

I got caught in some more Stylo work that's gonna take a while, so if someone else wants to work on this in the meantime feel free.

Contributor

SimonSapin commented Oct 6, 2017

I got caught in some more Stylo work that's gonna take a while, so if someone else wants to work on this in the meantime feel free.

@kennytm

This comment has been minimized.

Show comment
Hide comment
@kennytm

kennytm Oct 6, 2017

Member

Is this supposed to allow any type as long as it involves Self? Or must it impl Deref<Target=Self>?

trait MyStuff {
    fn a(self: Option<Self>);
    fn b(self: Result<Self, Self>);
    fn c(self: (Self, Self, Self));
    fn d(self: Box<Box<Self>>);
}

impl MyStuff for i32 {
   ...
}

Some(1).a();  // ok?
Ok(2).b();  // ok?
(3, 4, 5).c(); // ok?
(box box 6).d(); // ok?
Member

kennytm commented Oct 6, 2017

Is this supposed to allow any type as long as it involves Self? Or must it impl Deref<Target=Self>?

trait MyStuff {
    fn a(self: Option<Self>);
    fn b(self: Result<Self, Self>);
    fn c(self: (Self, Self, Self));
    fn d(self: Box<Box<Self>>);
}

impl MyStuff for i32 {
   ...
}

Some(1).a();  // ok?
Ok(2).b();  // ok?
(3, 4, 5).c(); // ok?
(box box 6).d(); // ok?

kennytm added a commit to kennytm/rust that referenced this issue Oct 10, 2017

Rollup merge of #45099 - mikeyhew:fix-astconv-self-type-comments, r=n…
…ikomatsakis

Update comments referring to old check_method_self_type

I was browsing the code base, trying to figure out how #44874 could be implemented, and noticed some comments that were out of date and a bit misleading (`check_method_self_type` has since been renamed to `check_method_receiver`). Thought it would be an easy first contribution to Rust!

kennytm added a commit to kennytm/rust that referenced this issue Oct 10, 2017

Rollup merge of #45099 - mikeyhew:fix-astconv-self-type-comments, r=n…
…ikomatsakis

Update comments referring to old check_method_self_type

I was browsing the code base, trying to figure out how #44874 could be implemented, and noticed some comments that were out of date and a bit misleading (`check_method_self_type` has since been renamed to `check_method_receiver`). Thought it would be an easy first contribution to Rust!
@mikeyhew

This comment has been minimized.

Show comment
Hide comment
@mikeyhew

mikeyhew Oct 10, 2017

Contributor

I've started working on this issue. You can see my progress on this branch

Contributor

mikeyhew commented Oct 10, 2017

I've started working on this issue. You can see my progress on this branch

@mikeyhew

This comment has been minimized.

Show comment
Hide comment
@mikeyhew

mikeyhew Nov 2, 2017

Contributor

@arielb1 You seem adamant that this should only be allowed for traits and not structs. Aside from method shadowing, are there other concerns?

Contributor

mikeyhew commented Nov 2, 2017

@arielb1 You seem adamant that this should only be allowed for traits and not structs. Aside from method shadowing, are there other concerns?

@arielb1

This comment has been minimized.

Show comment
Hide comment
@arielb1

arielb1 Nov 2, 2017

Contributor

@mikeyhew

inherent impl methods are loaded based on the type. You shouldn't be able to add a method to Rc<YourType> that is usable without any use statement.

Contributor

arielb1 commented Nov 2, 2017

@mikeyhew

inherent impl methods are loaded based on the type. You shouldn't be able to add a method to Rc<YourType> that is usable without any use statement.

@arielb1

This comment has been minimized.

Show comment
Hide comment
@arielb1

arielb1 Nov 2, 2017

Contributor

That's it, if you write something like

trait Foo {
    fn bar(self: Rc<Self>);
}

Then it can only be used if the trait Foo is in-scope. Even if you do something like fn baz(self: u32); that only works for modules that use the trait.

If you write an inherent impl, then it can be called without having the trait in-scope, which means we have to be more careful to not allow these sorts of things.

Contributor

arielb1 commented Nov 2, 2017

That's it, if you write something like

trait Foo {
    fn bar(self: Rc<Self>);
}

Then it can only be used if the trait Foo is in-scope. Even if you do something like fn baz(self: u32); that only works for modules that use the trait.

If you write an inherent impl, then it can be called without having the trait in-scope, which means we have to be more careful to not allow these sorts of things.

@mikeyhew

This comment has been minimized.

Show comment
Hide comment
@mikeyhew

mikeyhew Nov 3, 2017

Contributor

@arielb1 Can you give an example of what we want to avoid? I'm afraid I don't really see what the issue is. A method you define to take &self will still be callable on Rc<Self>, the same as if you define it to take self: Rc<Self>. And the latter only affectsRc<MyStruct>, not Rc<T> in general.

Contributor

mikeyhew commented Nov 3, 2017

@arielb1 Can you give an example of what we want to avoid? I'm afraid I don't really see what the issue is. A method you define to take &self will still be callable on Rc<Self>, the same as if you define it to take self: Rc<Self>. And the latter only affectsRc<MyStruct>, not Rc<T> in general.

@mikeyhew

This comment has been minimized.

Show comment
Hide comment
@mikeyhew

mikeyhew Nov 4, 2017

Contributor

I've been trying to figure out how we can support dynamic dispatch with arbitrary self types. Basically we need a way to take a CustomPointer<Trait>, and do two things: (1) extract the vtable, so we can call the method, and (2) turn it into a CustomPointer<T> without knowing T.

(1) is pretty straightforward: call Deref::deref and extract the vtable from that. For (2), we'll effectively need to do the opposite of how unsized coercions are implemented for ADTs. We don't know T, but we can can coerce to CustomPointer<()>, assuming CustomPointer<()> has the same layout as CustomPointer<T> for all T: Sized. (Is that true?)

The tough question is, how do we get the type CustomPointer<()>? It looks simple in this case, but what if CustomPointer had multiple type parameters and we had a CustomPointer<Trait, Trait>? Which type parameter do we switch with ()? In the case of unsized coercions, it's easy, because the type to coerce to is given to us. Here, though, we're on our own.

@arielb1 @nikomatsakis any thoughts?

Contributor

mikeyhew commented Nov 4, 2017

I've been trying to figure out how we can support dynamic dispatch with arbitrary self types. Basically we need a way to take a CustomPointer<Trait>, and do two things: (1) extract the vtable, so we can call the method, and (2) turn it into a CustomPointer<T> without knowing T.

(1) is pretty straightforward: call Deref::deref and extract the vtable from that. For (2), we'll effectively need to do the opposite of how unsized coercions are implemented for ADTs. We don't know T, but we can can coerce to CustomPointer<()>, assuming CustomPointer<()> has the same layout as CustomPointer<T> for all T: Sized. (Is that true?)

The tough question is, how do we get the type CustomPointer<()>? It looks simple in this case, but what if CustomPointer had multiple type parameters and we had a CustomPointer<Trait, Trait>? Which type parameter do we switch with ()? In the case of unsized coercions, it's easy, because the type to coerce to is given to us. Here, though, we're on our own.

@arielb1 @nikomatsakis any thoughts?

@nikomatsakis

This comment has been minimized.

Show comment
Hide comment
@nikomatsakis

nikomatsakis Nov 4, 2017

Contributor

@arielb1

and is NOT supposed to work for inherent impl methods:

Wait, why do you not want it work for inherent impl methods? Because of scoping? I'm confused. =)

Contributor

nikomatsakis commented Nov 4, 2017

@arielb1

and is NOT supposed to work for inherent impl methods:

Wait, why do you not want it work for inherent impl methods? Because of scoping? I'm confused. =)

@nikomatsakis

This comment has been minimized.

Show comment
Hide comment
@nikomatsakis

nikomatsakis Nov 4, 2017

Contributor

@mikeyhew

I've been trying to figure out how we can support dynamic dispatch with arbitrary self types.

I do want to support that, but I expected it to be out of scope for this first cut. That is, I expected that if a trait uses anything other than self, &self, &mut self, or self: Box<Self> it would be considered no longer object safe.

Contributor

nikomatsakis commented Nov 4, 2017

@mikeyhew

I've been trying to figure out how we can support dynamic dispatch with arbitrary self types.

I do want to support that, but I expected it to be out of scope for this first cut. That is, I expected that if a trait uses anything other than self, &self, &mut self, or self: Box<Self> it would be considered no longer object safe.

@mikeyhew

This comment has been minimized.

Show comment
Hide comment
@mikeyhew

mikeyhew Nov 4, 2017

Contributor

@nikomatsakis

I do want to support that, but I expected it to be out of scope for this first cut.

I know, but I couldn't help looking into it, it's all very interesting to me :)

Contributor

mikeyhew commented Nov 4, 2017

@nikomatsakis

I do want to support that, but I expected it to be out of scope for this first cut.

I know, but I couldn't help looking into it, it's all very interesting to me :)

@arielb1

This comment has been minimized.

Show comment
Hide comment
@arielb1

arielb1 Nov 5, 2017

Contributor

Wait, why do you not want it work for inherent impl methods? Because of scoping? I'm confused. =)

We need some sort of "orphan rule" to at least prevent people from doing things like this:

struct Foo;
impl Foo {
    fn frobnicate<T>(self: Vec<T>, x: Self) { /* ... */ }
}

Because then every crate in the world can call my_vec.frobnicate(...); without importing anything, so if 2 crates do this there's a conflict when we link them together.

Maybe the best way to solve this would be to require self to be a "thin pointer to Self" in some way (we can't use Deref alone because it doesn't allow for raw pointers - but Deref + deref of raw pointers, or eventually an UnsafeDeref trait that reifies that - would be fine).

I think that if we have the deref-back requirement, there's no problem with allowing inherent methods - we just need to change inherent method search a bit to also look at defids of derefs. So that's probably a better idea than restricting to trait methods only.

Note that the CoerceSized restriction for object safety is orthogonal if we want allocators:

struct Foo;
impl Tr for Foo {
    fn frobnicate<A: Allocator+?Sized>(self: RcWithAllocator<Self, A>) { /* ... */ }
}

Where an RcWithAllocator<Self, A> can be converted to a doubly-fat RcWithAllocator<Tr, Allocator>.

Contributor

arielb1 commented Nov 5, 2017

Wait, why do you not want it work for inherent impl methods? Because of scoping? I'm confused. =)

We need some sort of "orphan rule" to at least prevent people from doing things like this:

struct Foo;
impl Foo {
    fn frobnicate<T>(self: Vec<T>, x: Self) { /* ... */ }
}

Because then every crate in the world can call my_vec.frobnicate(...); without importing anything, so if 2 crates do this there's a conflict when we link them together.

Maybe the best way to solve this would be to require self to be a "thin pointer to Self" in some way (we can't use Deref alone because it doesn't allow for raw pointers - but Deref + deref of raw pointers, or eventually an UnsafeDeref trait that reifies that - would be fine).

I think that if we have the deref-back requirement, there's no problem with allowing inherent methods - we just need to change inherent method search a bit to also look at defids of derefs. So that's probably a better idea than restricting to trait methods only.

Note that the CoerceSized restriction for object safety is orthogonal if we want allocators:

struct Foo;
impl Tr for Foo {
    fn frobnicate<A: Allocator+?Sized>(self: RcWithAllocator<Self, A>) { /* ... */ }
}

Where an RcWithAllocator<Self, A> can be converted to a doubly-fat RcWithAllocator<Tr, Allocator>.

@mikeyhew

This comment has been minimized.

Show comment
Hide comment
@mikeyhew

mikeyhew Nov 5, 2017

Contributor

@arielb1

Because then every crate in the world can call my_vec.frobnicate(...); without importing anything, so if 2 crates do this there's a conflict when we link them together.

Are saying is that there would be a "conflicting symbols for architechture x86_64..." linker error?

Maybe the best way to solve this would be to require self to be a "thin pointer to Self" in some way (we can't use Deref alone because it doesn't allow for raw pointers - but Deref + deref of raw pointers, or eventually an UnsafeDeref trait that reifies that - would be fine).

I'm confused, are you still talking about frobnicate here, or have you moved on to the vtable stuff?

Contributor

mikeyhew commented Nov 5, 2017

@arielb1

Because then every crate in the world can call my_vec.frobnicate(...); without importing anything, so if 2 crates do this there's a conflict when we link them together.

Are saying is that there would be a "conflicting symbols for architechture x86_64..." linker error?

Maybe the best way to solve this would be to require self to be a "thin pointer to Self" in some way (we can't use Deref alone because it doesn't allow for raw pointers - but Deref + deref of raw pointers, or eventually an UnsafeDeref trait that reifies that - would be fine).

I'm confused, are you still talking about frobnicate here, or have you moved on to the vtable stuff?

@arielb1

This comment has been minimized.

Show comment
Hide comment
@arielb1

arielb1 Nov 5, 2017

Contributor

I'm confused, are you still talking about frobnicate here, or have you moved on to the vtable stuff?

The deref-back requirement is supposed to be for everything, not only object-safety. It prevents the problem when one person does

struct MyType;
impl MyType {
    fn foo<T>(self: Vec<(MyType, T)>) { /* ... */ }
}   

While another person does

struct OurType;
impl OurType {
    fn foo<T>(self: Vec<(T, OurType)>) {/* ... */ }
}   

And now you have a conflict on Vec<(MyType, OurType)>. If you include the deref-back requirement, there is no problem with allowing inherent impls.

Contributor

arielb1 commented Nov 5, 2017

I'm confused, are you still talking about frobnicate here, or have you moved on to the vtable stuff?

The deref-back requirement is supposed to be for everything, not only object-safety. It prevents the problem when one person does

struct MyType;
impl MyType {
    fn foo<T>(self: Vec<(MyType, T)>) { /* ... */ }
}   

While another person does

struct OurType;
impl OurType {
    fn foo<T>(self: Vec<(T, OurType)>) {/* ... */ }
}   

And now you have a conflict on Vec<(MyType, OurType)>. If you include the deref-back requirement, there is no problem with allowing inherent impls.

@arielb1 arielb1 changed the title from Allow trait methods with arbitrary self-types to Tracking issue for `arbitrary_self_types` Dec 12, 2017

bors added a commit that referenced this issue Dec 19, 2017

Auto merge of #46664 - mikeyhew:raw_pointer_self, r=arielb1
arbitrary_self_types: add support for raw pointer `self` types

This adds support for raw pointer `self` types, under the `arbitrary_self_types` feature flag. Types like `self: *const Self`, `self: *const Rc<Self>`, `self: Rc<*const Self` are all supported. Object safety checks are updated to allow`self: *const Self` and `self: *mut Self`.

This PR does not add support for `*const self` and `*mut self` syntax. That can be added in a later PR once this code is reviewed and merged.

#44874

r? @arielb1
@SimonSapin

This comment has been minimized.

Show comment
Hide comment
@SimonSapin

SimonSapin Dec 23, 2017

Contributor

Recent Nightlies emit a lot of warnings like this when compiling Servo:

warning[E0619]: the type of this value must be known in this context
  --> /Users/simon/projects/servo/components/script/dom/bindings/iterable.rs:81:34
   |
81 |             dict_return(cx, rval.handle_mut(), true, value.handle())
   |                                  ^^^^^^^^^^
   |
   = note: this will be made into a hard error in a future version of the compiler

First, I had to do git archeology in the rust repo to find that that warning was introduced bc0439b, which is part of #46837, which links here. The error message should probably include some URL to let people read further details and context, ask questions, etc.

Second, I have no idea what this warning is telling me. Why is this code problematic now, while it wasn’t before? What should I do to fix it? The error message should explain some more.

Contributor

SimonSapin commented Dec 23, 2017

Recent Nightlies emit a lot of warnings like this when compiling Servo:

warning[E0619]: the type of this value must be known in this context
  --> /Users/simon/projects/servo/components/script/dom/bindings/iterable.rs:81:34
   |
81 |             dict_return(cx, rval.handle_mut(), true, value.handle())
   |                                  ^^^^^^^^^^
   |
   = note: this will be made into a hard error in a future version of the compiler

First, I had to do git archeology in the rust repo to find that that warning was introduced bc0439b, which is part of #46837, which links here. The error message should probably include some URL to let people read further details and context, ask questions, etc.

Second, I have no idea what this warning is telling me. Why is this code problematic now, while it wasn’t before? What should I do to fix it? The error message should explain some more.

@petrochenkov

This comment has been minimized.

Show comment
Hide comment
@petrochenkov

petrochenkov Dec 23, 2017

Contributor

The breakage from this change seems to be larger than it's normally allowed for compatibility warnings.
A crater run wasn't performed, but if even rustc alone hits multiple instances of broken code (#46914), it means than effect on the ecosystem in general is going to be large.

Contributor

petrochenkov commented Dec 23, 2017

The breakage from this change seems to be larger than it's normally allowed for compatibility warnings.
A crater run wasn't performed, but if even rustc alone hits multiple instances of broken code (#46914), it means than effect on the ecosystem in general is going to be large.

@mikeyhew

This comment has been minimized.

Show comment
Hide comment
@mikeyhew

mikeyhew Dec 23, 2017

Contributor

@SimonSapin I'm working on changing that warning to a future-compatibility lint right now (#46914), which will reference a tracking issue that explains the reason for the change; I still have to finish writing the issue though so right now it doesn't explain anything

Contributor

mikeyhew commented Dec 23, 2017

@SimonSapin I'm working on changing that warning to a future-compatibility lint right now (#46914), which will reference a tracking issue that explains the reason for the change; I still have to finish writing the issue though so right now it doesn't explain anything

@mikeyhew

This comment has been minimized.

Show comment
Hide comment
@mikeyhew

mikeyhew Dec 23, 2017

Contributor

I guess we didn't realize there would be so much breakage from that PR. Right now, I'm finding lots of places in rust-lang/rust that produce this warning, and they are only popping up now that I'm turning it into a full-blown lint, which is turned into a hard error by deny(warnings).

Contributor

mikeyhew commented Dec 23, 2017

I guess we didn't realize there would be so much breakage from that PR. Right now, I'm finding lots of places in rust-lang/rust that produce this warning, and they are only popping up now that I'm turning it into a full-blown lint, which is turned into a hard error by deny(warnings).

@bstrie

This comment has been minimized.

Show comment
Hide comment
@bstrie

bstrie May 12, 2018

Contributor

Can anyone give a summary of what the status is here? Given that we have two accepted, high-priority RFCs mentioning this feature (rust-lang/rfcs#2349 and rust-lang-nursery/futures-rfcs#2), it's concerning that we still don't have even anything resembling an RFC for this. Since I assume (?) that the RFCs in question are going to end up influencing things in libstd I suppose it's not absolutely imperative that we stabilize this anytime soon, but even having an idea of what the design goals of this feature are would be a good start to making sure we don't box ourselves into a corner somehow. It seems unprecedented for a feature to progress so far without even a pre-RFC...

Contributor

bstrie commented May 12, 2018

Can anyone give a summary of what the status is here? Given that we have two accepted, high-priority RFCs mentioning this feature (rust-lang/rfcs#2349 and rust-lang-nursery/futures-rfcs#2), it's concerning that we still don't have even anything resembling an RFC for this. Since I assume (?) that the RFCs in question are going to end up influencing things in libstd I suppose it's not absolutely imperative that we stabilize this anytime soon, but even having an idea of what the design goals of this feature are would be a good start to making sure we don't box ourselves into a corner somehow. It seems unprecedented for a feature to progress so far without even a pre-RFC...

@mikeyhew

This comment has been minimized.

Show comment
Hide comment
@mikeyhew

mikeyhew May 12, 2018

Contributor

@bstrie there is an RFC PR that @withoutboats has opened. As far as implementation is concerned, everything in that RFC is implemented behind the arbitrary_self_types feature gate, except that all custom receiver types are considered non-object-safe, and dynamic dispatch hasn't been implemented. I've been working on implementing that, and you can follow this WIP PR for updates.

There are also some things that are implemented that aren't included in that RFC: notably, raw pointer method receivers, and receivers that deref transitively to Self rather than directly implementing Deref<Target=Self>, e.g. &Rc<Self>.

I hope that clears things up a bit. It doesn't completely address your concerns about RFCs being merged that are depending on an unimplemented, undocumented, and perhaps incompletely designed language feature, but hopefully, once I'm finished implementing the object-safety checks and dynamic dispatch, and that RFC gets merged, the foundation for those other RFCs will be less rocky.

Contributor

mikeyhew commented May 12, 2018

@bstrie there is an RFC PR that @withoutboats has opened. As far as implementation is concerned, everything in that RFC is implemented behind the arbitrary_self_types feature gate, except that all custom receiver types are considered non-object-safe, and dynamic dispatch hasn't been implemented. I've been working on implementing that, and you can follow this WIP PR for updates.

There are also some things that are implemented that aren't included in that RFC: notably, raw pointer method receivers, and receivers that deref transitively to Self rather than directly implementing Deref<Target=Self>, e.g. &Rc<Self>.

I hope that clears things up a bit. It doesn't completely address your concerns about RFCs being merged that are depending on an unimplemented, undocumented, and perhaps incompletely designed language feature, but hopefully, once I'm finished implementing the object-safety checks and dynamic dispatch, and that RFC gets merged, the foundation for those other RFCs will be less rocky.

@bstrie

This comment has been minimized.

Show comment
Hide comment
@bstrie

bstrie May 12, 2018

Contributor

@mikeyhew thanks! Up top I've added a link to the RFC so that people can follow along easier. To be clear, do we intend not to support object safety in receivers for this initial implementation pass, or is that just yet-to-be-implemented?

Contributor

bstrie commented May 12, 2018

@mikeyhew thanks! Up top I've added a link to the RFC so that people can follow along easier. To be clear, do we intend not to support object safety in receivers for this initial implementation pass, or is that just yet-to-be-implemented?

@mikeyhew

This comment has been minimized.

Show comment
Hide comment
@mikeyhew

mikeyhew May 12, 2018

Contributor

@bstrie the initial implementation pass is finished, and it did not include object-safety. The next pass, which I'm working on right now, will include object-safety.

Contributor

mikeyhew commented May 12, 2018

@bstrie the initial implementation pass is finished, and it did not include object-safety. The next pass, which I'm working on right now, will include object-safety.

@burdges

This comment has been minimized.

Show comment
Hide comment
@burdges

burdges Jul 5, 2018

There are interesting covarint uses of this :

fn foo<'a,E,F: FnOnce() -> Result<&'a mut Cat,E>>(self: F, bar: Bar) -> Result<Dog,E> {
    let h = heavy_setup(bar);
    let r = self()?.light_manipulations(h);
    Ok( heavy_conclusions(r) )
}

Now foo(|| &mut my_mutex.bla.lock()?.deref_mut().blabla) locks only briefly in the middle, but the API containing foo need not plan around any particular struct hierarchy or locking scheme, whiole still enforcing that heavy_setup be run before heavy_conclusions.

burdges commented Jul 5, 2018

There are interesting covarint uses of this :

fn foo<'a,E,F: FnOnce() -> Result<&'a mut Cat,E>>(self: F, bar: Bar) -> Result<Dog,E> {
    let h = heavy_setup(bar);
    let r = self()?.light_manipulations(h);
    Ok( heavy_conclusions(r) )
}

Now foo(|| &mut my_mutex.bla.lock()?.deref_mut().blabla) locks only briefly in the middle, but the API containing foo need not plan around any particular struct hierarchy or locking scheme, whiole still enforcing that heavy_setup be run before heavy_conclusions.

@mikeyhew

This comment has been minimized.

Show comment
Hide comment
@mikeyhew

mikeyhew Jul 6, 2018

Contributor

@burdges that wouldn't be possible, because the self argument must Deref to Self. Why does the closure need to be a method receiver and not just a normal argument?

You could create a Thunk<T> that holds a closure that gets called when it's dereferenced. As far as I know, though, Deref impls aren't supposed to panic, so that might be an antipattern.

Contributor

mikeyhew commented Jul 6, 2018

@burdges that wouldn't be possible, because the self argument must Deref to Self. Why does the closure need to be a method receiver and not just a normal argument?

You could create a Thunk<T> that holds a closure that gets called when it's dereferenced. As far as I know, though, Deref impls aren't supposed to panic, so that might be an antipattern.

@burdges

This comment has been minimized.

Show comment
Hide comment
@burdges

burdges Jul 7, 2018

Yes, a normal argument works just fine of course, and works on stable. I even used it that way by mistake, instead of writing (|| &mut my_mutex.bla.lock()?.deref_mut().blabla).foo().

At first blush, this seemingly made sense as a method receiver, but yeah arbitrary self types do sounds far off now, and normal arguments might make more sense and/or improve type inference and error messages.

burdges commented Jul 7, 2018

Yes, a normal argument works just fine of course, and works on stable. I even used it that way by mistake, instead of writing (|| &mut my_mutex.bla.lock()?.deref_mut().blabla).foo().

At first blush, this seemingly made sense as a method receiver, but yeah arbitrary self types do sounds far off now, and normal arguments might make more sense and/or improve type inference and error messages.

@eliantor

This comment has been minimized.

Show comment
Hide comment
@eliantor

eliantor Jul 9, 2018

Is this planned to be in 2018 edition?

eliantor commented Jul 9, 2018

Is this planned to be in 2018 edition?

@mikeyhew

This comment has been minimized.

Show comment
Hide comment
@mikeyhew

mikeyhew Jul 17, 2018

Contributor

@eliantor no it isn't. It still isn't fully implemented, and once it is, it will probably still be a while until it is stabilized. The RFC isn't merged yet either, and I hope to get a working implementation before we do merge it.

Contributor

mikeyhew commented Jul 17, 2018

@eliantor no it isn't. It still isn't fully implemented, and once it is, it will probably still be a while until it is stabilized. The RFC isn't merged yet either, and I hope to get a working implementation before we do merge it.

@withoutboats

This comment has been minimized.

Show comment
Hide comment
@withoutboats

withoutboats Aug 29, 2018

Contributor

I'm having some trouble with method resolution when passing through multiple dereferences:

#![feature(pin, arbitrary_self_types)]

use std::marker::Unpin;
use std::ops::{Deref, DerefMut};

#[derive(Copy, Clone)]
pub struct Pin<P> {
    pointer: P,
}

impl<P, T: ?Sized> Deref for Pin<P> where
    P: Deref<Target = T>,
{
    type Target = T;
    fn deref(&self) -> &T {
        &*self.pointer
    }
}

impl<P, T: ?Sized> DerefMut for Pin<P> where
    P: DerefMut<Target = T>,
    T: Unpin,
{
    fn deref_mut(&mut self) -> &mut T {
        &mut *self.pointer
    }
}

trait Future {
    type Output;
    fn poll(self: Pin<&mut Self>) -> Self::Output;
}

impl Future for () {
    type Output = ();
    fn poll(self: Pin<&mut Self>) -> Self::Output { }
}

fn test(pin: Pin<&mut ()>) {
    pin.poll()
}

https://play.rust-lang.org/?gist=77af55591988d9cf2aa216e8b4fcb8cb&version=nightly&mode=debug&edition=2015

@mikeyhew does this look like a bug or am I doing something wrong?

Contributor

withoutboats commented Aug 29, 2018

I'm having some trouble with method resolution when passing through multiple dereferences:

#![feature(pin, arbitrary_self_types)]

use std::marker::Unpin;
use std::ops::{Deref, DerefMut};

#[derive(Copy, Clone)]
pub struct Pin<P> {
    pointer: P,
}

impl<P, T: ?Sized> Deref for Pin<P> where
    P: Deref<Target = T>,
{
    type Target = T;
    fn deref(&self) -> &T {
        &*self.pointer
    }
}

impl<P, T: ?Sized> DerefMut for Pin<P> where
    P: DerefMut<Target = T>,
    T: Unpin,
{
    fn deref_mut(&mut self) -> &mut T {
        &mut *self.pointer
    }
}

trait Future {
    type Output;
    fn poll(self: Pin<&mut Self>) -> Self::Output;
}

impl Future for () {
    type Output = ();
    fn poll(self: Pin<&mut Self>) -> Self::Output { }
}

fn test(pin: Pin<&mut ()>) {
    pin.poll()
}

https://play.rust-lang.org/?gist=77af55591988d9cf2aa216e8b4fcb8cb&version=nightly&mode=debug&edition=2015

@mikeyhew does this look like a bug or am I doing something wrong?

@andreytkachenko

This comment has been minimized.

Show comment
Hide comment
@andreytkachenko

andreytkachenko Aug 30, 2018

Am I right, there are no way to declare vector of Futures yet?:

Vec<Box<Future<Output = u32>>>

andreytkachenko commented Aug 30, 2018

Am I right, there are no way to declare vector of Futures yet?:

Vec<Box<Future<Output = u32>>>
@mikeyhew

This comment has been minimized.

Show comment
Hide comment
@mikeyhew

mikeyhew Aug 30, 2018

Contributor

@withoutboats that looks like a bug. pin.poll() should work. Future::poll(pin) does work, so it is indeed a problem with method lookup.

@andreytkachenko as far as I know, the Future trait from libcore is not object safe yet. So no, that's not possible yet. Once Pin is considered object safe by the compiler, though, it will be.

Contributor

mikeyhew commented Aug 30, 2018

@withoutboats that looks like a bug. pin.poll() should work. Future::poll(pin) does work, so it is indeed a problem with method lookup.

@andreytkachenko as far as I know, the Future trait from libcore is not object safe yet. So no, that's not possible yet. Once Pin is considered object safe by the compiler, though, it will be.

@withoutboats

This comment has been minimized.

Show comment
Hide comment
@withoutboats

withoutboats Aug 31, 2018

Contributor

@mikeyhew the bug seems connected to #53843 which manifests on stable and isn't tied to this feature

@andreytkachenko to work around the problem, there's currently a type called FutureObj you can use

Contributor

withoutboats commented Aug 31, 2018

@mikeyhew the bug seems connected to #53843 which manifests on stable and isn't tied to this feature

@andreytkachenko to work around the problem, there's currently a type called FutureObj you can use

@andreytkachenko

This comment has been minimized.

Show comment
Hide comment
@andreytkachenko

andreytkachenko Aug 31, 2018

@mikeyhew @withoutboats Thank you for your replies, I'll take a look on FutureObj

andreytkachenko commented Aug 31, 2018

@mikeyhew @withoutboats Thank you for your replies, I'll take a look on FutureObj

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment