Skip to content

Commit 927d01f

Browse files
committed
avoid committing to autoderef in object method probing
1 parent 0ceb30d commit 927d01f

File tree

2 files changed

+81
-7
lines changed

2 files changed

+81
-7
lines changed

src/librustc_typeck/check/method/probe.rs

+22-7
Original file line numberDiff line numberDiff line change
@@ -537,13 +537,28 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
537537
match self_ty.value.value.sty {
538538
ty::Dynamic(ref data, ..) => {
539539
if let Some(p) = data.principal() {
540-
let InferOk { value: instantiated_self_ty, obligations: _ } =
541-
self.fcx.probe_instantiate_query_response(
542-
self.span, &self.orig_steps_var_values, self_ty)
543-
.unwrap_or_else(|_| {
544-
span_bug!(self.span, "{:?} was applicable but now isn't?", self_ty)
545-
});
546-
self.assemble_inherent_candidates_from_object(instantiated_self_ty);
540+
// Subtle: we can't use `instantiate_query_response` here: using it will
541+
// commit to all of the type equalities assumed by inference going through
542+
// autoderef (see the `method-probe-no-guessing` test).
543+
//
544+
// However, in this code, it is OK if we end up with an object type that is
545+
// "more general" than the object type that we are evaluating. For *every*
546+
// object type `MY_OBJECT`, a function call that goes through a trait-ref
547+
// of the form `<MY_OBJECT as SuperTraitOf(MY_OBJECT)>::func` is a valid
548+
// `ObjectCandidate`, and it should be discoverable "exactly" through one
549+
// of the iterations in the autoderef loop, so there is no problem with it
550+
// being discoverable in another one of these iterations.
551+
//
552+
// Using `instantiate_canonical_with_fresh_inference_vars` on our
553+
// `Canonical<QueryResponse<Ty<'tcx>>>` and then *throwing away* the
554+
// `CanonicalVarValues` will exactly give us such a generalization - it
555+
// will still match the original object type, but it won't pollute our
556+
// type variables in any form, so just do that!
557+
let (QueryResponse { value: generalized_self_ty, .. }, _ignored_var_values) =
558+
self.fcx.instantiate_canonical_with_fresh_inference_vars(
559+
self.span, &self_ty);
560+
561+
self.assemble_inherent_candidates_from_object(generalized_self_ty);
547562
self.assemble_inherent_impl_candidates_for_type(p.def_id());
548563
}
549564
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// Check that method matching does not make "guesses" depending on
2+
// Deref impls that don't eventually end up being picked.
3+
4+
use std::ops::Deref;
5+
6+
// An impl with less derefs will get called over an impl with more derefs,
7+
// so `(t: Foo<_>).my_fn()` will use `<Foo<u32> as MyTrait1>::my_fn(t)`,
8+
// and does *not* force the `_` to equal `()`, because the Deref impl
9+
// was *not* used.
10+
11+
trait MyTrait1 {
12+
fn my_fn(&self) {}
13+
}
14+
15+
impl MyTrait1 for Foo<u32> {}
16+
17+
struct Foo<T>(T);
18+
19+
impl Deref for Foo<()> {
20+
type Target = dyn MyTrait1 + 'static;
21+
fn deref(&self) -> &(dyn MyTrait1 + 'static) {
22+
panic!()
23+
}
24+
}
25+
26+
// ...but if there is no impl with less derefs, the "guess" will be
27+
// forced, so `(t: Bar<_>).my_fn2()` is `<dyn MyTrait2 as MyTrait2>::my_fn2(*t)`,
28+
// and because the deref impl is used, the `_` is forced to equal `u8`.
29+
30+
trait MyTrait2 {
31+
fn my_fn2(&self) {}
32+
}
33+
34+
impl MyTrait2 for u32 {}
35+
struct Bar<T>(T, u32);
36+
impl Deref for Bar<u8> {
37+
type Target = dyn MyTrait2 + 'static;
38+
fn deref(&self) -> &(dyn MyTrait2 + 'static) {
39+
&self.1
40+
}
41+
}
42+
43+
// actually invoke things
44+
45+
fn main() {
46+
let mut foo: Option<Foo<_>> = None;
47+
let mut bar: Option<Bar<_>> = None;
48+
let mut first_iter = true;
49+
loop {
50+
if !first_iter {
51+
foo.as_ref().unwrap().my_fn();
52+
bar.as_ref().unwrap().my_fn2();
53+
break;
54+
}
55+
foo = Some(Foo(0));
56+
bar = Some(Bar(Default::default(), 0));
57+
first_iter = false;
58+
}
59+
}

0 commit comments

Comments
 (0)