Skip to content

Commit 4c9233c

Browse files
committed
Auto merge of #57885 - arielb1:xform-probe, r=nikomatsakis
Avoid committing to autoderef in object method probing This fixes the "leak" introduced in #57835 (see test for details, also apparently #54252 had no tests for the "leaks" that were fixed in it, so go ahead and add one). Maybe beta-nominating because regression, but I'm against landing things on beta we don't have to. r? @nikomatsakis
2 parents d329d46 + 927d01f commit 4c9233c

5 files changed

+402
-7
lines changed

src/librustc_typeck/check/method/probe.rs

+53-7
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,37 @@ impl<'a, 'gcx, 'tcx> Deref for ProbeContext<'a, 'gcx, 'tcx> {
8585

8686
#[derive(Debug)]
8787
struct Candidate<'tcx> {
88+
// Candidates are (I'm not quite sure, but they are mostly) basically
89+
// some metadata on top of a `ty::AssociatedItem` (without substs).
90+
//
91+
// However, method probing wants to be able to evaluate the predicates
92+
// for a function with the substs applied - for example, if a function
93+
// has `where Self: Sized`, we don't want to consider it unless `Self`
94+
// is actually `Sized`, and similarly, return-type suggestions want
95+
// to consider the "actual" return type.
96+
//
97+
// The way this is handled is through `xform_self_ty`. It contains
98+
// the receiver type of this candidate, but `xform_self_ty`,
99+
// `xform_ret_ty` and `kind` (which contains the predicates) have the
100+
// generic parameters of this candidate substituted with the *same set*
101+
// of inference variables, which acts as some weird sort of "query".
102+
//
103+
// When we check out a candidate, we require `xform_self_ty` to be
104+
// a subtype of the passed-in self-type, and this equates the type
105+
// variables in the rest of the fields.
106+
//
107+
// For example, if we have this candidate:
108+
// ```
109+
// trait Foo {
110+
// fn foo(&self) where Self: Sized;
111+
// }
112+
// ```
113+
//
114+
// Then `xform_self_ty` will be `&'erased ?X` and `kind` will contain
115+
// the predicate `?X: Sized`, so if we are evaluating `Foo` for a
116+
// the receiver `&T`, we'll do the subtyping which will make `?X`
117+
// get the right value, then when we evaluate the predicate we'll check
118+
// if `T: Sized`.
88119
xform_self_ty: Ty<'tcx>,
89120
xform_ret_ty: Option<Ty<'tcx>>,
90121
item: ty::AssociatedItem,
@@ -506,13 +537,28 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
506537
match self_ty.value.value.sty {
507538
ty::Dynamic(ref data, ..) => {
508539
if let Some(p) = data.principal() {
509-
let InferOk { value: instantiated_self_ty, obligations: _ } =
510-
self.fcx.probe_instantiate_query_response(
511-
self.span, &self.orig_steps_var_values, self_ty)
512-
.unwrap_or_else(|_| {
513-
span_bug!(self.span, "{:?} was applicable but now isn't?", self_ty)
514-
});
515-
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);
516562
self.assemble_inherent_impl_candidates_for_type(p.def_id());
517563
}
518564
}
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+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
#![feature(arbitrary_self_types, coerce_unsized, dispatch_from_dyn, unsize, unsized_locals)]
2+
3+
// This tests a few edge-cases around `arbitrary_self_types`. Most specifically,
4+
// it checks that the `ObjectCandidate` you get from method matching can't
5+
// match a trait with the same DefId as a supertrait but a bad type parameter.
6+
7+
use std::marker::PhantomData;
8+
9+
mod internal {
10+
use std::ops::{CoerceUnsized, Deref, DispatchFromDyn};
11+
use std::marker::{PhantomData, Unsize};
12+
13+
pub struct Smaht<T: ?Sized, MISC>(pub Box<T>, pub PhantomData<MISC>);
14+
15+
impl<T: ?Sized, MISC> Deref for Smaht<T, MISC> {
16+
type Target = T;
17+
18+
fn deref(&self) -> &Self::Target {
19+
&self.0
20+
}
21+
}
22+
impl<T: ?Sized + Unsize<U>, U: ?Sized, MISC> CoerceUnsized<Smaht<U, MISC>>
23+
for Smaht<T, MISC>
24+
{}
25+
impl<T: ?Sized + Unsize<U>, U: ?Sized, MISC> DispatchFromDyn<Smaht<U, MISC>>
26+
for Smaht<T, MISC>
27+
{}
28+
29+
pub trait Foo: X<u32> {}
30+
pub trait X<T> {
31+
fn foo(self: Smaht<Self, T>) -> T;
32+
}
33+
34+
impl X<u32> for () {
35+
fn foo(self: Smaht<Self, u32>) -> u32 {
36+
0
37+
}
38+
}
39+
40+
pub trait Marker {}
41+
impl Marker for dyn Foo {}
42+
impl<T: Marker + ?Sized> X<u64> for T {
43+
fn foo(self: Smaht<Self, u64>) -> u64 {
44+
1
45+
}
46+
}
47+
48+
impl Deref for dyn Foo {
49+
type Target = ();
50+
fn deref(&self) -> &() { &() }
51+
}
52+
53+
impl Foo for () {}
54+
}
55+
56+
pub trait FinalFoo {
57+
fn foo(&self) -> u8;
58+
}
59+
60+
impl FinalFoo for () {
61+
fn foo(&self) -> u8 { 0 }
62+
}
63+
64+
mod nuisance_foo {
65+
pub trait NuisanceFoo {
66+
fn foo(self);
67+
}
68+
69+
impl<T: ?Sized> NuisanceFoo for T {
70+
fn foo(self) {}
71+
}
72+
}
73+
74+
75+
fn objectcandidate_impl() {
76+
let x: internal::Smaht<(), u32> = internal::Smaht(Box::new(()), PhantomData);
77+
let x: internal::Smaht<dyn internal::Foo, u32> = x;
78+
79+
// This picks `<dyn internal::Foo as X<u32>>::foo` via `ObjectCandidate`.
80+
//
81+
// The `TraitCandidate` is not relevant because `X` is not in scope.
82+
let z = x.foo();
83+
84+
// Observe the type of `z` is `u32`
85+
let _seetype: () = z; //~ ERROR mismatched types
86+
//~| expected (), found u32
87+
}
88+
89+
fn traitcandidate_impl() {
90+
use internal::X;
91+
92+
let x: internal::Smaht<(), u64> = internal::Smaht(Box::new(()), PhantomData);
93+
let x: internal::Smaht<dyn internal::Foo, u64> = x;
94+
95+
// This picks `<dyn internal::Foo as X<u64>>::foo` via `TraitCandidate`.
96+
//
97+
// The `ObjectCandidate` does not apply, as it only applies to
98+
// `X<u32>` (and not `X<u64>`).
99+
let z = x.foo();
100+
101+
// Observe the type of `z` is `u64`
102+
let _seetype: () = z; //~ ERROR mismatched types
103+
//~| expected (), found u64
104+
}
105+
106+
fn traitcandidate_impl_with_nuisance() {
107+
use internal::X;
108+
use nuisance_foo::NuisanceFoo;
109+
110+
let x: internal::Smaht<(), u64> = internal::Smaht(Box::new(()), PhantomData);
111+
let x: internal::Smaht<dyn internal::Foo, u64> = x;
112+
113+
// This picks `<dyn internal::Foo as X<u64>>::foo` via `TraitCandidate`.
114+
//
115+
// The `ObjectCandidate` does not apply, as it only applies to
116+
// `X<u32>` (and not `X<u64>`).
117+
//
118+
// The NuisanceFoo impl has the same priority as the `X` impl,
119+
// so we get a conflict.
120+
let z = x.foo(); //~ ERROR multiple applicable items in scope
121+
}
122+
123+
124+
fn neither_impl() {
125+
let x: internal::Smaht<(), u64> = internal::Smaht(Box::new(()), PhantomData);
126+
let x: internal::Smaht<dyn internal::Foo, u64> = x;
127+
128+
// This can't pick the `TraitCandidate` impl, because `Foo` is not
129+
// imported. However, this also can't pick the `ObjectCandidate`
130+
// impl, because it only applies to `X<u32>` (and not `X<u64>`).
131+
//
132+
// Therefore, neither of the candidates is applicable, and we pick
133+
// the `FinalFoo` impl after another deref, which will return `u8`.
134+
let z = x.foo();
135+
136+
// Observe the type of `z` is `u8`
137+
let _seetype: () = z; //~ ERROR mismatched types
138+
//~| expected (), found u8
139+
}
140+
141+
fn both_impls() {
142+
use internal::X;
143+
144+
let x: internal::Smaht<(), u32> = internal::Smaht(Box::new(()), PhantomData);
145+
let x: internal::Smaht<dyn internal::Foo, u32> = x;
146+
147+
// This can pick both the `TraitCandidate` and the `ObjectCandidate` impl.
148+
//
149+
// However, the `ObjectCandidate` is considered an "inherent candidate",
150+
// and therefore has priority over both the `TraitCandidate` as well as
151+
// any other "nuisance" candidate" (if present).
152+
let z = x.foo();
153+
154+
// Observe the type of `z` is `u32`
155+
let _seetype: () = z; //~ ERROR mismatched types
156+
//~| expected (), found u32
157+
}
158+
159+
160+
fn both_impls_with_nuisance() {
161+
// Similar to the `both_impls` example, except with a nuisance impl to
162+
// make sure the `ObjectCandidate` indeed has a higher priority.
163+
164+
use internal::X;
165+
use nuisance_foo::NuisanceFoo;
166+
167+
let x: internal::Smaht<(), u32> = internal::Smaht(Box::new(()), PhantomData);
168+
let x: internal::Smaht<dyn internal::Foo, u32> = x;
169+
let z = x.foo();
170+
171+
// Observe the type of `z` is `u32`
172+
let _seetype: () = z; //~ ERROR mismatched types
173+
//~| expected (), found u32
174+
}
175+
176+
fn main() {
177+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:85:24
3+
|
4+
LL | let _seetype: () = z; //~ ERROR mismatched types
5+
| ^ expected (), found u32
6+
|
7+
= note: expected type `()`
8+
found type `u32`
9+
10+
error[E0308]: mismatched types
11+
--> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:102:24
12+
|
13+
LL | let _seetype: () = z; //~ ERROR mismatched types
14+
| ^ expected (), found u64
15+
|
16+
= note: expected type `()`
17+
found type `u64`
18+
19+
error[E0034]: multiple applicable items in scope
20+
--> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:120:15
21+
|
22+
LL | let z = x.foo(); //~ ERROR multiple applicable items in scope
23+
| ^^^ multiple `foo` found
24+
|
25+
note: candidate #1 is defined in an impl of the trait `internal::X` for the type `_`
26+
--> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:43:9
27+
|
28+
LL | fn foo(self: Smaht<Self, u64>) -> u64 {
29+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
30+
note: candidate #2 is defined in an impl of the trait `nuisance_foo::NuisanceFoo` for the type `_`
31+
--> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:70:9
32+
|
33+
LL | fn foo(self) {}
34+
| ^^^^^^^^^^^^
35+
note: candidate #3 is defined in the trait `FinalFoo`
36+
--> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:57:5
37+
|
38+
LL | fn foo(&self) -> u8;
39+
| ^^^^^^^^^^^^^^^^^^^^
40+
= help: to disambiguate the method call, write `FinalFoo::foo(x)` instead
41+
42+
error[E0308]: mismatched types
43+
--> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:137:24
44+
|
45+
LL | let _seetype: () = z; //~ ERROR mismatched types
46+
| ^ expected (), found u8
47+
|
48+
= note: expected type `()`
49+
found type `u8`
50+
51+
error[E0308]: mismatched types
52+
--> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:155:24
53+
|
54+
LL | let _seetype: () = z; //~ ERROR mismatched types
55+
| ^ expected (), found u32
56+
|
57+
= note: expected type `()`
58+
found type `u32`
59+
60+
error[E0308]: mismatched types
61+
--> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:172:24
62+
|
63+
LL | let _seetype: () = z; //~ ERROR mismatched types
64+
| ^ expected (), found u32
65+
|
66+
= note: expected type `()`
67+
found type `u32`
68+
69+
error: aborting due to 6 previous errors
70+
71+
Some errors occurred: E0034, E0308.
72+
For more information about an error, try `rustc --explain E0034`.

0 commit comments

Comments
 (0)