distinguish projections from the env/obj-types vs those from
trait definitions, and give prefence to the former. This is consistent with what we do for selection. It also works around a limitation that was leading to #28871.
This commit is contained in:
parent
83cf3ce498
commit
8fa5f09a8f
@ -54,9 +54,19 @@ pub struct MismatchedProjectionTypes<'tcx> {
|
||||
|
||||
#[derive(PartialEq, Eq, Debug)]
|
||||
enum ProjectionTyCandidate<'tcx> {
|
||||
// from a where-clause in the env or object type
|
||||
ParamEnv(ty::PolyProjectionPredicate<'tcx>),
|
||||
|
||||
// from the definition of `Trait` when you have something like <<A as Trait>::B as Trait2>::C
|
||||
TraitDef(ty::PolyProjectionPredicate<'tcx>),
|
||||
|
||||
// defined in an impl
|
||||
Impl(VtableImplData<'tcx, PredicateObligation<'tcx>>),
|
||||
|
||||
// closure return type
|
||||
Closure(VtableClosureData<'tcx, PredicateObligation<'tcx>>),
|
||||
|
||||
// fn pointer return type
|
||||
FnPointer(Ty<'tcx>),
|
||||
}
|
||||
|
||||
@ -491,7 +501,11 @@ fn project_type<'cx,'tcx>(
|
||||
candidates.vec.len(),
|
||||
candidates.ambiguous);
|
||||
|
||||
// We probably need some winnowing logic similar to select here.
|
||||
// Inherent ambiguity that prevents us from even enumerating the
|
||||
// candidates.
|
||||
if candidates.ambiguous {
|
||||
return Err(ProjectionTyError::TooManyCandidates);
|
||||
}
|
||||
|
||||
// Drop duplicates.
|
||||
//
|
||||
@ -512,10 +526,30 @@ fn project_type<'cx,'tcx>(
|
||||
}
|
||||
}
|
||||
|
||||
if candidates.ambiguous || candidates.vec.len() > 1 {
|
||||
return Err(ProjectionTyError::TooManyCandidates);
|
||||
// Prefer where-clauses. As in select, if there are multiple
|
||||
// candidates, we prefer where-clause candidates over impls. This
|
||||
// may seem a bit surprising, since impls are the source of
|
||||
// "truth" in some sense, but in fact some of the impls that SEEM
|
||||
// applicable are not, because of nested obligations. Where
|
||||
// clauses are the safer choice. See the comment on
|
||||
// `select::SelectionCandidate` and #21974 for more details.
|
||||
if candidates.vec.len() > 1 {
|
||||
debug!("retaining param-env candidates only from {:?}", candidates.vec);
|
||||
candidates.vec.retain(|c| match *c {
|
||||
ProjectionTyCandidate::ParamEnv(..) => true,
|
||||
ProjectionTyCandidate::Impl(..) |
|
||||
ProjectionTyCandidate::Closure(..) |
|
||||
ProjectionTyCandidate::TraitDef(..) |
|
||||
ProjectionTyCandidate::FnPointer(..) => false,
|
||||
});
|
||||
debug!("resulting candidate set: {:?}", candidates.vec);
|
||||
if candidates.vec.len() != 1 {
|
||||
return Err(ProjectionTyError::TooManyCandidates);
|
||||
}
|
||||
}
|
||||
|
||||
assert!(candidates.vec.len() <= 1);
|
||||
|
||||
match candidates.vec.pop() {
|
||||
Some(candidate) => {
|
||||
let (ty, obligations) = confirm_candidate(selcx, obligation, candidate);
|
||||
@ -538,9 +572,14 @@ fn assemble_candidates_from_param_env<'cx,'tcx>(
|
||||
obligation_trait_ref: &ty::TraitRef<'tcx>,
|
||||
candidate_set: &mut ProjectionTyCandidateSet<'tcx>)
|
||||
{
|
||||
debug!("assemble_candidates_from_param_env(..)");
|
||||
let env_predicates = selcx.param_env().caller_bounds.iter().cloned();
|
||||
assemble_candidates_from_predicates(selcx, obligation, obligation_trait_ref,
|
||||
candidate_set, env_predicates);
|
||||
assemble_candidates_from_predicates(selcx,
|
||||
obligation,
|
||||
obligation_trait_ref,
|
||||
candidate_set,
|
||||
ProjectionTyCandidate::ParamEnv,
|
||||
env_predicates);
|
||||
}
|
||||
|
||||
/// In the case of a nested projection like <<A as Foo>::FooT as Bar>::BarT, we may find
|
||||
@ -559,6 +598,8 @@ fn assemble_candidates_from_trait_def<'cx,'tcx>(
|
||||
obligation_trait_ref: &ty::TraitRef<'tcx>,
|
||||
candidate_set: &mut ProjectionTyCandidateSet<'tcx>)
|
||||
{
|
||||
debug!("assemble_candidates_from_trait_def(..)");
|
||||
|
||||
// Check whether the self-type is itself a projection.
|
||||
let trait_ref = match obligation_trait_ref.self_ty().sty {
|
||||
ty::TyProjection(ref data) => data.trait_ref.clone(),
|
||||
@ -575,8 +616,12 @@ fn assemble_candidates_from_trait_def<'cx,'tcx>(
|
||||
let trait_predicates = selcx.tcx().lookup_predicates(trait_ref.def_id);
|
||||
let bounds = trait_predicates.instantiate(selcx.tcx(), trait_ref.substs);
|
||||
let bounds = elaborate_predicates(selcx.tcx(), bounds.predicates.into_vec());
|
||||
assemble_candidates_from_predicates(selcx, obligation, obligation_trait_ref,
|
||||
candidate_set, bounds)
|
||||
assemble_candidates_from_predicates(selcx,
|
||||
obligation,
|
||||
obligation_trait_ref,
|
||||
candidate_set,
|
||||
ProjectionTyCandidate::TraitDef,
|
||||
bounds)
|
||||
}
|
||||
|
||||
fn assemble_candidates_from_predicates<'cx,'tcx,I>(
|
||||
@ -584,6 +629,7 @@ fn assemble_candidates_from_predicates<'cx,'tcx,I>(
|
||||
obligation: &ProjectionTyObligation<'tcx>,
|
||||
obligation_trait_ref: &ty::TraitRef<'tcx>,
|
||||
candidate_set: &mut ProjectionTyCandidateSet<'tcx>,
|
||||
ctor: fn(ty::PolyProjectionPredicate<'tcx>) -> ProjectionTyCandidate<'tcx>,
|
||||
env_predicates: I)
|
||||
where I: Iterator<Item=ty::Predicate<'tcx>>
|
||||
{
|
||||
@ -614,8 +660,7 @@ fn assemble_candidates_from_predicates<'cx,'tcx,I>(
|
||||
data, is_match, same_name);
|
||||
|
||||
if is_match {
|
||||
candidate_set.vec.push(
|
||||
ProjectionTyCandidate::ParamEnv(data.clone()));
|
||||
candidate_set.vec.push(ctor(data.clone()));
|
||||
}
|
||||
}
|
||||
_ => { }
|
||||
@ -647,8 +692,12 @@ fn assemble_candidates_from_object_type<'cx,'tcx>(
|
||||
.map(|p| p.to_predicate())
|
||||
.collect();
|
||||
let env_predicates = elaborate_predicates(selcx.tcx(), env_predicates);
|
||||
assemble_candidates_from_predicates(selcx, obligation, obligation_trait_ref,
|
||||
candidate_set, env_predicates)
|
||||
assemble_candidates_from_predicates(selcx,
|
||||
obligation,
|
||||
obligation_trait_ref,
|
||||
candidate_set,
|
||||
ProjectionTyCandidate::ParamEnv,
|
||||
env_predicates)
|
||||
}
|
||||
|
||||
fn assemble_candidates_from_impls<'cx,'tcx>(
|
||||
@ -746,7 +795,8 @@ fn confirm_candidate<'cx,'tcx>(
|
||||
obligation);
|
||||
|
||||
match candidate {
|
||||
ProjectionTyCandidate::ParamEnv(poly_projection) => {
|
||||
ProjectionTyCandidate::ParamEnv(poly_projection) |
|
||||
ProjectionTyCandidate::TraitDef(poly_projection) => {
|
||||
confirm_param_env_candidate(selcx, obligation, poly_projection)
|
||||
}
|
||||
|
||||
|
31
src/test/run-pass/issue-28871.rs
Normal file
31
src/test/run-pass/issue-28871.rs
Normal file
@ -0,0 +1,31 @@
|
||||
// Copyright 2015 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.
|
||||
|
||||
// Regression test for #28871. The problem is that rustc encountered
|
||||
// two ways to project, one from a where clause and one from the where
|
||||
// clauses on the trait definition. (In fact, in this case, the where
|
||||
// clauses originated from the trait definition as well.) The true
|
||||
// cause of the error is that the trait definition where clauses are
|
||||
// not being normalized, and hence the two sources are considered in
|
||||
// conflict, and not a duplicate. Hacky solution is to prefer where
|
||||
// clauses over the data found in the trait definition.
|
||||
|
||||
trait T {
|
||||
type T;
|
||||
}
|
||||
|
||||
struct S;
|
||||
impl T for S {
|
||||
type T = S;
|
||||
}
|
||||
|
||||
trait T2 {
|
||||
type T: Iterator<Item=<S as T>::T>;
|
||||
}
|
Loading…
Reference in New Issue
Block a user