Skip to content

Commit 7276d8b

Browse files
committed
Auto merge of #27258 - nikomatsakis:issue-26952, r=eddyb
Correct regression in type-inference caused by failing to reconfirm that the object trait matches the required trait during trait selection. The existing code was checking that the object trait WOULD match (in a probe), but never executing the match outside of a probe. This corrects various regressions observed in the wild, including issue #26952. Fixes #26952. r? @eddyb cc @frankmcsherry
2 parents e333e6a + 4726bb4 commit 7276d8b

File tree

4 files changed

+80
-31
lines changed

4 files changed

+80
-31
lines changed

src/librustc/middle/infer/mod.rs

+6-4
Original file line numberDiff line numberDiff line change
@@ -698,8 +698,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
698698
}
699699
}
700700

701-
fn rollback_to(&self, snapshot: CombinedSnapshot) {
702-
debug!("rollback!");
701+
fn rollback_to(&self, cause: &str, snapshot: CombinedSnapshot) {
702+
debug!("rollback_to(cause={})", cause);
703703
let CombinedSnapshot { type_snapshot,
704704
int_snapshot,
705705
float_snapshot,
@@ -759,7 +759,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
759759
debug!("commit_if_ok() -- r.is_ok() = {}", r.is_ok());
760760
match r {
761761
Ok(_) => { self.commit_from(snapshot); }
762-
Err(_) => { self.rollback_to(snapshot); }
762+
Err(_) => { self.rollback_to("commit_if_ok -- error", snapshot); }
763763
}
764764
r
765765
}
@@ -778,6 +778,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
778778

779779
let r = self.commit_if_ok(|_| f());
780780

781+
debug!("commit_regions_if_ok: rolling back everything but regions");
782+
781783
// Roll back any non-region bindings - they should be resolved
782784
// inside `f`, with, e.g. `resolve_type_vars_if_possible`.
783785
self.type_variables
@@ -804,7 +806,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
804806
debug!("probe()");
805807
let snapshot = self.start_snapshot();
806808
let r = f(&snapshot);
807-
self.rollback_to(snapshot);
809+
self.rollback_to("probe", snapshot);
808810
r
809811
}
810812

src/librustc/middle/traits/select.rs

+40-26
Original file line numberDiff line numberDiff line change
@@ -1351,11 +1351,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
13511351
// correct trait, but also the correct type parameters.
13521352
// For example, we may be trying to upcast `Foo` to `Bar<i32>`,
13531353
// but `Foo` is declared as `trait Foo : Bar<u32>`.
1354-
let upcast_trait_refs = util::supertraits(self.tcx(), poly_trait_ref)
1355-
.filter(|upcast_trait_ref| self.infcx.probe(|_| {
1356-
let upcast_trait_ref = upcast_trait_ref.clone();
1357-
self.match_poly_trait_ref(obligation, upcast_trait_ref).is_ok()
1358-
})).count();
1354+
let upcast_trait_refs =
1355+
util::supertraits(self.tcx(), poly_trait_ref)
1356+
.filter(|upcast_trait_ref| {
1357+
self.infcx.probe(|_| {
1358+
let upcast_trait_ref = upcast_trait_ref.clone();
1359+
self.match_poly_trait_ref(obligation, upcast_trait_ref).is_ok()
1360+
})
1361+
})
1362+
.count();
13591363

13601364
if upcast_trait_refs > 1 {
13611365
// can be upcast in many ways; need more type information
@@ -1627,9 +1631,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
16271631
let principal =
16281632
data.principal_trait_ref_with_self_ty(self.tcx(),
16291633
self.tcx().types.err);
1630-
let desired_def_id = obligation.predicate.def_id();
1634+
let copy_def_id = obligation.predicate.def_id();
16311635
for tr in util::supertraits(self.tcx(), principal) {
1632-
if tr.def_id() == desired_def_id {
1636+
if tr.def_id() == copy_def_id {
16331637
return ok_if(Vec::new())
16341638
}
16351639
}
@@ -2282,31 +2286,41 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
22822286
}
22832287
};
22842288

2285-
// Upcast the object type to the obligation type. There must
2286-
// be exactly one applicable trait-reference; if this were not
2287-
// the case, we would have reported an ambiguity error rather
2288-
// than successfully selecting one of the candidates.
2289-
let mut upcast_trait_refs = util::supertraits(self.tcx(), poly_trait_ref)
2290-
.map(|upcast_trait_ref| {
2291-
(upcast_trait_ref.clone(), self.infcx.probe(|_| {
2292-
self.match_poly_trait_ref(obligation, upcast_trait_ref)
2293-
}).is_ok())
2294-
});
22952289
let mut upcast_trait_ref = None;
2296-
let mut vtable_base = 0;
2290+
let vtable_base;
2291+
2292+
{
2293+
// We want to find the first supertrait in the list of
2294+
// supertraits that we can unify with, and do that
2295+
// unification. We know that there is exactly one in the list
2296+
// where we can unify because otherwise select would have
2297+
// reported an ambiguity. (When we do find a match, also
2298+
// record it for later.)
2299+
let nonmatching =
2300+
util::supertraits(self.tcx(), poly_trait_ref)
2301+
.take_while(|&t| {
2302+
match
2303+
self.infcx.commit_if_ok(
2304+
|_| self.match_poly_trait_ref(obligation, t))
2305+
{
2306+
Ok(_) => { upcast_trait_ref = Some(t); false }
2307+
Err(_) => { true }
2308+
}
2309+
});
2310+
2311+
// Additionally, for each of the nonmatching predicates that
2312+
// we pass over, we sum up the set of number of vtable
2313+
// entries, so that we can compute the offset for the selected
2314+
// trait.
2315+
vtable_base =
2316+
nonmatching.map(|t| util::count_own_vtable_entries(self.tcx(), t))
2317+
.sum();
22972318

2298-
while let Some((supertrait, matches)) = upcast_trait_refs.next() {
2299-
if matches {
2300-
upcast_trait_ref = Some(supertrait);
2301-
break;
2302-
}
2303-
vtable_base += util::count_own_vtable_entries(self.tcx(), supertrait);
23042319
}
2305-
assert!(upcast_trait_refs.all(|(_, matches)| !matches));
23062320

23072321
VtableObjectData {
23082322
upcast_trait_ref: upcast_trait_ref.unwrap(),
2309-
vtable_base: vtable_base
2323+
vtable_base: vtable_base,
23102324
}
23112325
}
23122326

src/librustc/middle/ty.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1985,7 +1985,7 @@ impl<'tcx> PolyTraitRef<'tcx> {
19851985
/// erase, or otherwise "discharge" these bound regions, we change the
19861986
/// type from `Binder<T>` to just `T` (see
19871987
/// e.g. `liberate_late_bound_regions`).
1988-
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
1988+
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
19891989
pub struct Binder<T>(pub T);
19901990

19911991
impl<T> Binder<T> {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Test that when we match a trait reference like `Foo<A>: Foo<_#0t>`,
12+
// we unify with `_#0t` with `A`. In this code, if we failed to do
13+
// that, then you get an unconstrained type-variable in `call`.
14+
//
15+
// Also serves as a regression test for issue #26952, though the test
16+
// was derived from another reported regression with the same cause.
17+
18+
use std::marker::PhantomData;
19+
20+
trait Trait<A> { fn foo(&self); }
21+
22+
struct Type<A> { a: PhantomData<A> }
23+
24+
fn as_trait<A>(t: &Type<A>) -> &Trait<A> { loop { } }
25+
26+
fn want<A,T:Trait<A>+?Sized>(t: &T) { }
27+
28+
fn call<A>(p: Type<A>) {
29+
let q = as_trait(&p);
30+
want(q); // parameter A to `want` *would* be unconstrained
31+
}
32+
33+
fn main() { }

0 commit comments

Comments
 (0)