Auto merge of #29209 - arielb1:exponential-evaluation, r=nmatsakis
This fixes an exponential worst-case and also provides an additional 1% perf improvement. r? @nikomatsakis
This commit is contained in:
commit
e4309c2db6
14 changed files with 527 additions and 296 deletions
|
|
@ -44,6 +44,7 @@ pub use self::object_safety::object_safety_violations;
|
|||
pub use self::object_safety::ObjectSafetyViolation;
|
||||
pub use self::object_safety::MethodViolationCode;
|
||||
pub use self::object_safety::is_vtable_safe_method;
|
||||
pub use self::select::EvaluationCache;
|
||||
pub use self::select::SelectionContext;
|
||||
pub use self::select::SelectionCache;
|
||||
pub use self::select::{MethodMatchResult, MethodMatched, MethodAmbiguous, MethodDidNotMatch};
|
||||
|
|
@ -339,32 +340,53 @@ pub fn type_known_to_meet_builtin_bound<'a,'tcx>(infcx: &InferCtxt<'a,'tcx>,
|
|||
ty,
|
||||
bound);
|
||||
|
||||
let mut fulfill_cx = FulfillmentContext::new(false);
|
||||
|
||||
// We can use a dummy node-id here because we won't pay any mind
|
||||
// to region obligations that arise (there shouldn't really be any
|
||||
// anyhow).
|
||||
let cause = ObligationCause::misc(span, ast::DUMMY_NODE_ID);
|
||||
let obligation =
|
||||
util::predicate_for_builtin_bound(infcx.tcx, cause, bound, 0, ty);
|
||||
let obligation = match obligation {
|
||||
Ok(o) => o,
|
||||
Err(..) => return false
|
||||
};
|
||||
let result = SelectionContext::new(infcx)
|
||||
.evaluate_obligation_conservatively(&obligation);
|
||||
debug!("type_known_to_meet_builtin_bound: ty={:?} bound={:?} => {:?}",
|
||||
ty, bound, result);
|
||||
|
||||
fulfill_cx.register_builtin_bound(infcx, ty, bound, cause);
|
||||
if result && (ty.has_infer_types() || ty.has_closure_types()) {
|
||||
// Because of inference "guessing", selection can sometimes claim
|
||||
// to succeed while the success requires a guess. To ensure
|
||||
// this function's result remains infallible, we must confirm
|
||||
// that guess. While imperfect, I believe this is sound.
|
||||
|
||||
// Note: we only assume something is `Copy` if we can
|
||||
// *definitively* show that it implements `Copy`. Otherwise,
|
||||
// assume it is move; linear is always ok.
|
||||
match fulfill_cx.select_all_or_error(infcx) {
|
||||
Ok(()) => {
|
||||
debug!("type_known_to_meet_builtin_bound: ty={:?} bound={:?} success",
|
||||
ty,
|
||||
bound);
|
||||
true
|
||||
}
|
||||
Err(e) => {
|
||||
debug!("type_known_to_meet_builtin_bound: ty={:?} bound={:?} errors={:?}",
|
||||
ty,
|
||||
bound,
|
||||
e);
|
||||
false
|
||||
let mut fulfill_cx = FulfillmentContext::new(false);
|
||||
|
||||
// We can use a dummy node-id here because we won't pay any mind
|
||||
// to region obligations that arise (there shouldn't really be any
|
||||
// anyhow).
|
||||
let cause = ObligationCause::misc(span, ast::DUMMY_NODE_ID);
|
||||
|
||||
fulfill_cx.register_builtin_bound(infcx, ty, bound, cause);
|
||||
|
||||
// Note: we only assume something is `Copy` if we can
|
||||
// *definitively* show that it implements `Copy`. Otherwise,
|
||||
// assume it is move; linear is always ok.
|
||||
match fulfill_cx.select_all_or_error(infcx) {
|
||||
Ok(()) => {
|
||||
debug!("type_known_to_meet_builtin_bound: ty={:?} bound={:?} success",
|
||||
ty,
|
||||
bound);
|
||||
true
|
||||
}
|
||||
Err(e) => {
|
||||
debug!("type_known_to_meet_builtin_bound: ty={:?} bound={:?} errors={:?}",
|
||||
ty,
|
||||
bound,
|
||||
e);
|
||||
false
|
||||
}
|
||||
}
|
||||
} else {
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@
|
|||
// except according to those terms.
|
||||
|
||||
//! See `README.md` for high-level documentation
|
||||
#![allow(dead_code)] // FIXME -- just temporarily
|
||||
|
||||
pub use self::MethodMatchResult::*;
|
||||
pub use self::MethodMatchedData::*;
|
||||
|
|
@ -190,7 +189,6 @@ pub enum MethodMatchedData {
|
|||
/// parameter environment.
|
||||
#[derive(PartialEq,Eq,Debug,Clone)]
|
||||
enum SelectionCandidate<'tcx> {
|
||||
PhantomFnCandidate,
|
||||
BuiltinCandidate(ty::BuiltinBound),
|
||||
ParamCandidate(ty::PolyTraitRef<'tcx>),
|
||||
ImplCandidate(DefId),
|
||||
|
|
@ -236,11 +234,24 @@ enum BuiltinBoundConditions<'tcx> {
|
|||
AmbiguousBuiltin
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum EvaluationResult<'tcx> {
|
||||
#[derive(Copy, Clone, Debug, PartialOrd, Ord, PartialEq, Eq)]
|
||||
/// The result of trait evaluation. The order is important
|
||||
/// here as the evaluation of a list is the maximum of the
|
||||
/// evaluations.
|
||||
enum EvaluationResult {
|
||||
/// Evaluation successful
|
||||
EvaluatedToOk,
|
||||
/// Evaluation failed because of recursion - treated as ambiguous
|
||||
EvaluatedToUnknown,
|
||||
/// Evaluation is known to be ambiguous
|
||||
EvaluatedToAmbig,
|
||||
EvaluatedToErr(SelectionError<'tcx>),
|
||||
/// Evaluation failed
|
||||
EvaluatedToErr,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct EvaluationCache<'tcx> {
|
||||
hashmap: RefCell<FnvHashMap<ty::PolyTraitRef<'tcx>, EvaluationResult>>
|
||||
}
|
||||
|
||||
impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||
|
|
@ -381,6 +392,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
|||
// The result is "true" if the obligation *may* hold and "false" if
|
||||
// we can be sure it does not.
|
||||
|
||||
|
||||
/// Evaluates whether the obligation `obligation` can be satisfied (by any means).
|
||||
pub fn evaluate_obligation(&mut self,
|
||||
obligation: &PredicateObligation<'tcx>)
|
||||
|
|
@ -389,45 +401,50 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
|||
debug!("evaluate_obligation({:?})",
|
||||
obligation);
|
||||
|
||||
self.evaluate_predicate_recursively(TraitObligationStackList::empty(), obligation)
|
||||
.may_apply()
|
||||
self.infcx.probe(|_| {
|
||||
self.evaluate_predicate_recursively(TraitObligationStackList::empty(), obligation)
|
||||
.may_apply()
|
||||
})
|
||||
}
|
||||
|
||||
fn evaluate_builtin_bound_recursively<'o>(&mut self,
|
||||
bound: ty::BuiltinBound,
|
||||
previous_stack: &TraitObligationStack<'o, 'tcx>,
|
||||
ty: Ty<'tcx>)
|
||||
-> EvaluationResult<'tcx>
|
||||
/// Evaluates whether the obligation `obligation` can be satisfied,
|
||||
/// and returns `false` if not certain. However, this is not entirely
|
||||
/// accurate if inference variables are involved.
|
||||
pub fn evaluate_obligation_conservatively(&mut self,
|
||||
obligation: &PredicateObligation<'tcx>)
|
||||
-> bool
|
||||
{
|
||||
let obligation =
|
||||
util::predicate_for_builtin_bound(
|
||||
self.tcx(),
|
||||
previous_stack.obligation.cause.clone(),
|
||||
bound,
|
||||
previous_stack.obligation.recursion_depth + 1,
|
||||
ty);
|
||||
debug!("evaluate_obligation_conservatively({:?})",
|
||||
obligation);
|
||||
|
||||
match obligation {
|
||||
Ok(obligation) => {
|
||||
self.evaluate_predicate_recursively(previous_stack.list(), &obligation)
|
||||
}
|
||||
Err(ErrorReported) => {
|
||||
EvaluatedToOk
|
||||
}
|
||||
}
|
||||
self.infcx.probe(|_| {
|
||||
self.evaluate_predicate_recursively(TraitObligationStackList::empty(), obligation)
|
||||
== EvaluatedToOk
|
||||
})
|
||||
}
|
||||
|
||||
/// Evaluates the predicates in `predicates` recursively. Note that
|
||||
/// this applies projections in the predicates, and therefore
|
||||
/// is run within an inference probe.
|
||||
fn evaluate_predicates_recursively<'a,'o,I>(&mut self,
|
||||
stack: TraitObligationStackList<'o, 'tcx>,
|
||||
predicates: I)
|
||||
-> EvaluationResult<'tcx>
|
||||
-> EvaluationResult
|
||||
where I : Iterator<Item=&'a PredicateObligation<'tcx>>, 'tcx:'a
|
||||
{
|
||||
let mut result = EvaluatedToOk;
|
||||
for obligation in predicates {
|
||||
match self.evaluate_predicate_recursively(stack, obligation) {
|
||||
EvaluatedToErr(e) => { return EvaluatedToErr(e); }
|
||||
let eval = self.evaluate_predicate_recursively(stack, obligation);
|
||||
debug!("evaluate_predicate_recursively({:?}) = {:?}",
|
||||
obligation, eval);
|
||||
match eval {
|
||||
EvaluatedToErr => { return EvaluatedToErr; }
|
||||
EvaluatedToAmbig => { result = EvaluatedToAmbig; }
|
||||
EvaluatedToUnknown => {
|
||||
if result < EvaluatedToUnknown {
|
||||
result = EvaluatedToUnknown;
|
||||
}
|
||||
}
|
||||
EvaluatedToOk => { }
|
||||
}
|
||||
}
|
||||
|
|
@ -437,7 +454,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
|||
fn evaluate_predicate_recursively<'o>(&mut self,
|
||||
previous_stack: TraitObligationStackList<'o, 'tcx>,
|
||||
obligation: &PredicateObligation<'tcx>)
|
||||
-> EvaluationResult<'tcx>
|
||||
-> EvaluationResult
|
||||
{
|
||||
debug!("evaluate_predicate_recursively({:?})",
|
||||
obligation);
|
||||
|
|
@ -459,12 +476,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
|||
}
|
||||
|
||||
ty::Predicate::Equate(ref p) => {
|
||||
let result = self.infcx.probe(|_| {
|
||||
self.infcx.equality_predicate(obligation.cause.span, p)
|
||||
});
|
||||
match result {
|
||||
// does this code ever run?
|
||||
match self.infcx.equality_predicate(obligation.cause.span, p) {
|
||||
Ok(()) => EvaluatedToOk,
|
||||
Err(_) => EvaluatedToErr(Unimplemented),
|
||||
Err(_) => EvaluatedToErr
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -489,26 +504,24 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
|||
if object_safety::is_object_safe(self.tcx(), trait_def_id) {
|
||||
EvaluatedToOk
|
||||
} else {
|
||||
EvaluatedToErr(Unimplemented)
|
||||
EvaluatedToErr
|
||||
}
|
||||
}
|
||||
|
||||
ty::Predicate::Projection(ref data) => {
|
||||
self.infcx.probe(|_| {
|
||||
let project_obligation = obligation.with(data.clone());
|
||||
match project::poly_project_and_unify_type(self, &project_obligation) {
|
||||
Ok(Some(subobligations)) => {
|
||||
self.evaluate_predicates_recursively(previous_stack,
|
||||
subobligations.iter())
|
||||
}
|
||||
Ok(None) => {
|
||||
EvaluatedToAmbig
|
||||
}
|
||||
Err(_) => {
|
||||
EvaluatedToErr(Unimplemented)
|
||||
}
|
||||
let project_obligation = obligation.with(data.clone());
|
||||
match project::poly_project_and_unify_type(self, &project_obligation) {
|
||||
Ok(Some(subobligations)) => {
|
||||
self.evaluate_predicates_recursively(previous_stack,
|
||||
subobligations.iter())
|
||||
}
|
||||
})
|
||||
Ok(None) => {
|
||||
EvaluatedToAmbig
|
||||
}
|
||||
Err(_) => {
|
||||
EvaluatedToErr
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -516,22 +529,33 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
|||
fn evaluate_obligation_recursively<'o>(&mut self,
|
||||
previous_stack: TraitObligationStackList<'o, 'tcx>,
|
||||
obligation: &TraitObligation<'tcx>)
|
||||
-> EvaluationResult<'tcx>
|
||||
-> EvaluationResult
|
||||
{
|
||||
debug!("evaluate_obligation_recursively({:?})",
|
||||
obligation);
|
||||
|
||||
let stack = self.push_stack(previous_stack, obligation);
|
||||
let fresh_trait_ref = stack.fresh_trait_ref;
|
||||
if let Some(result) = self.check_evaluation_cache(fresh_trait_ref) {
|
||||
debug!("CACHE HIT: EVAL({:?})={:?}",
|
||||
fresh_trait_ref,
|
||||
result);
|
||||
return result;
|
||||
}
|
||||
|
||||
let result = self.evaluate_stack(&stack);
|
||||
|
||||
debug!("result: {:?}", result);
|
||||
debug!("CACHE MISS: EVAL({:?})={:?}",
|
||||
fresh_trait_ref,
|
||||
result);
|
||||
self.insert_evaluation_cache(fresh_trait_ref, result);
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn evaluate_stack<'o>(&mut self,
|
||||
stack: &TraitObligationStack<'o, 'tcx>)
|
||||
-> EvaluationResult<'tcx>
|
||||
-> EvaluationResult
|
||||
{
|
||||
// In intercrate mode, whenever any of the types are unbound,
|
||||
// there can always be an impl. Even if there are no impls in
|
||||
|
|
@ -559,17 +583,20 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
|||
// precise still.
|
||||
let input_types = stack.fresh_trait_ref.0.input_types();
|
||||
let unbound_input_types = input_types.iter().any(|ty| ty.is_fresh());
|
||||
if
|
||||
unbound_input_types &&
|
||||
(self.intercrate ||
|
||||
stack.iter().skip(1).any(
|
||||
|prev| self.match_fresh_trait_refs(&stack.fresh_trait_ref,
|
||||
&prev.fresh_trait_ref)))
|
||||
{
|
||||
debug!("evaluate_stack({:?}) --> unbound argument, recursion --> ambiguous",
|
||||
if unbound_input_types && self.intercrate {
|
||||
debug!("evaluate_stack({:?}) --> unbound argument, intercrate --> ambiguous",
|
||||
stack.fresh_trait_ref);
|
||||
return EvaluatedToAmbig;
|
||||
}
|
||||
if unbound_input_types &&
|
||||
stack.iter().skip(1).any(
|
||||
|prev| self.match_fresh_trait_refs(&stack.fresh_trait_ref,
|
||||
&prev.fresh_trait_ref))
|
||||
{
|
||||
debug!("evaluate_stack({:?}) --> unbound argument, recursive --> giving up",
|
||||
stack.fresh_trait_ref);
|
||||
return EvaluatedToUnknown;
|
||||
}
|
||||
|
||||
// If there is any previous entry on the stack that precisely
|
||||
// matches this obligation, then we can assume that the
|
||||
|
|
@ -601,40 +628,74 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
|||
}
|
||||
|
||||
match self.candidate_from_obligation(stack) {
|
||||
Ok(Some(c)) => self.winnow_candidate(stack, &c),
|
||||
Ok(Some(c)) => self.evaluate_candidate(stack, &c),
|
||||
Ok(None) => EvaluatedToAmbig,
|
||||
Err(e) => EvaluatedToErr(e),
|
||||
Err(..) => EvaluatedToErr
|
||||
}
|
||||
}
|
||||
|
||||
/// Evaluates whether the impl with id `impl_def_id` could be applied to the self type
|
||||
/// `obligation_self_ty`. This can be used either for trait or inherent impls.
|
||||
pub fn evaluate_impl(&mut self,
|
||||
impl_def_id: DefId,
|
||||
obligation: &TraitObligation<'tcx>)
|
||||
-> bool
|
||||
/// Further evaluate `candidate` to decide whether all type parameters match and whether nested
|
||||
/// obligations are met. Returns true if `candidate` remains viable after this further
|
||||
/// scrutiny.
|
||||
fn evaluate_candidate<'o>(&mut self,
|
||||
stack: &TraitObligationStack<'o, 'tcx>,
|
||||
candidate: &SelectionCandidate<'tcx>)
|
||||
-> EvaluationResult
|
||||
{
|
||||
debug!("evaluate_impl(impl_def_id={:?}, obligation={:?})",
|
||||
impl_def_id,
|
||||
obligation);
|
||||
|
||||
self.infcx.probe(|snapshot| {
|
||||
match self.match_impl(impl_def_id, obligation, snapshot) {
|
||||
Ok((substs, skol_map)) => {
|
||||
let vtable_impl = self.vtable_impl(impl_def_id,
|
||||
substs,
|
||||
obligation.cause.clone(),
|
||||
obligation.recursion_depth + 1,
|
||||
skol_map,
|
||||
snapshot);
|
||||
self.winnow_selection(TraitObligationStackList::empty(),
|
||||
VtableImpl(vtable_impl)).may_apply()
|
||||
}
|
||||
Err(()) => {
|
||||
false
|
||||
debug!("evaluate_candidate: depth={} candidate={:?}",
|
||||
stack.obligation.recursion_depth, candidate);
|
||||
let result = self.infcx.probe(|_| {
|
||||
let candidate = (*candidate).clone();
|
||||
match self.confirm_candidate(stack.obligation, candidate) {
|
||||
Ok(selection) => {
|
||||
self.evaluate_predicates_recursively(
|
||||
stack.list(),
|
||||
selection.nested_obligations().iter())
|
||||
}
|
||||
Err(..) => EvaluatedToErr
|
||||
}
|
||||
})
|
||||
});
|
||||
debug!("evaluate_candidate: depth={} result={:?}",
|
||||
stack.obligation.recursion_depth, result);
|
||||
result
|
||||
}
|
||||
|
||||
fn pick_evaluation_cache(&self) -> &EvaluationCache<'tcx> {
|
||||
// see comment in `pick_candidate_cache`
|
||||
if self.intercrate ||
|
||||
!self.param_env().caller_bounds.is_empty()
|
||||
{
|
||||
&self.param_env().evaluation_cache
|
||||
} else
|
||||
{
|
||||
&self.tcx().evaluation_cache
|
||||
}
|
||||
}
|
||||
|
||||
fn check_evaluation_cache(&self, trait_ref: ty::PolyTraitRef<'tcx>)
|
||||
-> Option<EvaluationResult>
|
||||
{
|
||||
let cache = self.pick_evaluation_cache();
|
||||
cache.hashmap.borrow().get(&trait_ref).cloned()
|
||||
}
|
||||
|
||||
fn insert_evaluation_cache(&mut self,
|
||||
trait_ref: ty::PolyTraitRef<'tcx>,
|
||||
result: EvaluationResult)
|
||||
{
|
||||
// Avoid caching results that depend on more than just the trait-ref:
|
||||
// The stack can create EvaluatedToUnknown, and closure signatures
|
||||
// being yet uninferred can create "spurious" EvaluatedToAmbig
|
||||
// and EvaluatedToOk.
|
||||
if result == EvaluatedToUnknown ||
|
||||
((result == EvaluatedToAmbig || result == EvaluatedToOk)
|
||||
&& trait_ref.has_closure_types())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
let cache = self.pick_evaluation_cache();
|
||||
cache.hashmap.borrow_mut().insert(trait_ref, result);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -669,7 +730,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
|||
|
||||
match self.check_candidate_cache(&cache_fresh_trait_pred) {
|
||||
Some(c) => {
|
||||
debug!("CACHE HIT: cache_fresh_trait_pred={:?}, candidate={:?}",
|
||||
debug!("CACHE HIT: SELECT({:?})={:?}",
|
||||
cache_fresh_trait_pred,
|
||||
c);
|
||||
return c;
|
||||
|
|
@ -681,7 +742,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
|||
let candidate = self.candidate_from_obligation_no_cache(stack);
|
||||
|
||||
if self.should_update_candidate_cache(&cache_fresh_trait_pred, &candidate) {
|
||||
debug!("CACHE MISS: cache_fresh_trait_pred={:?}, candidate={:?}",
|
||||
debug!("CACHE MISS: SELECT({:?})={:?}",
|
||||
cache_fresh_trait_pred, candidate);
|
||||
self.insert_candidate_cache(cache_fresh_trait_pred, candidate.clone());
|
||||
}
|
||||
|
|
@ -740,7 +801,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
|||
// Instead, we select the right impl now but report `Bar does
|
||||
// not implement Clone`.
|
||||
if candidates.len() > 1 {
|
||||
candidates.retain(|c| self.winnow_candidate(stack, c).may_apply())
|
||||
candidates.retain(|c| self.evaluate_candidate(stack, c).may_apply())
|
||||
}
|
||||
|
||||
// If there are STILL multiple candidate, we can further reduce
|
||||
|
|
@ -1138,16 +1199,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
|||
fn evaluate_where_clause<'o>(&mut self,
|
||||
stack: &TraitObligationStack<'o, 'tcx>,
|
||||
where_clause_trait_ref: ty::PolyTraitRef<'tcx>)
|
||||
-> EvaluationResult<'tcx>
|
||||
-> EvaluationResult
|
||||
{
|
||||
self.infcx().probe(move |_| {
|
||||
match self.match_where_clause_trait_ref(stack.obligation, where_clause_trait_ref) {
|
||||
Ok(obligations) => {
|
||||
self.evaluate_predicates_recursively(stack.list(), obligations.iter())
|
||||
}
|
||||
Err(()) => {
|
||||
EvaluatedToErr(Unimplemented)
|
||||
}
|
||||
Err(()) => EvaluatedToErr
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -1486,37 +1545,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
|||
// attempt to evaluate recursive bounds to see if they are
|
||||
// satisfied.
|
||||
|
||||
/// Further evaluate `candidate` to decide whether all type parameters match and whether nested
|
||||
/// obligations are met. Returns true if `candidate` remains viable after this further
|
||||
/// scrutiny.
|
||||
fn winnow_candidate<'o>(&mut self,
|
||||
stack: &TraitObligationStack<'o, 'tcx>,
|
||||
candidate: &SelectionCandidate<'tcx>)
|
||||
-> EvaluationResult<'tcx>
|
||||
{
|
||||
debug!("winnow_candidate: candidate={:?}", candidate);
|
||||
let result = self.infcx.probe(|_| {
|
||||
let candidate = (*candidate).clone();
|
||||
match self.confirm_candidate(stack.obligation, candidate) {
|
||||
Ok(selection) => self.winnow_selection(stack.list(),
|
||||
selection),
|
||||
Err(error) => EvaluatedToErr(error),
|
||||
}
|
||||
});
|
||||
debug!("winnow_candidate depth={} result={:?}",
|
||||
stack.obligation.recursion_depth, result);
|
||||
result
|
||||
}
|
||||
|
||||
fn winnow_selection<'o>(&mut self,
|
||||
stack: TraitObligationStackList<'o,'tcx>,
|
||||
selection: Selection<'tcx>)
|
||||
-> EvaluationResult<'tcx>
|
||||
{
|
||||
self.evaluate_predicates_recursively(stack,
|
||||
selection.nested_obligations().iter())
|
||||
}
|
||||
|
||||
/// Returns true if `candidate_i` should be dropped in favor of
|
||||
/// `candidate_j`. Generally speaking we will drop duplicate
|
||||
/// candidates and prefer where-clause candidates.
|
||||
|
|
@ -1542,9 +1570,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
|||
"default implementations shouldn't be recorded \
|
||||
when there are other valid candidates");
|
||||
}
|
||||
&PhantomFnCandidate => {
|
||||
self.tcx().sess.bug("PhantomFn didn't short-circuit selection");
|
||||
}
|
||||
&ImplCandidate(..) |
|
||||
&ClosureCandidate(..) |
|
||||
&FnPointerCandidate(..) |
|
||||
|
|
@ -1974,7 +1999,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
|||
try!(self.confirm_builtin_candidate(obligation, builtin_bound))))
|
||||
}
|
||||
|
||||
PhantomFnCandidate |
|
||||
ErrorCandidate => {
|
||||
Ok(VtableBuiltin(VtableBuiltinData { nested: vec![] }))
|
||||
}
|
||||
|
|
@ -2260,6 +2284,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
|||
impl_def_id,
|
||||
impl_obligations);
|
||||
|
||||
// Because of RFC447, the impl-trait-ref and obligations
|
||||
// are sufficient to determine the impl substs, without
|
||||
// relying on projections in the impl-trait-ref.
|
||||
//
|
||||
// e.g. `impl<U: Tr, V: Iterator<Item=U>> Foo<<U as Tr>::T> for V`
|
||||
impl_obligations.append(&mut substs.obligations);
|
||||
|
||||
VtableImplData { impl_def_id: impl_def_id,
|
||||
|
|
@ -2744,74 +2773,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Determines whether the self type declared against
|
||||
/// `impl_def_id` matches `obligation_self_ty`. If successful,
|
||||
/// returns the substitutions used to make them match. See
|
||||
/// `match_impl()`. For example, if `impl_def_id` is declared
|
||||
/// as:
|
||||
///
|
||||
/// impl<T:Copy> Foo for Box<T> { ... }
|
||||
///
|
||||
/// and `obligation_self_ty` is `int`, we'd get back an `Err(_)`
|
||||
/// result. But if `obligation_self_ty` were `Box<int>`, we'd get
|
||||
/// back `Ok(T=int)`.
|
||||
fn match_inherent_impl(&mut self,
|
||||
impl_def_id: DefId,
|
||||
obligation_cause: &ObligationCause,
|
||||
obligation_self_ty: Ty<'tcx>)
|
||||
-> Result<Substs<'tcx>,()>
|
||||
{
|
||||
// Create fresh type variables for each type parameter declared
|
||||
// on the impl etc.
|
||||
let impl_substs = util::fresh_type_vars_for_impl(self.infcx,
|
||||
obligation_cause.span,
|
||||
impl_def_id);
|
||||
|
||||
// Find the self type for the impl.
|
||||
let impl_self_ty = self.tcx().lookup_item_type(impl_def_id).ty;
|
||||
let impl_self_ty = impl_self_ty.subst(self.tcx(), &impl_substs);
|
||||
|
||||
debug!("match_impl_self_types(obligation_self_ty={:?}, impl_self_ty={:?})",
|
||||
obligation_self_ty,
|
||||
impl_self_ty);
|
||||
|
||||
match self.match_self_types(obligation_cause,
|
||||
impl_self_ty,
|
||||
obligation_self_ty) {
|
||||
Ok(()) => {
|
||||
debug!("Matched impl_substs={:?}", impl_substs);
|
||||
Ok(impl_substs)
|
||||
}
|
||||
Err(()) => {
|
||||
debug!("NoMatch");
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn match_self_types(&mut self,
|
||||
cause: &ObligationCause,
|
||||
|
||||
// The self type provided by the impl/caller-obligation:
|
||||
provided_self_ty: Ty<'tcx>,
|
||||
|
||||
// The self type the obligation is for:
|
||||
required_self_ty: Ty<'tcx>)
|
||||
-> Result<(),()>
|
||||
{
|
||||
// FIXME(#5781) -- equating the types is stronger than
|
||||
// necessary. Should consider variance of trait w/r/t Self.
|
||||
|
||||
let origin = infer::RelateSelfType(cause.span);
|
||||
match self.infcx.eq_types(false,
|
||||
origin,
|
||||
provided_self_ty,
|
||||
required_self_ty) {
|
||||
Ok(()) => Ok(()),
|
||||
Err(_) => Err(()),
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Miscellany
|
||||
|
||||
|
|
@ -2892,17 +2853,37 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
|||
-> Vec<PredicateObligation<'tcx>>
|
||||
{
|
||||
debug!("impl_or_trait_obligations(def_id={:?})", def_id);
|
||||
let tcx = self.tcx();
|
||||
|
||||
let predicates = self.tcx().lookup_predicates(def_id);
|
||||
let predicates = predicates.instantiate(self.tcx(), substs);
|
||||
let predicates = normalize_with_depth(self, cause.clone(), recursion_depth, &predicates);
|
||||
let mut predicates = self.infcx().plug_leaks(skol_map, snapshot, &predicates);
|
||||
let mut obligations =
|
||||
util::predicates_for_generics(cause,
|
||||
recursion_depth,
|
||||
&predicates.value);
|
||||
obligations.append(&mut predicates.obligations);
|
||||
obligations
|
||||
// To allow for one-pass evaluation of the nested obligation,
|
||||
// each predicate must be preceded by the obligations required
|
||||
// to normalize it.
|
||||
// for example, if we have:
|
||||
// impl<U: Iterator, V: Iterator<Item=U>> Foo for V where U::Item: Copy
|
||||
// the impl will have the following predicates:
|
||||
// <V as Iterator>::Item = U,
|
||||
// U: Iterator, U: Sized,
|
||||
// V: Iterator, V: Sized,
|
||||
// <U as Iterator>::Item: Copy
|
||||
// When we substitute, say, `V => IntoIter<u32>, U => $0`, the last
|
||||
// obligation will normalize to `<$0 as Iterator>::Item = $1` and
|
||||
// `$1: Copy`, so we must ensure the obligations are emitted in
|
||||
// that order.
|
||||
let predicates = tcx
|
||||
.lookup_predicates(def_id)
|
||||
.predicates.iter()
|
||||
.flat_map(|predicate| {
|
||||
let predicate =
|
||||
normalize_with_depth(self, cause.clone(), recursion_depth,
|
||||
&predicate.subst(tcx, substs));
|
||||
predicate.obligations.into_iter().chain(
|
||||
Some(Obligation {
|
||||
cause: cause.clone(),
|
||||
recursion_depth: recursion_depth,
|
||||
predicate: predicate.value
|
||||
}))
|
||||
}).collect();
|
||||
self.infcx().plug_leaks(skol_map, snapshot, &predicates)
|
||||
}
|
||||
|
||||
#[allow(unused_comparisons)]
|
||||
|
|
@ -2956,6 +2937,14 @@ impl<'tcx> SelectionCache<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'tcx> EvaluationCache<'tcx> {
|
||||
pub fn new() -> EvaluationCache<'tcx> {
|
||||
EvaluationCache {
|
||||
hashmap: RefCell::new(FnvHashMap())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'o,'tcx> TraitObligationStack<'o,'tcx> {
|
||||
fn list(&'o self) -> TraitObligationStackList<'o,'tcx> {
|
||||
TraitObligationStackList::with(self)
|
||||
|
|
@ -3001,17 +2990,14 @@ impl<'o,'tcx> fmt::Debug for TraitObligationStack<'o,'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'tcx> EvaluationResult<'tcx> {
|
||||
impl EvaluationResult {
|
||||
fn may_apply(&self) -> bool {
|
||||
match *self {
|
||||
EvaluatedToOk |
|
||||
EvaluatedToAmbig |
|
||||
EvaluatedToErr(OutputTypeParameterMismatch(..)) |
|
||||
EvaluatedToErr(TraitNotObjectSafe(_)) =>
|
||||
true,
|
||||
EvaluatedToUnknown => true,
|
||||
|
||||
EvaluatedToErr(Unimplemented) =>
|
||||
false,
|
||||
EvaluatedToErr => false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -332,6 +332,11 @@ pub struct ctxt<'tcx> {
|
|||
/// for things that do not have to do with the parameters in scope.
|
||||
pub selection_cache: traits::SelectionCache<'tcx>,
|
||||
|
||||
/// Caches the results of trait evaluation. This cache is used
|
||||
/// for things that do not have to do with the parameters in scope.
|
||||
/// Merge this with `selection_cache`?
|
||||
pub evaluation_cache: traits::EvaluationCache<'tcx>,
|
||||
|
||||
/// A set of predicates that have been fulfilled *somewhere*.
|
||||
/// This is used to avoid duplicate work. Predicates are only
|
||||
/// added to this set when they mention only "global" names
|
||||
|
|
@ -512,6 +517,7 @@ impl<'tcx> ctxt<'tcx> {
|
|||
transmute_restrictions: RefCell::new(Vec::new()),
|
||||
stability: RefCell::new(stability),
|
||||
selection_cache: traits::SelectionCache::new(),
|
||||
evaluation_cache: traits::EvaluationCache::new(),
|
||||
repr_hint_cache: RefCell::new(DefIdMap()),
|
||||
const_qualif_map: RefCell::new(NodeMap()),
|
||||
custom_coerce_unsized_kinds: RefCell::new(DefIdMap()),
|
||||
|
|
|
|||
|
|
@ -1091,6 +1091,9 @@ pub struct ParameterEnvironment<'a, 'tcx:'a> {
|
|||
/// for things that have to do with the parameters in scope.
|
||||
pub selection_cache: traits::SelectionCache<'tcx>,
|
||||
|
||||
/// Caches the results of trait evaluation.
|
||||
pub evaluation_cache: traits::EvaluationCache<'tcx>,
|
||||
|
||||
/// Scope that is attached to free regions for this scope. This
|
||||
/// is usually the id of the fn body, but for more abstract scopes
|
||||
/// like structs we often use the node-id of the struct.
|
||||
|
|
@ -1112,6 +1115,7 @@ impl<'a, 'tcx> ParameterEnvironment<'a, 'tcx> {
|
|||
implicit_region_bound: self.implicit_region_bound,
|
||||
caller_bounds: caller_bounds,
|
||||
selection_cache: traits::SelectionCache::new(),
|
||||
evaluation_cache: traits::EvaluationCache::new(),
|
||||
free_id: self.free_id,
|
||||
}
|
||||
}
|
||||
|
|
@ -2584,6 +2588,7 @@ impl<'tcx> ctxt<'tcx> {
|
|||
caller_bounds: Vec::new(),
|
||||
implicit_region_bound: ty::ReEmpty,
|
||||
selection_cache: traits::SelectionCache::new(),
|
||||
evaluation_cache: traits::EvaluationCache::new(),
|
||||
|
||||
// for an empty parameter
|
||||
// environment, there ARE no free
|
||||
|
|
@ -2673,6 +2678,7 @@ impl<'tcx> ctxt<'tcx> {
|
|||
implicit_region_bound: ty::ReScope(free_id_outlive),
|
||||
caller_bounds: predicates,
|
||||
selection_cache: traits::SelectionCache::new(),
|
||||
evaluation_cache: traits::EvaluationCache::new(),
|
||||
free_id: free_id,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -822,6 +822,7 @@ impl<'a, 'tcx> TypeFoldable<'tcx> for ty::ParameterEnvironment<'a, 'tcx> where '
|
|||
implicit_region_bound: self.implicit_region_bound.fold_with(folder),
|
||||
caller_bounds: self.caller_bounds.fold_with(folder),
|
||||
selection_cache: traits::SelectionCache::new(),
|
||||
evaluation_cache: traits::EvaluationCache::new(),
|
||||
free_id: self.free_id,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -775,31 +775,33 @@ fn convert_item(ccx: &CrateCtxt, it: &hir::Item) {
|
|||
ref impl_items) => {
|
||||
// Create generics from the generics specified in the impl head.
|
||||
debug!("convert: ast_generics={:?}", generics);
|
||||
let def_id = ccx.tcx.map.local_def_id(it.id);
|
||||
let ty_generics = ty_generics_for_type_or_impl(ccx, generics);
|
||||
let ty_predicates = ty_generic_predicates_for_type_or_impl(ccx, generics);
|
||||
let mut ty_predicates = ty_generic_predicates_for_type_or_impl(ccx, generics);
|
||||
|
||||
debug!("convert: impl_bounds={:?}", ty_predicates);
|
||||
|
||||
let selfty = ccx.icx(&ty_predicates).to_ty(&ExplicitRscope, &**selfty);
|
||||
write_ty_to_tcx(tcx, it.id, selfty);
|
||||
|
||||
tcx.register_item_type(ccx.tcx.map.local_def_id(it.id),
|
||||
tcx.register_item_type(def_id,
|
||||
TypeScheme { generics: ty_generics.clone(),
|
||||
ty: selfty });
|
||||
tcx.predicates.borrow_mut().insert(ccx.tcx.map.local_def_id(it.id),
|
||||
ty_predicates.clone());
|
||||
if let &Some(ref ast_trait_ref) = opt_trait_ref {
|
||||
tcx.impl_trait_refs.borrow_mut().insert(
|
||||
ccx.tcx.map.local_def_id(it.id),
|
||||
def_id,
|
||||
Some(astconv::instantiate_mono_trait_ref(&ccx.icx(&ty_predicates),
|
||||
&ExplicitRscope,
|
||||
ast_trait_ref,
|
||||
Some(selfty)))
|
||||
);
|
||||
} else {
|
||||
tcx.impl_trait_refs.borrow_mut().insert(ccx.tcx.map.local_def_id(it.id), None);
|
||||
tcx.impl_trait_refs.borrow_mut().insert(def_id, None);
|
||||
}
|
||||
|
||||
enforce_impl_params_are_constrained(tcx, generics, &mut ty_predicates, def_id);
|
||||
tcx.predicates.borrow_mut().insert(def_id, ty_predicates.clone());
|
||||
|
||||
|
||||
// If there is a trait reference, treat the methods as always public.
|
||||
// This is to work around some incorrect behavior in privacy checking:
|
||||
|
|
@ -844,7 +846,7 @@ fn convert_item(ccx: &CrateCtxt, it: &hir::Item) {
|
|||
generics: ty_generics.clone(),
|
||||
ty: ty,
|
||||
});
|
||||
convert_associated_const(ccx, ImplContainer(ccx.tcx.map.local_def_id(it.id)),
|
||||
convert_associated_const(ccx, ImplContainer(def_id),
|
||||
impl_item.name, impl_item.id,
|
||||
impl_item.vis.inherit_from(parent_visibility),
|
||||
ty, true /* has_value */);
|
||||
|
|
@ -861,7 +863,7 @@ fn convert_item(ccx: &CrateCtxt, it: &hir::Item) {
|
|||
|
||||
let typ = ccx.icx(&ty_predicates).to_ty(&ExplicitRscope, ty);
|
||||
|
||||
convert_associated_type(ccx, ImplContainer(ccx.tcx.map.local_def_id(it.id)),
|
||||
convert_associated_type(ccx, ImplContainer(def_id),
|
||||
impl_item.name, impl_item.id, impl_item.vis,
|
||||
Some(typ));
|
||||
}
|
||||
|
|
@ -880,7 +882,7 @@ fn convert_item(ccx: &CrateCtxt, it: &hir::Item) {
|
|||
}
|
||||
});
|
||||
convert_methods(ccx,
|
||||
ImplContainer(ccx.tcx.map.local_def_id(it.id)),
|
||||
ImplContainer(def_id),
|
||||
methods,
|
||||
selfty,
|
||||
&ty_generics,
|
||||
|
|
@ -898,10 +900,7 @@ fn convert_item(ccx: &CrateCtxt, it: &hir::Item) {
|
|||
}
|
||||
}
|
||||
|
||||
enforce_impl_params_are_constrained(tcx,
|
||||
generics,
|
||||
ccx.tcx.map.local_def_id(it.id),
|
||||
impl_items);
|
||||
enforce_impl_lifetimes_are_constrained(tcx, generics, def_id, impl_items);
|
||||
},
|
||||
hir::ItemTrait(_, _, _, ref trait_items) => {
|
||||
let trait_def = trait_def_of_item(ccx, it);
|
||||
|
|
@ -2377,13 +2376,15 @@ fn check_method_self_type<'a, 'tcx, RS:RegionScope>(
|
|||
/// Checks that all the type parameters on an impl
|
||||
fn enforce_impl_params_are_constrained<'tcx>(tcx: &ty::ctxt<'tcx>,
|
||||
ast_generics: &hir::Generics,
|
||||
impl_def_id: DefId,
|
||||
impl_items: &[P<hir::ImplItem>])
|
||||
impl_predicates: &mut ty::GenericPredicates<'tcx>,
|
||||
impl_def_id: DefId)
|
||||
{
|
||||
let impl_scheme = tcx.lookup_item_type(impl_def_id);
|
||||
let impl_predicates = tcx.lookup_predicates(impl_def_id);
|
||||
let impl_trait_ref = tcx.impl_trait_ref(impl_def_id);
|
||||
|
||||
assert!(impl_predicates.predicates.is_empty_in(FnSpace));
|
||||
assert!(impl_predicates.predicates.is_empty_in(SelfSpace));
|
||||
|
||||
// The trait reference is an input, so find all type parameters
|
||||
// reachable from there, to start (if this is an inherent impl,
|
||||
// then just examine the self type).
|
||||
|
|
@ -2393,10 +2394,10 @@ fn enforce_impl_params_are_constrained<'tcx>(tcx: &ty::ctxt<'tcx>,
|
|||
input_parameters.extend(ctp::parameters_for_trait_ref(trait_ref));
|
||||
}
|
||||
|
||||
ctp::identify_constrained_type_params(tcx,
|
||||
impl_predicates.predicates.as_slice(),
|
||||
impl_trait_ref,
|
||||
&mut input_parameters);
|
||||
ctp::setup_constraining_predicates(tcx,
|
||||
impl_predicates.predicates.get_mut_slice(TypeSpace),
|
||||
impl_trait_ref,
|
||||
&mut input_parameters);
|
||||
|
||||
for (index, ty_param) in ast_generics.ty_params.iter().enumerate() {
|
||||
let param_ty = ty::ParamTy { space: TypeSpace,
|
||||
|
|
@ -2406,8 +2407,25 @@ fn enforce_impl_params_are_constrained<'tcx>(tcx: &ty::ctxt<'tcx>,
|
|||
report_unused_parameter(tcx, ty_param.span, "type", ¶m_ty.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn enforce_impl_lifetimes_are_constrained<'tcx>(tcx: &ty::ctxt<'tcx>,
|
||||
ast_generics: &hir::Generics,
|
||||
impl_def_id: DefId,
|
||||
impl_items: &[P<hir::ImplItem>])
|
||||
{
|
||||
// Every lifetime used in an associated type must be constrained.
|
||||
let impl_scheme = tcx.lookup_item_type(impl_def_id);
|
||||
let impl_predicates = tcx.lookup_predicates(impl_def_id);
|
||||
let impl_trait_ref = tcx.impl_trait_ref(impl_def_id);
|
||||
|
||||
let mut input_parameters: HashSet<_> =
|
||||
ctp::parameters_for_type(impl_scheme.ty).into_iter().collect();
|
||||
if let Some(ref trait_ref) = impl_trait_ref {
|
||||
input_parameters.extend(ctp::parameters_for_trait_ref(trait_ref));
|
||||
}
|
||||
ctp::identify_constrained_type_params(tcx,
|
||||
&impl_predicates.predicates.as_slice(), impl_trait_ref, &mut input_parameters);
|
||||
|
||||
let lifetimes_in_associated_types: HashSet<_> =
|
||||
impl_items.iter()
|
||||
|
|
|
|||
|
|
@ -84,40 +84,92 @@ pub fn identify_constrained_type_params<'tcx>(_tcx: &ty::ctxt<'tcx>,
|
|||
impl_trait_ref: Option<ty::TraitRef<'tcx>>,
|
||||
input_parameters: &mut HashSet<Parameter>)
|
||||
{
|
||||
loop {
|
||||
let num_inputs = input_parameters.len();
|
||||
let mut predicates = predicates.to_owned();
|
||||
setup_constraining_predicates(_tcx, &mut predicates, impl_trait_ref, input_parameters);
|
||||
}
|
||||
|
||||
let poly_projection_predicates = // : iterator over PolyProjectionPredicate
|
||||
predicates.iter()
|
||||
.filter_map(|predicate| {
|
||||
match *predicate {
|
||||
ty::Predicate::Projection(ref data) => Some(data.clone()),
|
||||
_ => None,
|
||||
}
|
||||
});
|
||||
|
||||
for poly_projection in poly_projection_predicates {
|
||||
// Note that we can skip binder here because the impl
|
||||
// trait ref never contains any late-bound regions.
|
||||
let projection = poly_projection.skip_binder();
|
||||
/// Order the predicates in `predicates` such that each parameter is
|
||||
/// constrained before it is used, if that is possible, and add the
|
||||
/// paramaters so constrained to `input_parameters`. For example,
|
||||
/// imagine the following impl:
|
||||
///
|
||||
/// impl<T: Debug, U: Iterator<Item=T>> Trait for U
|
||||
///
|
||||
/// The impl's predicates are collected from left to right. Ignoring
|
||||
/// the implicit `Sized` bounds, these are
|
||||
/// * T: Debug
|
||||
/// * U: Iterator
|
||||
/// * <U as Iterator>::Item = T -- a desugared ProjectionPredicate
|
||||
///
|
||||
/// When we, for example, try to go over the trait-reference
|
||||
/// `IntoIter<u32> as Trait`, we substitute the impl parameters with fresh
|
||||
/// variables and match them with the impl trait-ref, so we know that
|
||||
/// `$U = IntoIter<u32>`.
|
||||
///
|
||||
/// However, in order to process the `$T: Debug` predicate, we must first
|
||||
/// know the value of `$T` - which is only given by processing the
|
||||
/// projection. As we occasionally want to process predicates in a single
|
||||
/// pass, we want the projection to come first. In fact, as projections
|
||||
/// can (acyclically) depend on one another - see RFC447 for details - we
|
||||
/// need to topologically sort them.
|
||||
pub fn setup_constraining_predicates<'tcx>(_tcx: &ty::ctxt<'tcx>,
|
||||
predicates: &mut [ty::Predicate<'tcx>],
|
||||
impl_trait_ref: Option<ty::TraitRef<'tcx>>,
|
||||
input_parameters: &mut HashSet<Parameter>)
|
||||
{
|
||||
// The canonical way of doing the needed topological sort
|
||||
// would be a DFS, but getting the graph and its ownership
|
||||
// right is annoying, so I am using an in-place fixed-point iteration,
|
||||
// which is `O(nt)` where `t` is the depth of type-parameter constraints,
|
||||
// remembering that `t` should be less than 7 in practice.
|
||||
//
|
||||
// Basically, I iterate over all projections and swap every
|
||||
// "ready" projection to the start of the list, such that
|
||||
// all of the projections before `i` are topologically sorted
|
||||
// and constrain all the parameters in `input_parameters`.
|
||||
//
|
||||
// In the example, `input_parameters` starts by containing `U` - which
|
||||
// is constrained by the trait-ref - and so on the first pass we
|
||||
// observe that `<U as Iterator>::Item = T` is a "ready" projection that
|
||||
// constrains `T` and swap it to front. As it is the sole projection,
|
||||
// no more swaps can take place afterwards, with the result being
|
||||
// * <U as Iterator>::Item = T
|
||||
// * T: Debug
|
||||
// * U: Iterator
|
||||
let mut i = 0;
|
||||
let mut changed = true;
|
||||
while changed {
|
||||
changed = false;
|
||||
|
||||
// Special case: watch out for some kind of sneaky attempt
|
||||
// to project out an associated type defined by this very
|
||||
// trait.
|
||||
let unbound_trait_ref = &projection.projection_ty.trait_ref;
|
||||
if Some(unbound_trait_ref.clone()) == impl_trait_ref {
|
||||
for j in i..predicates.len() {
|
||||
|
||||
if let ty::Predicate::Projection(ref poly_projection) = predicates[j] {
|
||||
// Note that we can skip binder here because the impl
|
||||
// trait ref never contains any late-bound regions.
|
||||
let projection = poly_projection.skip_binder();
|
||||
|
||||
// Special case: watch out for some kind of sneaky attempt
|
||||
// to project out an associated type defined by this very
|
||||
// trait.
|
||||
let unbound_trait_ref = &projection.projection_ty.trait_ref;
|
||||
if Some(unbound_trait_ref.clone()) == impl_trait_ref {
|
||||
continue;
|
||||
}
|
||||
|
||||
let inputs = parameters_for_trait_ref(&projection.projection_ty.trait_ref);
|
||||
let relies_only_on_inputs = inputs.iter().all(|p| input_parameters.contains(&p));
|
||||
if !relies_only_on_inputs {
|
||||
continue;
|
||||
}
|
||||
input_parameters.extend(parameters_for_type(projection.ty));
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
let inputs = parameters_for_trait_ref(&projection.projection_ty.trait_ref);
|
||||
let relies_only_on_inputs = inputs.iter().all(|p| input_parameters.contains(&p));
|
||||
if relies_only_on_inputs {
|
||||
input_parameters.extend(parameters_for_type(projection.ty));
|
||||
}
|
||||
}
|
||||
|
||||
if input_parameters.len() == num_inputs {
|
||||
break;
|
||||
// fancy control flow to bypass borrow checker
|
||||
predicates.swap(i, j);
|
||||
i += 1;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ fn main()
|
|||
let f: f32 = 1.2;
|
||||
let v = 0 as *const u8;
|
||||
let fat_v : *const [u8] = unsafe { &*(0 as *const [u8; 1])};
|
||||
let fat_sv : *const [i8] = unsafe { &*(0 as *const [i8; 1])};
|
||||
let foo: &Foo = &f;
|
||||
|
||||
let _ = v as &u8; //~ ERROR non-scalar
|
||||
|
|
@ -94,7 +95,7 @@ fn main()
|
|||
let _ = main as *mut str; //~ ERROR casting
|
||||
let _ = &f as *mut f32; //~ ERROR casting
|
||||
let _ = &f as *const f64; //~ ERROR casting
|
||||
let _ = fat_v as usize;
|
||||
let _ = fat_sv as usize;
|
||||
//~^ ERROR casting
|
||||
//~^^ HELP through a thin pointer first
|
||||
|
||||
|
|
@ -106,7 +107,7 @@ fn main()
|
|||
let _ = main.f as *const u32; //~ ERROR attempted access of field
|
||||
|
||||
let cf: *const Foo = &0;
|
||||
let _ = cf as *const [u8];
|
||||
let _ = cf as *const [u16];
|
||||
//~^ ERROR casting
|
||||
//~^^ NOTE vtable kinds
|
||||
let _ = cf as *const Bar;
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//~^^^^^^^^^^ ERROR overflow
|
||||
//
|
||||
// We get an error message at the top of file (dummy span).
|
||||
// This is not helpful, but also kind of annoying to prevent,
|
||||
|
|
@ -32,6 +31,7 @@ impl<T:Clone> ToOpt for Option<T> {
|
|||
}
|
||||
|
||||
fn function<T:ToOpt + Clone>(counter: usize, t: T) {
|
||||
//~^ ERROR reached the recursion limit during monomorphization
|
||||
if counter > 0 {
|
||||
function(counter - 1, t.to_option());
|
||||
// FIXME(#4287) Error message should be here. It should be
|
||||
|
|
|
|||
32
src/test/compile-fail/issue-29147.rs
Normal file
32
src/test/compile-fail/issue-29147.rs
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
// 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.
|
||||
|
||||
#![recursion_limit="1024"]
|
||||
|
||||
pub struct S0<T>(T,T);
|
||||
pub struct S1<T>(Option<Box<S0<S0<T>>>>,Option<Box<S0<S0<T>>>>);
|
||||
pub struct S2<T>(Option<Box<S1<S1<T>>>>,Option<Box<S1<S1<T>>>>);
|
||||
pub struct S3<T>(Option<Box<S2<S2<T>>>>,Option<Box<S2<S2<T>>>>);
|
||||
pub struct S4<T>(Option<Box<S3<S3<T>>>>,Option<Box<S3<S3<T>>>>);
|
||||
pub struct S5<T>(Option<Box<S4<S4<T>>>>,Option<Box<S4<S4<T>>>>,Option<T>);
|
||||
|
||||
trait Foo { fn xxx(&self); }
|
||||
trait Bar {} // anything local or #[fundamental]
|
||||
|
||||
impl<T> Foo for T where T: Bar, T: Sync {
|
||||
fn xxx(&self) {}
|
||||
}
|
||||
|
||||
impl Foo for S5<u32> { fn xxx(&self) {} }
|
||||
impl Foo for S5<u64> { fn xxx(&self) {} }
|
||||
|
||||
fn main() {
|
||||
let _ = <S5<_>>::xxx; //~ ERROR cannot resolve `S5<_> : Foo`
|
||||
}
|
||||
|
|
@ -8,12 +8,6 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//~^^^^^^^^^^ ERROR overflow
|
||||
//
|
||||
// We get an error message at the top of file (dummy span).
|
||||
// This is not helpful, but also kind of annoying to prevent,
|
||||
// so for now just live with it.
|
||||
|
||||
enum Nil {NilValue}
|
||||
struct Cons<T> {head:isize, tail:T}
|
||||
trait Dot {fn dot(&self, other:Self) -> isize;}
|
||||
|
|
@ -26,7 +20,7 @@ impl<T:Dot> Dot for Cons<T> {
|
|||
}
|
||||
}
|
||||
fn test<T:Dot> (n:isize, i:isize, first:T, second:T) ->isize {
|
||||
match n { 0 => {first.dot(second)}
|
||||
match n { 0 => {first.dot(second)} //~ ERROR overflow
|
||||
// FIXME(#4287) Error message should be here. It should be
|
||||
// a type error to instantiate `test` at a type other than T.
|
||||
_ => {test (n-1, i+1, Cons {head:2*i+1, tail:first}, Cons{head:i*i, tail:second})}
|
||||
|
|
|
|||
31
src/test/run-pass/coherence-rfc447-constrained.rs
Normal file
31
src/test/run-pass/coherence-rfc447-constrained.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.
|
||||
|
||||
// check that trait matching can handle impls whose types are only
|
||||
// constrained by a projection.
|
||||
|
||||
trait IsU32 {}
|
||||
impl IsU32 for u32 {}
|
||||
|
||||
trait Mirror { type Image: ?Sized; }
|
||||
impl<T: ?Sized> Mirror for T { type Image = T; }
|
||||
|
||||
trait Bar {}
|
||||
impl<U: Mirror, V: Mirror<Image=L>, L: Mirror<Image=U>> Bar for V
|
||||
where U::Image: IsU32 {}
|
||||
|
||||
trait Foo { fn name() -> &'static str; }
|
||||
impl Foo for u64 { fn name() -> &'static str { "u64" } }
|
||||
impl<T: Bar> Foo for T { fn name() -> &'static str { "Bar" }}
|
||||
|
||||
fn main() {
|
||||
assert_eq!(<u64 as Foo>::name(), "u64");
|
||||
assert_eq!(<u32 as Foo>::name(), "Bar");
|
||||
}
|
||||
36
src/test/run-pass/issue-29147.rs
Normal file
36
src/test/run-pass/issue-29147.rs
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
// 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.
|
||||
|
||||
#![recursion_limit="1024"]
|
||||
|
||||
use std::mem;
|
||||
|
||||
pub struct S0<T>(T,T);
|
||||
pub struct S1<T>(Option<Box<S0<S0<T>>>>,Option<Box<S0<S0<T>>>>);
|
||||
pub struct S2<T>(Option<Box<S1<S1<T>>>>,Option<Box<S1<S1<T>>>>);
|
||||
pub struct S3<T>(Option<Box<S2<S2<T>>>>,Option<Box<S2<S2<T>>>>);
|
||||
pub struct S4<T>(Option<Box<S3<S3<T>>>>,Option<Box<S3<S3<T>>>>);
|
||||
pub struct S5<T>(Option<Box<S4<S4<T>>>>,Option<Box<S4<S4<T>>>>,Option<T>);
|
||||
|
||||
trait Foo { fn xxx(&self); }
|
||||
/// some local of #[fundamental] trait
|
||||
trait Bar {}
|
||||
|
||||
impl<T> Foo for T where T: Bar, T: Sync {
|
||||
fn xxx(&self) {}
|
||||
}
|
||||
|
||||
impl Foo for S5<u8> { fn xxx(&self) {} }
|
||||
|
||||
fn main() {
|
||||
let s = S5(None,None,None);
|
||||
s.xxx();
|
||||
assert_eq!(mem::size_of_val(&s.2), mem::size_of::<Option<u8>>());
|
||||
}
|
||||
46
src/test/run-pass/trait-copy-guessing.rs
Normal file
46
src/test/run-pass/trait-copy-guessing.rs
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
// 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.
|
||||
|
||||
// "guessing" in trait selection can affect `copy_or_move`. Check that this
|
||||
// is correctly handled. I am not sure what is the "correct" behaviour,
|
||||
// but we should at least not ICE.
|
||||
|
||||
use std::mem;
|
||||
|
||||
struct U([u8; 1337]);
|
||||
|
||||
struct S<'a,T:'a>(&'a T);
|
||||
impl<'a, T> Clone for S<'a, T> { fn clone(&self) -> Self { S(self.0) } }
|
||||
/// This impl triggers inference "guessing" - S<_>: Copy => _ = U
|
||||
impl<'a> Copy for S<'a, Option<U>> {}
|
||||
|
||||
fn assert_impls_fn<R,T: Fn()->R>(_: &T){}
|
||||
|
||||
fn main() {
|
||||
let n = None;
|
||||
let e = S(&n);
|
||||
let f = || {
|
||||
// S being copy is critical for this to work
|
||||
drop(e);
|
||||
mem::size_of_val(e.0)
|
||||
};
|
||||
assert_impls_fn(&f);
|
||||
assert_eq!(f(), 1337+1);
|
||||
|
||||
assert_eq!((|| {
|
||||
// S being Copy is not critical here, but
|
||||
// we check it anyway.
|
||||
let n = None;
|
||||
let e = S(&n);
|
||||
let ret = mem::size_of_val(e.0);
|
||||
drop(e);
|
||||
ret
|
||||
})(), 1337+1);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue