Skip to content

Commit

Permalink
Fix issue #34792
Browse files Browse the repository at this point in the history
  • Loading branch information
Michael Rosenberg committed Jul 20, 2016
1 parent bbfcb47 commit 50d8057
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 44 deletions.
89 changes: 45 additions & 44 deletions src/librustc/traits/select.rs
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ enum SelectionCandidate<'tcx> {

/// This is a trait matching with a projected type as `Self`, and
/// we found an applicable bound in the trait definition.
ProjectionCandidate,
ProjectionCandidate(ty::PolyTraitRef<'tcx>),

/// Implementation of a `Fn`-family trait by one of the anonymous types
/// generated for a `||` expression. The ty::ClosureKind informs the
Expand Down Expand Up @@ -238,7 +238,9 @@ impl<'a, 'tcx> ty::Lift<'tcx> for SelectionCandidate<'a> {
DefaultImplObjectCandidate(def_id) => {
DefaultImplObjectCandidate(def_id)
}
ProjectionCandidate => ProjectionCandidate,
ProjectionCandidate(ref bound) => {
return tcx.lift(bound).map(ProjectionCandidate);
}
FnPointerCandidate => FnPointerCandidate,
ObjectCandidate => ObjectCandidate,
BuiltinObjectCandidate => BuiltinObjectCandidate,
Expand Down Expand Up @@ -1170,22 +1172,20 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
debug!("assemble_candidates_for_projected_tys: trait_def_id={:?}",
trait_def_id);

let result = self.probe(|this, snapshot| {
self.in_snapshot(|this, snapshot| {
this.match_projection_obligation_against_bounds_from_trait(obligation,
snapshot)
candidates,
snapshot);
});

if result {
candidates.vec.push(ProjectionCandidate);
}
}

fn match_projection_obligation_against_bounds_from_trait(
&mut self,
obligation: &TraitObligation<'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>,
snapshot: &infer::CombinedSnapshot)
-> bool
{

let poly_trait_predicate =
self.infcx().resolve_type_vars_if_possible(&obligation.predicate);
let (skol_trait_predicate, skol_map) =
Expand Down Expand Up @@ -1215,36 +1215,19 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
bounds={:?}",
bounds);

let matching_bound =
let matching_bounds =
util::elaborate_predicates(self.tcx(), bounds.predicates.into_vec())
.filter_to_traits()
.find(
.filter(
|bound| self.probe(
|this, _| this.match_projection(obligation,
bound.clone(),
skol_trait_predicate.trait_ref.clone(),
&skol_map,
snapshot)));

debug!("match_projection_obligation_against_bounds_from_trait: \
matching_bound={:?}",
matching_bound);
match matching_bound {
None => false,
Some(bound) => {
// Repeat the successful match, if any, this time outside of a probe.
let result = self.match_projection(obligation,
bound,
skol_trait_predicate.trait_ref.clone(),
&skol_map,
snapshot);

self.infcx.pop_skolemized(skol_map, snapshot);
snapshot)))
.map(|bound| ProjectionCandidate(bound));

assert!(result);
true
}
}
candidates.vec.extend(matching_bounds);
}

fn match_projection(&mut self,
Expand Down Expand Up @@ -1684,7 +1667,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {

match other.candidate {
ObjectCandidate |
ParamCandidate(_) | ProjectionCandidate => match victim.candidate {
ParamCandidate(_) | ProjectionCandidate(..) => match victim.candidate {
DefaultImplCandidate(..) => {
bug!(
"default implementations shouldn't be recorded \
Expand All @@ -1701,11 +1684,16 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
// for impls.
true
}
ObjectCandidate |
ProjectionCandidate => {
// Arbitrarily give param candidates priority
// over projection and object candidates.
true
// Arbitrarily give param candidates priority
// over projection and object candidates.
ObjectCandidate => true,
ProjectionCandidate(..) => {
if let ProjectionCandidate(..) = other.candidate {
// Not identical, since equality is checked at the start
false
} else {
true
}
},
ParamCandidate(..) => false,
},
Expand Down Expand Up @@ -2056,8 +2044,8 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
Ok(VtableFnPointer(data))
}

ProjectionCandidate => {
self.confirm_projection_candidate(obligation);
ProjectionCandidate(bound) => {
self.confirm_projection_candidate(obligation, bound);
Ok(VtableParam(Vec::new()))
}

Expand All @@ -2069,14 +2057,27 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
}

fn confirm_projection_candidate(&mut self,
obligation: &TraitObligation<'tcx>)
obligation: &TraitObligation<'tcx>,
matching_bound: ty::PolyTraitRef<'tcx>)
{
debug!("confirm_projection_candidate: matching_bound={:?}", matching_bound);

self.in_snapshot(|this, snapshot| {
let result =
this.match_projection_obligation_against_bounds_from_trait(obligation,
snapshot);
let poly_trait_predicate =
this.infcx().resolve_type_vars_if_possible(&obligation.predicate);
let (skol_trait_predicate, skol_map) =
this.infcx().skolemize_late_bound_regions(&poly_trait_predicate, snapshot);

// Repeat the successful match, if any, this time outside of a probe.
let result = this.match_projection(obligation,
matching_bound,
skol_trait_predicate.trait_ref.clone(),
&skol_map,
snapshot);
this.infcx.pop_skolemized(skol_map, snapshot);

assert!(result);
})
});
}

fn confirm_param_candidate(&mut self,
Expand Down
27 changes: 27 additions & 0 deletions src/test/run-pass/issue-34792.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// Test correct type inference for projections when the associated
// type has multiple trait bounds

struct A;
struct B;

trait Foo {
type T: PartialEq<A> + PartialEq<B>;
}

fn generic<F: Foo>(t: F::T, a: A, b: B) -> bool {
// Equivalent, but not as explicit: t == a && t == b
<<F as Foo>::T as PartialEq<_>>::eq(&t, &a) &&
<<F as Foo>::T as PartialEq<_>>::eq(&t, &b)
}

fn main() {}

0 comments on commit 50d8057

Please sign in to comment.