Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

where clause not honored in trait impl body #20414

Closed
Twisol opened this issue Jan 2, 2015 · 15 comments
Closed

where clause not honored in trait impl body #20414

Twisol opened this issue Jan 2, 2015 · 15 comments
Labels
A-type-system Area: Type system E-needs-test Call for participation: An issue has been fixed and does not reproduce, but no test has been added.

Comments

@Twisol
Copy link

Twisol commented Jan 2, 2015

Even though I've stipulated in the where clause that Newtype<'a, T> must implement Foo for this impl to apply, I can't call answer on a Newtype<'a, T> within the impl body.

Workaround

Per @jroesch below, this can be worked around by using Foo::answer(&mut newtype) instead of newtype.answer().

Test case

rustc 0.13.0-nightly (7608dbad6 2014-12-31 10:06:21 -0800)
binary: rustc
commit-hash: 7608dbad651f02e837ed05eef3d74a6662a6e928
commit-date: 2014-12-31 10:06:21 -0800
host: x86_64-apple-darwin
release: 0.13.0-nightly
trait Foo {
  fn answer(&mut self) -> u8;
}

trait Bar<'a> {
  fn stuff(&'a mut self) -> u8;
}

struct Newtype<'a, T: 'a> {
  value: &'a mut T,
}


impl<'a, T> Bar<'a> for T
where Newtype<'a, T>: Foo {
  fn stuff(&'a mut self) -> u8 {
    let mut newtype: Newtype<'a, T> = Newtype{value: self};
    newtype.answer()
  }
}


fn main() {}
foo.rs:18:13: 18:21 error: type `Newtype<'a, T>` does not implement any method in scope named `answer`
foo.rs:18     newtype.answer()
                      ^~~~~~~~
error: aborting due to previous error
@Twisol
Copy link
Author

Twisol commented Jan 2, 2015

Per theme in #rust, here is a shorter program which exhibits the same problem, where the where information can't be used in the impl body.

trait Foo {
    fn foo_fn(self);
}

struct NoData;

impl NoData where NoData: Foo {
    fn any_fn(self) {
        self.foo_fn()
    }
}

fn main() { }
foo.rs:9:14: 9:22 error: type `NoData` does not implement any method in scope named `foo_fn`
foo.rs:9         self.foo_fn()
                      ^~~~~~~~
error: aborting due to previous error

@theemathas
Copy link
Contributor

A variant that uses a function instead of a method.

trait Foo {
    fn foo_fn(self);
}

struct NoData;

fn any_fn(val: NoData) where NoData: Foo {
    val.foo_fn();
}

fn main() { }

Error:

compiler_bug3.rs:8:9: 8:17 error: type `NoData` does not implement any method in scope named `foo_fn`
compiler_bug3.rs:8     val.foo_fn();
                           ^~~~~~~~
error: aborting due to previous error

@nikomatsakis
Copy link
Contributor

cc @jroesch

@huonw
Copy link
Member

huonw commented Jan 2, 2015

It seems to me that the bug with the NoData: Foo examples is the compiler doesn't recognise that NoData does not implement Foo. IIRC, it's out-of-scope to support bounding types that don't mention a generic parameter in where clauses, meaning those two are actually fundamentally "invalid" atm anyway.

In fact, it seems the original is falling into the same hole since Newtype<'a, T> doesn't implement Foo for any 'a or T. This simpler version of the original suffers the same problem:

trait Foo {
  fn answer(&mut self) -> u8;
}

struct Newtype<'a, T: 'a> {
  value: &'a mut T,
}

// impl<'a, T> Foo for Newtype<'a, T> { fn answer(&mut self) -> u8 { 0 } }

fn stuff<'a, T>(this: &'a mut T) -> u8 where Newtype<'a, T>: Foo {
    let newtype: Newtype<'a, T> = Newtype{value: this};
    newtype.answer()
}

fn main() {}

Uncommenting the impl Foo for Newtype (which should then compile?) dives deep into winter, with an ICE:

<anon>:12:9: 12:16 error: internal compiler error: coherence failed to report ambiguity: cannot locate the impl of the trait `core::kinds::Sized` for the type `Newtype<'a, T>`
<anon>:12     let newtype: Newtype<'a, T> = Newtype{value: this};
                  ^~~~~~~

which is #20413.

@jroesch
Copy link
Member

jroesch commented Jan 2, 2015

@huonw: I agree. There is currently an issue to remove the ability to bound non-parameter types and is detailed in the meta bug related to where-clauses (see: #17657).

@Twisol
Copy link
Author

Twisol commented Jan 2, 2015

In fact, it seems the original is falling into the same hole since Newtype<'a, T> doesn't implement Foo for any 'a or T

My particular use-case is cross-crate: I have one crate defining Newtype, Foo, and Bar, and another crate defining several X and implementing Foo for each Newtype<X>. I want the first crate to impl Bar for all T with Newtype<T>: Foo. At the time of building the first crate, there shouldn't be an error - there isn't enough information at the time to say that no Newtype<'a, T> implements Foo.

The two non-generic examples don't have this possibility, because the type has no generic parameters and thus there is no opportunity for impls from another crate.

Also, a modified version of your example, which only implements Foo for T=(), results in the same error as in the original report.

trait Foo {
  fn answer(&mut self) -> u8;
}

struct Newtype<'a, T: 'a> {
  value: &'a mut T,
}

impl<'a> Foo for Newtype<'a, ()> { fn answer(&mut self) -> u8 { 0 } }

fn stuff<'a, T>(this: &'a mut T) -> u8 where Newtype<'a, T>: Foo {
    let newtype: Newtype<'a, T> = Newtype{value: this};
    newtype.answer()
}

fn main() {}

Twisol added a commit to Twisol/rust-telnet that referenced this issue Jan 3, 2015
…n event context.

The code right now is super nasty, since it is not currently possible to implement
  TelnetChannel for all T where Carrier<T> implements ChannelHandler. This is an
  explicit feature of the `where` clause, and a Rust issue is open for this.
  (rust-lang/rust#20414)
@sellibitze
Copy link
Contributor

I ran into an issue which is very similar (I believe). Here it goes:

trait Trait {
    fn method(self) -> int;
}
struct Wrapper<T> {
    field: T
}
impl<'a, T> Trait for &'a Wrapper<T> where &'a T: Trait {
    fn method(self) -> int {
        let r: &'a T = &self.field;
        r.method()
    }
}
fn main() {}

This is rejected but I think it should compile. The error message is:

<anon>:10:15: 10:23 error: type `&'a T` does not implement any method in scope named `method`
<anon>:10             r.method()
                        ^~~~~~~~
error: aborting due to previous error
playpen: application terminated with error code 101

@jroesch
Copy link
Member

jroesch commented Jan 5, 2015

@sellibitze There is currently a bug with method call style syntax. You can rewrite this code to use UFCS:

trait Trait {
    fn method(self) -> int;
}
struct Wrapper<T> {
    field: T
}
impl<'a, T> Trait for &'a Wrapper<T> where &'a T: Trait {
    fn method(self) -> int {
        let r: &'a T = &self.field;
        Trait::method(r)
    }
}
fn main() {}

That worked for me here: http://is.gd/hqeprS.

@Twisol
Copy link
Author

Twisol commented Jan 5, 2015

@jroesch: Hey, that worked for my use-case too. Weird! I'll add this workaround to the OP for now.

@kmcallister kmcallister added A-type-system Area: Type system I-wrong labels Jan 6, 2015
@Twisol
Copy link
Author

Twisol commented Jan 8, 2015

My original example and @sellibitze's now compile successfully under the latest nightly.

rustc 1.0.0-nightly (ea6f65c5f 2015-01-06 19:47:08 +0000)
binary: rustc
commit-hash: ea6f65c5f1a3f84e010d2cef02a0160804e9567a
commit-date: 2015-01-06 19:47:08 +0000
host: x86_64-apple-darwin
release: 1.0.0-nightly

Probably fixed by #20608?

@jroesch
Copy link
Member

jroesch commented Jan 8, 2015

@Twisol yeah method resolution uses slightly different code then function calls, and there was a bug lurking in the method call code that had been resolved for function calls. Glad everything it is working. I think we can close this one out, but we need someone with repository rights to do so.

@bstrie bstrie closed this as completed Jan 8, 2015
@bstrie bstrie reopened this Jan 8, 2015
@bstrie
Copy link
Contributor

bstrie commented Jan 8, 2015

Temporarily reopened while awaiting a test case PR.

jroesch added a commit to jroesch/rust that referenced this issue Jan 9, 2015
bors added a commit that referenced this issue Jan 9, 2015
Add test for issue #20414

Reviewed-by: alexcrichton
@nikomatsakis nikomatsakis added the E-needs-test Call for participation: An issue has been fixed and does not reproduce, but no test has been added. label Jan 11, 2015
@nikomatsakis
Copy link
Contributor

Labeling as E-needstest per @bstrie's comment

@jroesch
Copy link
Member

jroesch commented Jan 14, 2015

@nikomatsakis We should have a regression now as per: #20800. I should of marked it as closes this issue.

@nikomatsakis
Copy link
Contributor

k

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
A-type-system Area: Type system E-needs-test Call for participation: An issue has been fixed and does not reproduce, but no test has been added.
Projects
None yet
Development

No branches or pull requests

8 participants