Skip to content

Replace current quasi-subtyping relationship for object types with coercions #18737

Closed

Description

@nikomatsakis

Currently the type checker considers A+Send to be a subtype of A, 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

Activity

  1. nikomatsakis commented on Nov 7, 2014

    @nikomatsakis
    ContributorAuthor

    Nominating for P-backcompat-lang, 1.0.

  2. pnkfelix commented on Nov 13, 2014

    @pnkfelix
    Contributor

    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).

  3. added this to the 1.0 milestone on Nov 13, 2014
  4. nikomatsakis commented on Nov 13, 2014

    @nikomatsakis
    ContributorAuthor

    If push came to shove, we could live with this behavior.

  5. aturon commented on Jan 8, 2015

    @aturon
    Contributor

    Nominating. It sounds like this should not be on the -beta milestone, but we should discuss.

  6. pnkfelix commented on Jan 8, 2015

    @pnkfelix
    Contributor

    Assigning 1.0-beta milestone based on potential for breaking lots of code according to nrc.

  7. modified the milestones: 1.0 beta, 1.0 on Jan 8, 2015
  8. alexcrichton commented on Jan 14, 2015

    @alexcrichton
    Member

    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 BoxAny trait we currently have.

  9. 8 remaining items

  10. nikomatsakis commented on Mar 12, 2015

    @nikomatsakis
    ContributorAuthor
  11. nikomatsakis commented on Mar 12, 2015

    @nikomatsakis
    ContributorAuthor

    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 Any API harder to use). In particular, what would you expect this code to do:

    struct S { .. }
    fn foo(x: Box<Any+Send>) -> bool {
        Any::is::<S>(&x)
    }

    Presuming that x is, in fact, a Box<S>, I would expect this to return true. But it returns false. The reason is that Any expects a &Any. It is supplied a &Box<Any+Send>. Rather than deref coercions kicking in, as I expected, we get a trait coercion where Box<Any+Send> is coerced to the trait object Any, and thus the type test fails because S != Box<Any+Send>. The code works fine if you write &*x, in particular. Note that if we did the full fat-pointer-based method dispatch I described above, the same phenomenon would arise.

    This problem is somewhat specific to the Any API, because it is implemented basically for all T, but it suggests that free-fns can also be easily misused. It'd be nice to have an API form that is downright hard to misuse. I'm not sure what's the best fix here.

  12. alexcrichton commented on Mar 12, 2015

    @alexcrichton
    Member

    If the Any+Send case is generalized to Foo+X, does this mean that methods defined via impl Foo will not be available on Box<Foo+X> (regardless of X)?

    Hmm, I encountered a bit of a footgun.

    Hm that is... unfortunate! I do agree though that it is Any-specific, however. I recall running across this footgun in the past (both myself and via questions on IRC), so I wouldn't be too too worried about it. I think it's fine to move to free functions or require UFCS (perhaps favoring UFCS as the methods are #[stable] currently)

  13. arielb1 commented on Mar 12, 2015

    @arielb1
    Contributor

    @nikomatsakis

    How would moving to UFCS solve it? Isn't the problem that with <&Any as Any>::is::<S>(&ex) rustc coerces ex to Any by an autoref (to &Box<Any>) followed by an unsize rather than by an autoderef to Box<Any> followed by an autoref?

  14. nikomatsakis commented on Mar 16, 2015

    @nikomatsakis
    ContributorAuthor

    @alexcrichton

    If the Any+Send case is generalized to Foo+X, does this mean that methods defined via impl Foo will not be available on Box<Foo+X> (regardless of X)?

    Yes.

    I think it's fine to move to free functions or require UFCS (perhaps favoring UFCS as the methods are #[stable] currently)

    I think I favor free fns only because I find it somewhat more ergonomic. For example, you can do use any::is and then write is::<T>(x) vs Any::is(x). Similarly, <Box<Any>>::downcast() is fairly annoying.

    Hm that is... unfortunate! I do agree though that it is Any-specific, however. I recall running across this footgun in the past (both myself and via questions on IRC), so I wouldn't be too too worried about it.

    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.

  15. nikomatsakis commented on Mar 16, 2015

    @nikomatsakis
    ContributorAuthor

    @arielb1

    How would moving to UFCS solve it? Isn't the problem that with <&Any as Any>::is::<S>(&ex) rustc coerces ex to Any by an autoref (to &Box<Any>) followed by an unsize rather than by an autoderef to Box<Any> followed by an autoref?

    Sorry, I'm not sure what comment of mine you are referring to, but:

    1. UFCS solves the problem where I supply a &(Any+Send) and a &Any is expected. This is because we can't make an Any object from an Any+Send object except by upcasting (you can only make an object from a sized type).
    2. UFCS does not solve the footgun I mentioned where you supply an &Box<Any+Send> and an &Any is expected, because Box<Any+Send> can be "objectified" to Any.
  16. reem commented on Mar 16, 2015

    @reem
    Contributor

    @nikomatsakis sorry I didn't get to this sooner. Your approach sounds reasonable, but the footguns surrounding the use of Any and other traits which are implemented for most/all T make me less enthused about this. I can work around anything in a downstream library, but it would make me a lot more apprehensive about using unsafe-any, since the chance of something going horribly wrong is much higher.

  17. nikomatsakis commented on Mar 17, 2015

    @nikomatsakis
    ContributorAuthor

    @reem yes, I was wondering about what's the best solution here. Definitely I found working with Any to be... precarious when I was forward-porting tests. One possibility is of course a custom lint to watch out for things like coercing a &Box<Any+Send> into a &Any as an argument of any::is and friends, but that won't help for unsafe_any (and feels...very tailored!).

    Another possibility is that I could keep the simple hack that dropped all builtin bounds from method dispatch (i.e., step from Trait+Send to Trait). But that feels unfortunate because it is involving a lot of concepts I would like to go away. It's not a very general mechanism so I'd hate to be relying on it forever.

  18. nikomatsakis commented on Mar 17, 2015

    @nikomatsakis
    ContributorAuthor

    Well, something else just occurred to me. One other simple fix here might be to implement the relevant methods for Any on Any+Send as well. Kind of a hack, but at least we'd only have to get the code that does the upcasting correct once, and this is a very common case due to the thread error propagation APIs.

  19. nikomatsakis commented on Mar 17, 2015

    @nikomatsakis
    ContributorAuthor

    I implemented the approach of adding extra impls for Any. This causes all existing code in libstd to work and seems to address the footgun problem, while still leaving us room in the future to expand method dispatch to handle more such cases (this basically just builds in one particular upcast).

  20. added a commit that references this issue on Mar 17, 2015
  21. added a commit that references this issue on Dec 23, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions