Auto merge of #32780 - soltanmm:consider-the-following, r=nikomatsakis

Replace consider_unification_despite_ambiguity with new obligation variant

Is work towards #32730. Addresses part one of #32286. Addresses #24210 and #26046 to some degree.

r? @nikomatsakis
This commit is contained in:
bors 2016-04-13 11:28:30 -07:00
commit 35dca7fb7b
19 changed files with 163 additions and 75 deletions

View File

@ -1543,4 +1543,5 @@ register_diagnostics! {
E0490, // a value of type `..` is borrowed for too long
E0491, // in type `..`, reference has a longer lifetime than the data it...
E0495, // cannot infer an appropriate lifetime due to conflicting requirements
E0524, // expected a closure that implements `..` but this closure only implements `..`
}

View File

@ -59,6 +59,7 @@ impl FreeRegionMap {
ty::Predicate::Equate(..) |
ty::Predicate::WellFormed(..) |
ty::Predicate::ObjectSafe(..) |
ty::Predicate::ClosureKind(..) |
ty::Predicate::TypeOutlives(..) => {
// No region bounds here
}

View File

@ -495,6 +495,21 @@ pub fn report_selection_error<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
err.emit();
}
ty::Predicate::ClosureKind(closure_def_id, kind) => {
let found_kind = infcx.closure_kind(closure_def_id).unwrap();
let closure_span = infcx.tcx.map.span_if_local(closure_def_id).unwrap();
let mut err = struct_span_err!(
infcx.tcx.sess, closure_span, E0524,
"expected a closure that implements the `{}` trait, but this closure \
only implements `{}`",
kind,
found_kind);
err.span_note(
obligation.cause.span,
&format!("the requirement to implement `{}` derives from here", kind));
err.emit();
}
ty::Predicate::WellFormed(ty) => {
// WF predicates cannot themselves make
// errors. They can only block due to

View File

@ -652,6 +652,21 @@ fn process_predicate1<'a,'tcx>(selcx: &mut SelectionContext<'a,'tcx>,
}
}
ty::Predicate::ClosureKind(closure_def_id, kind) => {
match selcx.infcx().closure_kind(closure_def_id) {
Some(closure_kind) => {
if closure_kind.extends(kind) {
Ok(Some(vec![]))
} else {
Err(CodeSelectionError(Unimplemented))
}
}
None => {
Ok(None)
}
}
}
ty::Predicate::WellFormed(ty) => {
match ty::wf::obligations(selcx.infcx(), obligation.cause.body_id,
ty, obligation.cause.span) {

View File

@ -165,6 +165,7 @@ pub fn supertraits_reference_self<'tcx>(tcx: &TyCtxt<'tcx>,
ty::Predicate::ObjectSafe(..) |
ty::Predicate::TypeOutlives(..) |
ty::Predicate::RegionOutlives(..) |
ty::Predicate::ClosureKind(..) |
ty::Predicate::Equate(..) => {
false
}
@ -207,6 +208,7 @@ fn generics_require_sized_self<'tcx>(tcx: &TyCtxt<'tcx>,
ty::Predicate::RegionOutlives(..) |
ty::Predicate::WellFormed(..) |
ty::Predicate::ObjectSafe(..) |
ty::Predicate::ClosureKind(..) |
ty::Predicate::TypeOutlives(..) => {
false
}

View File

@ -198,9 +198,10 @@ enum SelectionCandidate<'tcx> {
/// we found an applicable bound in the trait definition.
ProjectionCandidate,
/// Implementation of a `Fn`-family trait by one of the
/// anonymous types generated for a `||` expression.
ClosureCandidate(/* closure */ DefId, &'tcx ty::ClosureSubsts<'tcx>),
/// Implementation of a `Fn`-family trait by one of the anonymous types
/// generated for a `||` expression. The ty::ClosureKind informs the
/// confirmation step what ClosureKind obligation to emit.
ClosureCandidate(/* closure */ DefId, &'tcx ty::ClosureSubsts<'tcx>, ty::ClosureKind),
/// Implementation of a `Fn`-family trait by one of the anonymous
/// types generated for a fn pointer type (e.g., `fn(int)->int`)
@ -321,75 +322,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
let stack = self.push_stack(TraitObligationStackList::empty(), obligation);
match self.candidate_from_obligation(&stack)? {
None => {
self.consider_unification_despite_ambiguity(obligation);
Ok(None)
}
None => Ok(None),
Some(candidate) => Ok(Some(self.confirm_candidate(obligation, candidate)?)),
}
}
/// In the particular case of unboxed closure obligations, we can
/// sometimes do some amount of unification for the
/// argument/return types even though we can't yet fully match obligation.
/// The particular case we are interesting in is an obligation of the form:
///
/// C : FnFoo<A>
///
/// where `C` is an unboxed closure type and `FnFoo` is one of the
/// `Fn` traits. Because we know that users cannot write impls for closure types
/// themselves, the only way that `C : FnFoo` can fail to match is under two
/// conditions:
///
/// 1. The closure kind for `C` is not yet known, because inference isn't complete.
/// 2. The closure kind for `C` *is* known, but doesn't match what is needed.
/// For example, `C` may be a `FnOnce` closure, but a `Fn` closure is needed.
///
/// In either case, we always know what argument types are
/// expected by `C`, no matter what kind of `Fn` trait it
/// eventually matches. So we can go ahead and unify the argument
/// types, even though the end result is ambiguous.
///
/// Note that this is safe *even if* the trait would never be
/// matched (case 2 above). After all, in that case, an error will
/// result, so it kind of doesn't matter what we do --- unifying
/// the argument types can only be helpful to the user, because
/// once they patch up the kind of closure that is expected, the
/// argment types won't really change.
fn consider_unification_despite_ambiguity(&mut self, obligation: &TraitObligation<'tcx>) {
// Is this a `C : FnFoo(...)` trait reference for some trait binding `FnFoo`?
match self.tcx().lang_items.fn_trait_kind(obligation.predicate.0.def_id()) {
Some(_) => { }
None => { return; }
}
// Is the self-type a closure type? We ignore bindings here
// because if it is a closure type, it must be a closure type from
// within this current fn, and hence none of the higher-ranked
// lifetimes can appear inside the self-type.
let self_ty = self.infcx.shallow_resolve(*obligation.self_ty().skip_binder());
let (closure_def_id, substs) = match self_ty.sty {
ty::TyClosure(id, ref substs) => (id, substs),
_ => { return; }
};
assert!(!substs.has_escaping_regions());
// It is OK to call the unnormalized variant here - this is only
// reached for TyClosure: Fn inputs where the closure kind is
// still unknown, which should only occur in typeck where the
// closure type is already normalized.
let closure_trait_ref = self.closure_trait_ref_unnormalized(obligation,
closure_def_id,
substs);
match self.confirm_poly_trait_refs(obligation.cause.clone(),
obligation.predicate.to_poly_trait_ref(),
closure_trait_ref) {
Ok(()) => { }
Err(_) => { /* Silently ignore errors. */ }
}
}
///////////////////////////////////////////////////////////////////////////
// EVALUATION
//
@ -532,6 +469,21 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}
}
}
ty::Predicate::ClosureKind(closure_def_id, kind) => {
match self.infcx.closure_kind(closure_def_id) {
Some(closure_kind) => {
if closure_kind.extends(kind) {
EvaluatedToOk
} else {
EvaluatedToErr
}
}
None => {
EvaluatedToAmbig
}
}
}
}
}
@ -1282,12 +1234,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
Some(closure_kind) => {
debug!("assemble_unboxed_candidates: closure_kind = {:?}", closure_kind);
if closure_kind.extends(kind) {
candidates.vec.push(ClosureCandidate(closure_def_id, substs));
candidates.vec.push(ClosureCandidate(closure_def_id, substs, kind));
}
}
None => {
debug!("assemble_unboxed_candidates: closure_kind not yet known");
candidates.ambiguous = true;
candidates.vec.push(ClosureCandidate(closure_def_id, substs, kind));
}
}
@ -2071,9 +2023,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
Ok(VtableImpl(vtable_impl))
}
ClosureCandidate(closure_def_id, substs) => {
ClosureCandidate(closure_def_id, substs, kind) => {
let vtable_closure =
self.confirm_closure_candidate(obligation, closure_def_id, substs)?;
self.confirm_closure_candidate(obligation, closure_def_id, substs, kind)?;
Ok(VtableClosure(vtable_closure))
}
@ -2430,7 +2382,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
fn confirm_closure_candidate(&mut self,
obligation: &TraitObligation<'tcx>,
closure_def_id: DefId,
substs: &ty::ClosureSubsts<'tcx>)
substs: &ty::ClosureSubsts<'tcx>,
kind: ty::ClosureKind)
-> Result<VtableClosureData<'tcx, PredicateObligation<'tcx>>,
SelectionError<'tcx>>
{
@ -2441,7 +2394,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
let Normalized {
value: trait_ref,
obligations
mut obligations
} = self.closure_trait_ref(obligation, closure_def_id, substs);
debug!("confirm_closure_candidate(closure_def_id={:?}, trait_ref={:?}, obligations={:?})",
@ -2453,6 +2406,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
obligation.predicate.to_poly_trait_ref(),
trait_ref)?;
obligations.push(Obligation::new(
obligation.cause.clone(),
ty::Predicate::ClosureKind(closure_def_id, kind)));
Ok(VtableClosureData {
closure_def_id: closure_def_id,
substs: substs.clone(),

View File

@ -60,6 +60,9 @@ impl<'a,'tcx> PredicateSet<'a,'tcx> {
ty::Predicate::ObjectSafe(data) =>
ty::Predicate::ObjectSafe(data),
ty::Predicate::ClosureKind(closure_def_id, kind) =>
ty::Predicate::ClosureKind(closure_def_id, kind)
};
self.set.insert(normalized_pred)
}
@ -156,6 +159,9 @@ impl<'cx, 'tcx> Elaborator<'cx, 'tcx> {
ty::Predicate::Projection(..) => {
// Nothing to elaborate in a projection predicate.
}
ty::Predicate::ClosureKind(..) => {
// Nothing to elaborate when waiting for a closure's kind to be inferred.
}
ty::Predicate::RegionOutlives(..) |
ty::Predicate::TypeOutlives(..) => {
// Currently, we do not "elaborate" predicates like

View File

@ -832,6 +832,11 @@ pub enum Predicate<'tcx> {
/// trait must be object-safe
ObjectSafe(DefId),
/// No direct syntax. May be thought of as `where T : FnFoo<...>` for some 'TypeSpace'
/// substitutions `...` and T being a closure type. Satisfied (or refuted) once we know the
/// closure's kind.
ClosureKind(DefId, ClosureKind),
}
impl<'tcx> Predicate<'tcx> {
@ -921,6 +926,8 @@ impl<'tcx> Predicate<'tcx> {
Predicate::WellFormed(data.subst(tcx, substs)),
Predicate::ObjectSafe(trait_def_id) =>
Predicate::ObjectSafe(trait_def_id),
Predicate::ClosureKind(closure_def_id, kind) =>
Predicate::ClosureKind(closure_def_id, kind),
}
}
}
@ -1108,6 +1115,9 @@ impl<'tcx> Predicate<'tcx> {
ty::Predicate::ObjectSafe(_trait_def_id) => {
vec![]
}
ty::Predicate::ClosureKind(_closure_def_id, _kind) => {
vec![]
}
};
// The only reason to collect into a vector here is that I was
@ -1128,6 +1138,7 @@ impl<'tcx> Predicate<'tcx> {
Predicate::RegionOutlives(..) |
Predicate::WellFormed(..) |
Predicate::ObjectSafe(..) |
Predicate::ClosureKind(..) |
Predicate::TypeOutlives(..) => {
None
}
@ -1783,7 +1794,7 @@ pub struct ItemSubsts<'tcx> {
pub substs: Substs<'tcx>,
}
#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Debug, RustcEncodable, RustcDecodable)]
#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)]
pub enum ClosureKind {
// Warning: Ordering is significant here! The ordering is chosen
// because the trait Fn is a subtrait of FnMut and so in turn, and

View File

@ -644,6 +644,8 @@ impl<'tcx> TypeFoldable<'tcx> for ty::Predicate<'tcx> {
ty::Predicate::Projection(binder.fold_with(folder)),
ty::Predicate::WellFormed(data) =>
ty::Predicate::WellFormed(data.fold_with(folder)),
ty::Predicate::ClosureKind(closure_def_id, kind) =>
ty::Predicate::ClosureKind(closure_def_id, kind),
ty::Predicate::ObjectSafe(trait_def_id) =>
ty::Predicate::ObjectSafe(trait_def_id),
}
@ -657,6 +659,7 @@ impl<'tcx> TypeFoldable<'tcx> for ty::Predicate<'tcx> {
ty::Predicate::TypeOutlives(ref binder) => binder.visit_with(visitor),
ty::Predicate::Projection(ref binder) => binder.visit_with(visitor),
ty::Predicate::WellFormed(data) => data.visit_with(visitor),
ty::Predicate::ClosureKind(_closure_def_id, _kind) => false,
ty::Predicate::ObjectSafe(_trait_def_id) => false,
}
}

View File

@ -301,6 +301,7 @@ impl<'tcx> TyCtxt<'tcx> {
ty::Predicate::Equate(..) |
ty::Predicate::WellFormed(..) |
ty::Predicate::ObjectSafe(..) |
ty::Predicate::ClosureKind(..) |
ty::Predicate::RegionOutlives(..) => {
None
}

View File

@ -92,6 +92,8 @@ pub fn predicate_obligations<'a,'tcx>(infcx: &InferCtxt<'a, 'tcx>,
}
ty::Predicate::ObjectSafe(_) => {
}
ty::Predicate::ClosureKind(..) => {
}
}
wf.normalize()
@ -155,6 +157,7 @@ pub fn implied_bounds<'a,'tcx>(
ty::Predicate::Trait(..) |
ty::Predicate::Equate(..) |
ty::Predicate::Projection(..) |
ty::Predicate::ClosureKind(..) |
ty::Predicate::ObjectSafe(..) =>
vec![],

View File

@ -467,6 +467,9 @@ impl<'tcx> fmt::Debug for ty::Predicate<'tcx> {
ty::Predicate::ObjectSafe(trait_def_id) => {
write!(f, "ObjectSafe({:?})", trait_def_id)
}
ty::Predicate::ClosureKind(closure_def_id, kind) => {
write!(f, "ClosureKind({:?}, {:?})", closure_def_id, kind)
}
}
}
}
@ -1039,6 +1042,16 @@ impl<'tcx> fmt::Display for ty::ProjectionTy<'tcx> {
}
}
impl fmt::Display for ty::ClosureKind {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
ty::ClosureKind::Fn => write!(f, "Fn"),
ty::ClosureKind::FnMut => write!(f, "FnMut"),
ty::ClosureKind::FnOnce => write!(f, "FnOnce"),
}
}
}
impl<'tcx> fmt::Display for ty::Predicate<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
@ -1052,6 +1065,11 @@ impl<'tcx> fmt::Display for ty::Predicate<'tcx> {
ty::tls::with(|tcx| {
write!(f, "the trait `{}` is object-safe", tcx.item_path_str(trait_def_id))
}),
ty::Predicate::ClosureKind(closure_def_id, kind) =>
ty::tls::with(|tcx| {
write!(f, "the closure `{}` implements the trait `{}`",
tcx.item_path_str(closure_def_id), kind)
}),
}
}
}

View File

@ -553,6 +553,18 @@ impl<'a,'tcx> TyDecoder<'a,'tcx> {
assert_eq!(self.next(), '|');
ty::Predicate::ObjectSafe(def_id)
}
'c' => {
let def_id = self.parse_def();
assert_eq!(self.next(), '|');
let kind = match self.next() {
'f' => ty::ClosureKind::Fn,
'm' => ty::ClosureKind::FnMut,
'o' => ty::ClosureKind::FnOnce,
c => bug!("Encountered invalid character in metadata: {}", c)
};
assert_eq!(self.next(), '|');
ty::Predicate::ClosureKind(def_id, kind)
}
c => bug!("Encountered invalid character in metadata: {}", c)
}
}

View File

@ -479,6 +479,14 @@ pub fn enc_predicate<'a, 'tcx>(w: &mut Cursor<Vec<u8>>,
ty::Predicate::ObjectSafe(trait_def_id) => {
write!(w, "O{}|", (cx.ds)(cx.tcx, trait_def_id));
}
ty::Predicate::ClosureKind(closure_def_id, kind) => {
let kind_char = match kind {
ty::ClosureKind::Fn => 'f',
ty::ClosureKind::FnMut => 'm',
ty::ClosureKind::FnOnce => 'o',
};
write!(w, "c{}|{}|", (cx.ds)(cx.tcx, closure_def_id), kind_char);
}
}
}

View File

@ -179,6 +179,16 @@ fn deduce_expectations_from_obligations<'a,'tcx>(
ty::Predicate::TypeOutlives(..) => None,
ty::Predicate::WellFormed(..) => None,
ty::Predicate::ObjectSafe(..) => None,
// NB: This predicate is created by breaking down a
// `ClosureType: FnFoo()` predicate, where
// `ClosureType` represents some `TyClosure`. It can't
// possibly be referring to the current closure,
// because we haven't produced the `TyClosure` for
// this closure yet; this is exactly why the other
// code is looking for a self type of a unresolved
// inference variable.
ty::Predicate::ClosureKind(..) => None,
};
opt_trait_ref
.and_then(|trait_ref| self_type_matches_expected_vid(fcx, trait_ref, expected_vid))

View File

@ -491,6 +491,7 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
ty::Predicate::RegionOutlives(..) |
ty::Predicate::WellFormed(..) |
ty::Predicate::ObjectSafe(..) |
ty::Predicate::ClosureKind(..) |
ty::Predicate::TypeOutlives(..) => {
None
}

View File

@ -449,6 +449,7 @@ impl<'tcx> GetTypeParameterBounds<'tcx> for ty::GenericPredicates<'tcx> {
ty::Predicate::RegionOutlives(..) |
ty::Predicate::WellFormed(..) |
ty::Predicate::ObjectSafe(..) |
ty::Predicate::ClosureKind(..) |
ty::Predicate::Projection(..) => {
false
}

View File

@ -849,6 +849,7 @@ impl<'a> Clean<WherePredicate> for ty::Predicate<'a> {
Predicate::Projection(ref pred) => pred.clean(cx),
Predicate::WellFormed(_) => panic!("not user writable"),
Predicate::ObjectSafe(_) => panic!("not user writable"),
Predicate::ClosureKind(..) => panic!("not user writable"),
}
}
}

View File

@ -0,0 +1,22 @@
// 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.
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
struct X;
fn foo<T>(_: T) {}
fn bar<T: Fn(u32)>(_: T) {}
fn main() {
let x = X;
let closure = |_| foo(x); //~ ERROR E0524
bar(closure);
}