rust-lang / rust

Empowering everyone to build reliable and efficient software.
https://www.rust-lang.org
Other
99.16k stars 12.8k forks source link

Regression: "no method found" error when calling same method twice, with HRTB impl #37154

Closed mbrubeck closed 8 years ago

mbrubeck commented 8 years ago

The following code (playground link) results in a method resolution error on the second call to x.method(). It compiles successfully if x.method() is called only once:

trait Foo {
    fn method(&self) {}
}

struct Wrapper<T>(T);

impl<T> Foo for Wrapper<T> where for<'a> &'a T: IntoIterator<Item=&'a ()> {}

fn f(x: Wrapper<Vec<()>>) {
    x.method(); // This works.
    x.method(); // error: no method named `method`
}

The error is:

error: no method named `method` found for type `Wrapper<std::vec::Vec<()>>` in the current scope
  --> <anon>:11:7
   |
11 |     x.method(); // error: no method named `method`
   |       ^^^^^^
   |
   = help: items from traits can only be used if the trait is implemented and in scope; the following trait defines an item `method`, perhaps you need to implement it:
   = help: candidate #1: `Foo`

@jonas-schievink suggested this might be a bug in the projection cache or some other cache.

This is a regression from Rust 1.10.0-stable to 1.11.0-stable.

jonas-schievink commented 8 years ago

In case it's useful to anyone, here's a rustc::traits=DEBUG log at the current master (commit d34318dd538bf4c9175e4138b3e4188ea8211620)

EDIT:

Here is the log with rustc_typeck::check::method=DEBUG,rustc::traits=DEBUG,rustc::middle=DEBUG and a small debug! in method probing added by me: https://gist.github.com/jonas-schievink/9551715c4f2bc05a8359e1dafddf7f8e

In particular, the first call causes this:

DEBUG:rustc_typeck::check::method::probe: applicable_candidates: [Candidate { xform_self_ty: &Wrapper<_>, item: ImplOrTraitItem(Method { name: method(69), generics: Generics { parent: Some(DefId { krate: CrateNum(0), node: DefIndex(4) => test/45447b7afbd5e544f7d0f1df0fccd26014d9850130abd3f020b89ff96b82079f-exe::Foo[0] }), parent_regions: 0, parent_types: 1, regions: [], types: [], has_self: true }, predicates: GenericPredicates([]), fty: BareFnTy { unsafety: Normal, abi: Rust, sig: Binder(([&Self]; variadic: false)->()) }, explicit_self: ByReference(ReLateBound(DebruijnIndex { depth: 1 }, BrAnon(0)), MutImmutable), vis: Restricted(NodeId(0)), defaultness: Default, has_body: true, def_id: DefId { krate: CrateNum(0), node: DefIndex(5) => test/45447b7afbd5e544f7d0f1df0fccd26014d9850130abd3f020b89ff96b82079f-exe::Foo[0]::method[0] }, container: TraitContainer(DefId { krate: CrateNum(0), node: DefIndex(4) => test/45447b7afbd5e544f7d0f1df0fccd26014d9850130abd3f020b89ff96b82079f-exe::Foo[0] }) }), kind: ExtensionImplCandidate(DefId { krate: CrateNum(0), node: DefIndex(11) => test/45447b7afbd5e544f7d0f1df0fccd26014d9850130abd3f020b89ff96b82079f-exe::{{impl}}[0] }, Substs { params: [_] }, []), import_id: None }]
DEBUG:rustc_typeck::check::method::confirm: confirm(unadjusted_self_ty=Wrapper<std::vec::Vec<()>>, pick=Pick { item: ImplOrTraitItem(Method { name: method(69), generics: Generics { parent: Some(DefId { krate: CrateNum(0), node: DefIndex(4) => test/45447b7afbd5e544f7d0f1df0fccd26014d9850130abd3f020b89ff96b82079f-exe::Foo[0] }), parent_regions: 0, parent_types: 1, regions: [], types: [], has_self: true }, predicates: GenericPredicates([]), fty: BareFnTy { unsafety: Normal, abi: Rust, sig: Binder(([&Self]; variadic: false)->()) }, explicit_self: ByReference(ReLateBound(DebruijnIndex { depth: 1 }, BrAnon(0)), MutImmutable), vis: Restricted(NodeId(0)), defaultness: Default, has_body: true, def_id: DefId { krate: CrateNum(0), node: DefIndex(5) => test/45447b7afbd5e544f7d0f1df0fccd26014d9850130abd3f020b89ff96b82079f-exe::Foo[0]::method[0] }, container: TraitContainer(DefId { krate: CrateNum(0), node: DefIndex(4) => test/45447b7afbd5e544f7d0f1df0fccd26014d9850130abd3f020b89ff96b82079f-exe::Foo[0] }) }), kind: ExtensionImplPick(DefId { krate: CrateNum(0), node: DefIndex(11) => test/45447b7afbd5e544f7d0f1df0fccd26014d9850130abd3f020b89ff96b82079f-exe::{{impl}}[0] }), import_id: None, autoderefs: 0, autoref: Some(MutImmutable), unsize: None }, supplied_method_types=[])

While the log contains 2 (not sure why not just one) occurrences of this (this logging statement was added by me, so don't go searching the logs from master), after the first call has been resolved:

DEBUG:rustc_typeck::check::method::probe: consider_probe: obligation does not hold: Obligation(predicate=Binder(ProjectionPredicate(ProjectionTy { trait_ref: <&'a std::vec::Vec<()> as std::iter::IntoIterator>, item_name: Item(75) }, &'a ())),depth=0)

Which then causes method::probe to drop the candidate on the ground.

jonas-schievink commented 8 years ago

This, like #36325, was introduced between these two nightlies:

Works on nightly-2016-06-04

rustc 1.11.0-nightly (7de2e6dc8 2016-06-03)
binary: rustc
commit-hash: 7de2e6dc828473b60aefe4d2140a602cbeb6d6f9
commit-date: 2016-06-03
host: x86_64-unknown-linux-gnu
release: 1.11.0-nightly

Broken on nightly-2016-06-05

rustc 1.11.0-nightly (12238b984 2016-06-04)
binary: rustc
commit-hash: 12238b984abfacb2cccea176f862c94aa1231fb5
commit-date: 2016-06-04
host: x86_64-unknown-linux-gnu
release: 1.11.0-nightly

These regressions likely have the same cause. I suspect the projection cache, introduced in #33816.

cc @nikomatsakis

arielb1 commented 8 years ago

The projection cache indeed looks like it.

nikomatsakis commented 8 years ago

So yes did a bit of digging and the projection cache is indeed doing some inappropriate caching here. I have a hacky fix; currently debating about a mildly less hacky one. Still not overly happy with how we handle skolemization today but that's another story. =)

nikomatsakis commented 8 years ago

Pending PR: https://github.com/rust-lang/rust/pull/37294

brson commented 8 years ago

Thanks @nikomatsakis !