Skip to content

autoderef: (&mut foo[bar]).baz() works, but foo[bar].baz() fails to type check due to wrong deref #21630

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

Closed
japaric opened this issue Jan 25, 2015 · 3 comments · Fixed by #21949
Labels
A-resolve Area: Name/path resolution done by `rustc_resolve` specifically

Comments

@japaric
Copy link
Member

japaric commented Jan 25, 2015

STR

use std::ops::{Index, IndexMut};

struct Foo;
struct Bar(Vec<i8>);

#[cfg(not(works))]
impl Index<Foo> for Bar {
    type Output = [i8];

    fn index(&self, _: &Foo) -> &[i8] {
        &*self.0
    }
}

#[cfg(works)]
impl Index<Foo> for Bar {
    type Output = Vec<i8>;

    fn index(&self, _: &Foo) -> &Vec<i8> {
        &self.0
    }
}

impl IndexMut<Foo> for Bar {
    type Output = Vec<i8>;

    fn index_mut(&mut self, _: &Foo) -> &mut Vec<i8> {
        &mut self.0
    }
}

fn test(bar: &mut Bar) {
    // these two should work
    bar[Foo].push(0);  //~ error: type `[i8]` does not implement any method in scope named `push`
    (&mut bar[Foo]).push(0);  // OK
}

fn main() {}

Version

rustc 1.0.0-nightly (4e4e8cff1 2015-01-24 22:14:14 +0000)

When <Bar as Index<Foo>>::Output == <Bar as IndexMut<Foo>>::Output == Vec<i8>, the bar[Foo].push(0) method call properly derefs bar[Foo] to Vec<i8>. But, when [i8] = <Bar as Index<Foo>>::Output != <Bar as IndexMut<Foo>>::Output = Vec<i8>, the method call wrongly derefs bar[Foo] to [i8]. In both cases explictly using (&mut bar[Foo]).push(0) does work.

cc @nikomatsakis @nick29581

@kmcallister kmcallister added the A-resolve Area: Name/path resolution done by `rustc_resolve` specifically label Jan 28, 2015
@nikomatsakis
Copy link
Contributor

This is not supposed to be possible. We screwed up somewhere in the definition of the Index traits -- there needs to be a constraint that the output type for IndexMut is always consistent with the Output type for Index -- in fact, I think there should just be one output type.

@nikomatsakis
Copy link
Contributor

Same with Deref.

@japaric
Copy link
Member Author

japaric commented Feb 2, 2015

@nikomatsakis Should we change the definition of Index/IndexMut to be a hierarchy like Deref/DerefMut then?

// `?Sized` bounds omitted for brevity
trait Index<Index> {
    type Output;
    fn index(&self, &Index) -> &Self::Output;
}

trait IndexMut<Index>: Index<Index> {
    fn index_mut(&mut self, &Index) -> &mut <Self as Index<Index>>::Output;
}

cc @aturon

japaric pushed a commit to japaric/rust that referenced this issue Feb 7, 2015
bors added a commit that referenced this issue Feb 7, 2015
closes #21630

Overloaded indexing (`&[mut] foo[bar]`) only works when `<Self as Index>::Output` is the same as `<Self as IndexMut>::Output` (see issue above). To restrict implementations of `IndexMut` that doesn't work, this PR makes `IndexMut` a supertrait over `Index`, i.e. `trait IndexMut<I>: Index<I>`, just like in the `trait DerefMut: Deref` case.

This breaks all downstream implementations of `IndexMut`, in most cases this simply means removing the `type Output = ..` bit, which is now redundant, from `IndexMut` implementations:

``` diff
 impl Index<Foo> for Bar {
     type Output = Baz;
     ..
 }

 impl IndexMut<Foo> for Bar {
-    type Output = Baz;
     ..
 }
```

[breaking-change]

---

r? @nikomatsakis
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-resolve Area: Name/path resolution done by `rustc_resolve` specifically
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants