diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs index 623677482db..ada4a95ed7a 100644 --- a/src/librustc_typeck/check/method/probe.rs +++ b/src/librustc_typeck/check/method/probe.rs @@ -85,6 +85,37 @@ impl<'a, 'gcx, 'tcx> Deref for ProbeContext<'a, 'gcx, 'tcx> { #[derive(Debug)] struct Candidate<'tcx> { + // Candidates are (I'm not quite sure, but they are mostly) basically + // some metadata on top of a `ty::AssociatedItem` (without substs). + // + // However, method probing wants to be able to evaluate the predicates + // for a function with the substs applied - for example, if a function + // has `where Self: Sized`, we don't want to consider it unless `Self` + // is actually `Sized`, and similarly, return-type suggestions want + // to consider the "actual" return type. + // + // The way this is handled is through `xform_self_ty`. It contains + // the receiver type of this candidate, but `xform_self_ty`, + // `xform_ret_ty` and `kind` (which contains the predicates) have the + // generic parameters of this candidate substituted with the *same set* + // of inference variables, which acts as some weird sort of "query". + // + // When we check out a candidate, we require `xform_self_ty` to be + // a subtype of the passed-in self-type, and this equates the type + // variables in the rest of the fields. + // + // For example, if we have this candidate: + // ``` + // trait Foo { + // fn foo(&self) where Self: Sized; + // } + // ``` + // + // Then `xform_self_ty` will be `&'erased ?X` and `kind` will contain + // the predicate `?X: Sized`, so if we are evaluating `Foo` for a + // the receiver `&T`, we'll do the subtyping which will make `?X` + // get the right value, then when we evaluate the predicate we'll check + // if `T: Sized`. xform_self_ty: Ty<'tcx>, xform_ret_ty: Option>, item: ty::AssociatedItem, @@ -506,13 +537,28 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { match self_ty.value.value.sty { ty::Dynamic(ref data, ..) => { if let Some(p) = data.principal() { - let InferOk { value: instantiated_self_ty, obligations: _ } = - self.fcx.probe_instantiate_query_response( - self.span, &self.orig_steps_var_values, self_ty) - .unwrap_or_else(|_| { - span_bug!(self.span, "{:?} was applicable but now isn't?", self_ty) - }); - self.assemble_inherent_candidates_from_object(instantiated_self_ty); + // Subtle: we can't use `instantiate_query_response` here: using it will + // commit to all of the type equalities assumed by inference going through + // autoderef (see the `method-probe-no-guessing` test). + // + // However, in this code, it is OK if we end up with an object type that is + // "more general" than the object type that we are evaluating. For *every* + // object type `MY_OBJECT`, a function call that goes through a trait-ref + // of the form `::func` is a valid + // `ObjectCandidate`, and it should be discoverable "exactly" through one + // of the iterations in the autoderef loop, so there is no problem with it + // being discoverable in another one of these iterations. + // + // Using `instantiate_canonical_with_fresh_inference_vars` on our + // `Canonical>>` and then *throwing away* the + // `CanonicalVarValues` will exactly give us such a generalization - it + // will still match the original object type, but it won't pollute our + // type variables in any form, so just do that! + let (QueryResponse { value: generalized_self_ty, .. }, _ignored_var_values) = + self.fcx.instantiate_canonical_with_fresh_inference_vars( + self.span, &self_ty); + + self.assemble_inherent_candidates_from_object(generalized_self_ty); self.assemble_inherent_impl_candidates_for_type(p.def_id()); } } diff --git a/src/test/run-pass/methods/method-probe-no-guessing-dyn-trait.rs b/src/test/run-pass/methods/method-probe-no-guessing-dyn-trait.rs new file mode 100644 index 00000000000..8c8165a1004 --- /dev/null +++ b/src/test/run-pass/methods/method-probe-no-guessing-dyn-trait.rs @@ -0,0 +1,59 @@ +// Check that method matching does not make "guesses" depending on +// Deref impls that don't eventually end up being picked. + +use std::ops::Deref; + +// An impl with less derefs will get called over an impl with more derefs, +// so `(t: Foo<_>).my_fn()` will use ` as MyTrait1>::my_fn(t)`, +// and does *not* force the `_` to equal `()`, because the Deref impl +// was *not* used. + +trait MyTrait1 { + fn my_fn(&self) {} +} + +impl MyTrait1 for Foo {} + +struct Foo(T); + +impl Deref for Foo<()> { + type Target = dyn MyTrait1 + 'static; + fn deref(&self) -> &(dyn MyTrait1 + 'static) { + panic!() + } +} + +// ...but if there is no impl with less derefs, the "guess" will be +// forced, so `(t: Bar<_>).my_fn2()` is `::my_fn2(*t)`, +// and because the deref impl is used, the `_` is forced to equal `u8`. + +trait MyTrait2 { + fn my_fn2(&self) {} +} + +impl MyTrait2 for u32 {} +struct Bar(T, u32); +impl Deref for Bar { + type Target = dyn MyTrait2 + 'static; + fn deref(&self) -> &(dyn MyTrait2 + 'static) { + &self.1 + } +} + +// actually invoke things + +fn main() { + let mut foo: Option> = None; + let mut bar: Option> = None; + let mut first_iter = true; + loop { + if !first_iter { + foo.as_ref().unwrap().my_fn(); + bar.as_ref().unwrap().my_fn2(); + break; + } + foo = Some(Foo(0)); + bar = Some(Bar(Default::default(), 0)); + first_iter = false; + } +} diff --git a/src/test/ui/methods/method-deref-to-same-trait-object-with-separate-params.rs b/src/test/ui/methods/method-deref-to-same-trait-object-with-separate-params.rs new file mode 100644 index 00000000000..a5dae1c71cd --- /dev/null +++ b/src/test/ui/methods/method-deref-to-same-trait-object-with-separate-params.rs @@ -0,0 +1,177 @@ +#![feature(arbitrary_self_types, coerce_unsized, dispatch_from_dyn, unsize, unsized_locals)] + +// This tests a few edge-cases around `arbitrary_self_types`. Most specifically, +// it checks that the `ObjectCandidate` you get from method matching can't +// match a trait with the same DefId as a supertrait but a bad type parameter. + +use std::marker::PhantomData; + +mod internal { + use std::ops::{CoerceUnsized, Deref, DispatchFromDyn}; + use std::marker::{PhantomData, Unsize}; + + pub struct Smaht(pub Box, pub PhantomData); + + impl Deref for Smaht { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.0 + } + } + impl, U: ?Sized, MISC> CoerceUnsized> + for Smaht + {} + impl, U: ?Sized, MISC> DispatchFromDyn> + for Smaht + {} + + pub trait Foo: X {} + pub trait X { + fn foo(self: Smaht) -> T; + } + + impl X for () { + fn foo(self: Smaht) -> u32 { + 0 + } + } + + pub trait Marker {} + impl Marker for dyn Foo {} + impl X for T { + fn foo(self: Smaht) -> u64 { + 1 + } + } + + impl Deref for dyn Foo { + type Target = (); + fn deref(&self) -> &() { &() } + } + + impl Foo for () {} +} + +pub trait FinalFoo { + fn foo(&self) -> u8; +} + +impl FinalFoo for () { + fn foo(&self) -> u8 { 0 } +} + +mod nuisance_foo { + pub trait NuisanceFoo { + fn foo(self); + } + + impl NuisanceFoo for T { + fn foo(self) {} + } +} + + +fn objectcandidate_impl() { + let x: internal::Smaht<(), u32> = internal::Smaht(Box::new(()), PhantomData); + let x: internal::Smaht = x; + + // This picks `>::foo` via `ObjectCandidate`. + // + // The `TraitCandidate` is not relevant because `X` is not in scope. + let z = x.foo(); + + // Observe the type of `z` is `u32` + let _seetype: () = z; //~ ERROR mismatched types + //~| expected (), found u32 +} + +fn traitcandidate_impl() { + use internal::X; + + let x: internal::Smaht<(), u64> = internal::Smaht(Box::new(()), PhantomData); + let x: internal::Smaht = x; + + // This picks `>::foo` via `TraitCandidate`. + // + // The `ObjectCandidate` does not apply, as it only applies to + // `X` (and not `X`). + let z = x.foo(); + + // Observe the type of `z` is `u64` + let _seetype: () = z; //~ ERROR mismatched types + //~| expected (), found u64 +} + +fn traitcandidate_impl_with_nuisance() { + use internal::X; + use nuisance_foo::NuisanceFoo; + + let x: internal::Smaht<(), u64> = internal::Smaht(Box::new(()), PhantomData); + let x: internal::Smaht = x; + + // This picks `>::foo` via `TraitCandidate`. + // + // The `ObjectCandidate` does not apply, as it only applies to + // `X` (and not `X`). + // + // The NuisanceFoo impl has the same priority as the `X` impl, + // so we get a conflict. + let z = x.foo(); //~ ERROR multiple applicable items in scope +} + + +fn neither_impl() { + let x: internal::Smaht<(), u64> = internal::Smaht(Box::new(()), PhantomData); + let x: internal::Smaht = x; + + // This can't pick the `TraitCandidate` impl, because `Foo` is not + // imported. However, this also can't pick the `ObjectCandidate` + // impl, because it only applies to `X` (and not `X`). + // + // Therefore, neither of the candidates is applicable, and we pick + // the `FinalFoo` impl after another deref, which will return `u8`. + let z = x.foo(); + + // Observe the type of `z` is `u8` + let _seetype: () = z; //~ ERROR mismatched types + //~| expected (), found u8 +} + +fn both_impls() { + use internal::X; + + let x: internal::Smaht<(), u32> = internal::Smaht(Box::new(()), PhantomData); + let x: internal::Smaht = x; + + // This can pick both the `TraitCandidate` and the `ObjectCandidate` impl. + // + // However, the `ObjectCandidate` is considered an "inherent candidate", + // and therefore has priority over both the `TraitCandidate` as well as + // any other "nuisance" candidate" (if present). + let z = x.foo(); + + // Observe the type of `z` is `u32` + let _seetype: () = z; //~ ERROR mismatched types + //~| expected (), found u32 +} + + +fn both_impls_with_nuisance() { + // Similar to the `both_impls` example, except with a nuisance impl to + // make sure the `ObjectCandidate` indeed has a higher priority. + + use internal::X; + use nuisance_foo::NuisanceFoo; + + let x: internal::Smaht<(), u32> = internal::Smaht(Box::new(()), PhantomData); + let x: internal::Smaht = x; + let z = x.foo(); + + // Observe the type of `z` is `u32` + let _seetype: () = z; //~ ERROR mismatched types + //~| expected (), found u32 +} + +fn main() { +} diff --git a/src/test/ui/methods/method-deref-to-same-trait-object-with-separate-params.stderr b/src/test/ui/methods/method-deref-to-same-trait-object-with-separate-params.stderr new file mode 100644 index 00000000000..2d8449b96de --- /dev/null +++ b/src/test/ui/methods/method-deref-to-same-trait-object-with-separate-params.stderr @@ -0,0 +1,72 @@ +error[E0308]: mismatched types + --> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:85:24 + | +LL | let _seetype: () = z; //~ ERROR mismatched types + | ^ expected (), found u32 + | + = note: expected type `()` + found type `u32` + +error[E0308]: mismatched types + --> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:102:24 + | +LL | let _seetype: () = z; //~ ERROR mismatched types + | ^ expected (), found u64 + | + = note: expected type `()` + found type `u64` + +error[E0034]: multiple applicable items in scope + --> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:120:15 + | +LL | let z = x.foo(); //~ ERROR multiple applicable items in scope + | ^^^ multiple `foo` found + | +note: candidate #1 is defined in an impl of the trait `internal::X` for the type `_` + --> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:43:9 + | +LL | fn foo(self: Smaht) -> u64 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: candidate #2 is defined in an impl of the trait `nuisance_foo::NuisanceFoo` for the type `_` + --> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:70:9 + | +LL | fn foo(self) {} + | ^^^^^^^^^^^^ +note: candidate #3 is defined in the trait `FinalFoo` + --> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:57:5 + | +LL | fn foo(&self) -> u8; + | ^^^^^^^^^^^^^^^^^^^^ + = help: to disambiguate the method call, write `FinalFoo::foo(x)` instead + +error[E0308]: mismatched types + --> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:137:24 + | +LL | let _seetype: () = z; //~ ERROR mismatched types + | ^ expected (), found u8 + | + = note: expected type `()` + found type `u8` + +error[E0308]: mismatched types + --> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:155:24 + | +LL | let _seetype: () = z; //~ ERROR mismatched types + | ^ expected (), found u32 + | + = note: expected type `()` + found type `u32` + +error[E0308]: mismatched types + --> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:172:24 + | +LL | let _seetype: () = z; //~ ERROR mismatched types + | ^ expected (), found u32 + | + = note: expected type `()` + found type `u32` + +error: aborting due to 6 previous errors + +Some errors occurred: E0034, E0308. +For more information about an error, try `rustc --explain E0034`. diff --git a/src/test/ui/methods/method-trait-object-with-hrtb.rs b/src/test/ui/methods/method-trait-object-with-hrtb.rs new file mode 100644 index 00000000000..da2f13f5a2f --- /dev/null +++ b/src/test/ui/methods/method-trait-object-with-hrtb.rs @@ -0,0 +1,41 @@ +// compile-pass + +// Check that method probing ObjectCandidate works in the presence of +// auto traits and/or HRTBs. + +mod internal { + pub trait MyObject<'a> { + type Output; + + fn foo(&self) -> Self::Output; + } + + impl<'a> MyObject<'a> for () { + type Output = &'a u32; + + fn foo(&self) -> Self::Output { &4 } + } +} + +fn t1(d: &dyn for<'a> internal::MyObject<'a, Output=&'a u32>) { + d.foo(); +} + +fn t2(d: &dyn internal::MyObject<'static, Output=&'static u32>) { + d.foo(); +} + +fn t3(d: &(dyn for<'a> internal::MyObject<'a, Output=&'a u32> + Sync)) { + d.foo(); +} + +fn t4(d: &(dyn internal::MyObject<'static, Output=&'static u32> + Sync)) { + d.foo(); +} + +fn main() { + t1(&()); + t2(&()); + t3(&()); + t4(&()); +}