refactor the handling of builtin candidates

This commit is contained in:
Ariel Ben-Yehuda 2016-04-18 00:04:21 +03:00
parent 4bcabbd45a
commit babb5df529
4 changed files with 129 additions and 220 deletions

View File

@ -187,7 +187,7 @@ pub enum MethodMatchedData {
/// parameter environment.
#[derive(PartialEq,Eq,Debug,Clone)]
enum SelectionCandidate<'tcx> {
BuiltinCandidate(ty::BuiltinBound),
BuiltinCandidate { has_nested: bool },
ParamCandidate(ty::PolyTraitRef<'tcx>),
ImplCandidate(DefId),
DefaultImplCandidate(DefId),
@ -240,7 +240,7 @@ enum BuiltinImplConditions<'tcx> {
None,
/// There is *no* impl for this, builtin or not. Ignore
/// all where-clauses.
Never(SelectionError<'tcx>),
Never,
/// It is unknown whether there is an impl.
Ambiguous
}
@ -1000,15 +1000,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
self.assemble_candidates_from_impls(obligation, &mut candidates)?;
// For other types, we'll use the builtin rules.
self.assemble_builtin_bound_candidates(ty::BoundCopy,
obligation,
&mut candidates)?;
let copy_conditions = self.copy_conditions(obligation);
self.assemble_builtin_bound_candidates(copy_conditions, &mut candidates)?;
}
Some(bound @ ty::BoundSized) => {
Some(ty::BoundSized) => {
// Sized is never implementable by end-users, it is
// always automatically computed.
self.assemble_builtin_bound_candidates(bound,
obligation,
let sized_conditions = self.sized_conditions(obligation);
self.assemble_builtin_bound_candidates(sized_conditions,
&mut candidates)?;
}
@ -1577,7 +1576,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
BuiltinObjectCandidate |
BuiltinUnsizeCandidate |
DefaultImplObjectCandidate(..) |
BuiltinCandidate(..) => {
BuiltinCandidate { .. } => {
// We have a where-clause so don't go around looking
// for impls.
true
@ -1618,16 +1617,16 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
// HACK: if this returns an error, selection exits without considering
// other impls.
fn assemble_builtin_bound_candidates<'o>(&mut self,
bound: ty::BuiltinBound,
obligation: &TraitObligation<'tcx>,
conditions: BuiltinImplConditions<'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>)
-> Result<(),SelectionError<'tcx>>
{
match self.builtin_bound(bound, obligation) {
BuiltinImplConditions::Where(..) => {
debug!("builtin_bound: bound={:?}",
bound);
candidates.vec.push(BuiltinCandidate(bound));
match conditions {
BuiltinImplConditions::Where(nested) => {
debug!("builtin_bound: nested={:?}", nested);
candidates.vec.push(BuiltinCandidate {
has_nested: nested.skip_binder().len() > 0
});
Ok(())
}
BuiltinImplConditions::None => { Ok(()) }
@ -1635,182 +1634,44 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
debug!("assemble_builtin_bound_candidates: ambiguous builtin");
Ok(candidates.ambiguous = true)
}
BuiltinImplConditions::Never(e) => { Err(e) }
BuiltinImplConditions::Never => { Err(Unimplemented) }
}
}
fn builtin_bound(&mut self,
bound: ty::BuiltinBound,
obligation: &TraitObligation<'tcx>)
fn sized_conditions(&mut self, obligation: &TraitObligation<'tcx>)
-> BuiltinImplConditions<'tcx>
{
// Note: these tests operate on types that may contain bound
// regions. To be proper, we ought to skolemize here, but we
// forego the skolemization and defer it until the
// confirmation step.
use self::BuiltinImplConditions::{Ambiguous, None, Never, Where};
let self_ty = self.infcx.shallow_resolve(obligation.predicate.0.self_ty());
// NOTE: binder moved to (*)
let self_ty = self.infcx.shallow_resolve(
obligation.predicate.skip_binder().self_ty());
let always = BuiltinImplConditions::Where(ty::Binder(Vec::new()));
let never = BuiltinImplConditions::Never(Unimplemented);
return match self_ty.sty {
ty::TyInfer(ty::IntVar(_)) |
ty::TyInfer(ty::FloatVar(_)) |
ty::TyUint(_) |
ty::TyInt(_) |
ty::TyBool |
ty::TyFloat(_) |
ty::TyFnDef(..) |
ty::TyFnPtr(_) |
ty::TyChar => {
match self_ty.sty {
ty::TyInfer(ty::IntVar(_)) | ty::TyInfer(ty::FloatVar(_)) |
ty::TyUint(_) | ty::TyInt(_) | ty::TyBool | ty::TyFloat(_) |
ty::TyFnDef(..) | ty::TyFnPtr(_) | ty::TyRawPtr(..) |
ty::TyChar | ty::TyBox(_) | ty::TyRef(..) |
ty::TyArray(..) | ty::TyTuple(..) | ty::TyClosure(..) |
ty::TyError => {
// safe for everything
always
Where(ty::Binder(Vec::new()))
}
ty::TyBox(_) => { // Box<T>
match bound {
ty::BoundCopy => never,
ty::BoundSized => always,
ty::BoundSync | ty::BoundSend => {
bug!("Send/Sync shouldn't occur in builtin_bounds()");
}
}
}
ty::TyRawPtr(..) => { // *const T, *mut T
match bound {
ty::BoundCopy | ty::BoundSized => always,
ty::BoundSync | ty::BoundSend => {
bug!("Send/Sync shouldn't occur in builtin_bounds()");
}
}
}
ty::TyTrait(ref data) => {
match bound {
ty::BoundSized => never,
ty::BoundCopy => {
// FIXME(#32963): bit-rot fungus infestation
if data.bounds.builtin_bounds.contains(&bound) {
always
} else {
// Recursively check all supertraits to find out if any further
// bounds are required and thus we must fulfill.
let principal =
data.principal_trait_ref_with_self_ty(self.tcx(),
self.tcx().types.err);
let copy_def_id = obligation.predicate.def_id();
for tr in util::supertraits(self.tcx(), principal) {
if tr.def_id() == copy_def_id {
return always
}
}
never
}
}
ty::BoundSync | ty::BoundSend => {
bug!("Send/Sync shouldn't occur in builtin_bounds()");
}
}
}
ty::TyRef(_, ty::TypeAndMut { ty: _, mutbl }) => {
// &mut T or &T
match bound {
ty::BoundCopy => {
match mutbl {
// &mut T is affine and hence never `Copy`
hir::MutMutable => never,
// &T is always copyable
hir::MutImmutable => always
}
}
ty::BoundSized => always,
ty::BoundSync | ty::BoundSend => {
bug!("Send/Sync shouldn't occur in builtin_bounds()");
}
}
}
ty::TyArray(element_ty, _) => {
// [T; n]
match bound {
ty::BoundCopy => ok_if(vec![element_ty]),
ty::BoundSized => always,
ty::BoundSync | ty::BoundSend => {
bug!("Send/Sync shouldn't occur in builtin_bounds()");
}
}
}
ty::TyStr | ty::TySlice(_) => {
match bound {
ty::BoundSync | ty::BoundSend => {
bug!("Send/Sync shouldn't occur in builtin_bounds()");
}
ty::BoundCopy | ty::BoundSized => never
}
}
// (T1, ..., Tn) -- meets any bound that all of T1...Tn meet
ty::TyTuple(ref tys) => ok_if(tys.clone()),
ty::TyClosure(..) => {
match bound {
ty::BoundSync | ty::BoundSend => {
bug!("Send/Sync shouldn't occur in builtin_bounds()");
}
ty::BoundCopy => never,
ty::BoundSized => always
}
}
ty::TyStr | ty::TySlice(_) | ty::TyTrait(..) => Never,
ty::TyStruct(def, substs) | ty::TyEnum(def, substs) => {
match bound {
// Fallback to whatever user-defined impls exist in this case.
ty::BoundCopy => BuiltinImplConditions::None,
// Sized if all the component types are sized.
ty::BoundSized => {
let sized_crit = def.sized_constraint(self.tcx());
if sized_crit == self.tcx().types.bool {
always
} else {
ok_if(vec![sized_crit.subst(self.tcx(), substs)])
}
}
// Shouldn't be coming through here.
ty::BoundSend | ty::BoundSync => bug!(),
}
let sized_crit = def.sized_constraint(self.tcx());
// (*) binder moved here
Where(ty::Binder(match sized_crit.sty {
ty::TyTuple(ref tys) => tys.to_owned().subst(self.tcx(), substs),
ty::TyBool => vec![],
_ => vec![sized_crit.subst(self.tcx(), substs)]
}))
}
ty::TyProjection(_) | ty::TyParam(_) => {
// Note: A type parameter is only considered to meet a
// particular bound if there is a where clause telling
// us that it does, and that case is handled by
// `assemble_candidates_from_caller_bounds()`.
BuiltinImplConditions::None
}
ty::TyInfer(ty::TyVar(_)) => {
// Unbound type variable. Might or might not have
// applicable impls and so forth, depending on what
// those type variables wind up being bound to.
debug!("assemble_builtin_bound_candidates: ambiguous builtin");
BuiltinImplConditions::Ambiguous
}
ty::TyError => always,
ty::TyProjection(_) | ty::TyParam(_) => None,
ty::TyInfer(ty::TyVar(_)) => Ambiguous,
ty::TyInfer(ty::FreshTy(_))
| ty::TyInfer(ty::FreshIntTy(_))
@ -1818,10 +1679,61 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
bug!("asked to assemble builtin bounds of unexpected type: {:?}",
self_ty);
}
};
}
}
fn ok_if<'tcx>(v: Vec<Ty<'tcx>>) -> BuiltinImplConditions<'tcx> {
BuiltinImplConditions::Where(ty::Binder(v))
fn copy_conditions(&mut self, obligation: &TraitObligation<'tcx>)
-> BuiltinImplConditions<'tcx>
{
// NOTE: binder moved to (*)
let self_ty = self.infcx.shallow_resolve(
obligation.predicate.skip_binder().self_ty());
use self::BuiltinImplConditions::{Ambiguous, None, Never, Where};
match self_ty.sty {
ty::TyInfer(ty::IntVar(_)) | ty::TyInfer(ty::FloatVar(_)) |
ty::TyUint(_) | ty::TyInt(_) | ty::TyBool | ty::TyFloat(_) |
ty::TyFnDef(..) | ty::TyFnPtr(_) | ty::TyChar |
ty::TyRawPtr(..) | ty::TyError |
ty::TyRef(_, ty::TypeAndMut { ty: _, mutbl: hir::MutImmutable }) => {
Where(ty::Binder(Vec::new()))
}
ty::TyBox(_) | ty::TyTrait(..) | ty::TyStr | ty::TySlice(..) |
ty::TyClosure(..) |
ty::TyRef(_, ty::TypeAndMut { ty: _, mutbl: hir::MutMutable }) => {
Never
}
ty::TyArray(element_ty, _) => {
// (*) binder moved here
Where(ty::Binder(vec![element_ty]))
}
ty::TyTuple(ref tys) => {
// (*) binder moved here
Where(ty::Binder(tys.clone()))
}
ty::TyStruct(..) | ty::TyEnum(..) | ty::TyProjection(..) | ty::TyParam(..) => {
// Fallback to whatever user-defined impls exist in this case.
None
}
ty::TyInfer(ty::TyVar(_)) => {
// Unbound type variable. Might or might not have
// applicable impls and so forth, depending on what
// those type variables wind up being bound to.
Ambiguous
}
ty::TyInfer(ty::FreshTy(_))
| ty::TyInfer(ty::FreshIntTy(_))
| ty::TyInfer(ty::FreshFloatTy(_)) => {
bug!("asked to assemble builtin bounds of unexpected type: {:?}",
self_ty);
}
}
}
@ -1988,9 +1900,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
candidate);
match candidate {
BuiltinCandidate(builtin_bound) => {
BuiltinCandidate { has_nested } => {
Ok(VtableBuiltin(
self.confirm_builtin_candidate(obligation, builtin_bound)))
self.confirm_builtin_candidate(obligation, has_nested)))
}
ParamCandidate(param) => {
@ -2090,45 +2002,36 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
fn confirm_builtin_candidate(&mut self,
obligation: &TraitObligation<'tcx>,
bound: ty::BuiltinBound)
has_nested: bool)
-> VtableBuiltinData<PredicateObligation<'tcx>>
{
debug!("confirm_builtin_candidate({:?})",
obligation);
debug!("confirm_builtin_candidate({:?}, {:?})",
obligation, has_nested);
match self.builtin_bound(bound, obligation) {
BuiltinImplConditions::Where(nested) =>
self.vtable_builtin_data(obligation, bound, nested),
_ => {
span_bug!(
obligation.cause.span,
"confiriming builtin impl for {:?} where none exists",
obligation);
}
}
}
let obligations = if has_nested {
let trait_def = obligation.predicate.def_id();
let conditions = match trait_def {
_ if Some(trait_def) == self.tcx().lang_items.sized_trait() => {
self.sized_conditions(obligation)
}
_ if Some(trait_def) == self.tcx().lang_items.copy_trait() => {
self.copy_conditions(obligation)
}
_ => bug!("unexpected builtin trait {:?}", trait_def)
};
let nested = match conditions {
BuiltinImplConditions::Where(nested) => nested,
_ => bug!("obligation {:?} had matched a builtin impl but now doesn't",
obligation)
};
fn vtable_builtin_data(&mut self,
obligation: &TraitObligation<'tcx>,
bound: ty::BuiltinBound,
nested: ty::Binder<Vec<Ty<'tcx>>>)
-> VtableBuiltinData<PredicateObligation<'tcx>>
{
debug!("vtable_builtin_data(obligation={:?}, bound={:?}, nested={:?})",
obligation, bound, nested);
let trait_def = match self.tcx().lang_items.from_builtin_kind(bound) {
Ok(def_id) => def_id,
Err(_) => {
bug!("builtin trait definition not found");
}
self.collect_predicates_for_types(obligation, trait_def, nested)
} else {
vec![]
};
let obligations = self.collect_predicates_for_types(obligation, trait_def, nested);
debug!("vtable_builtin_data: obligations={:?}",
debug!("confirm_builtin_candidate: obligations={:?}",
obligations);
VtableBuiltinData { nested: obligations }
}

View File

@ -329,7 +329,6 @@ impl<'tcx> TyCtxt<'tcx> {
where F : FnMut(ty::BoundRegion) -> ty::Region,
T : TypeFoldable<'tcx>,
{
debug!("replace_late_bound_regions({:?})", value);
let mut replacer = RegionReplacer::new(self, &mut f);
let result = value.skip_binder().fold_with(&mut replacer);
(result, replacer.map)
@ -444,8 +443,6 @@ impl<'a, 'tcx> TypeFolder<'tcx> for RegionReplacer<'a, 'tcx>
fn fold_region(&mut self, r: ty::Region) -> ty::Region {
match r {
ty::ReLateBound(debruijn, br) if debruijn.depth == self.current_depth => {
debug!("RegionReplacer.fold_region({:?}) folding region (current_depth={})",
r, self.current_depth);
let fld_r = &mut self.fld_r;
let region = *self.map.entry(br).or_insert_with(|| fld_r(br));
if let ty::ReLateBound(debruijn1, br) = region {

View File

@ -1722,6 +1722,10 @@ impl<'tcx, 'container> AdtDefData<'tcx, 'container> {
/// Returns a simpler type such that `Self: Sized` if and only
/// if that type is Sized, or `TyErr` if this type is recursive.
///
/// HACK: instead of returning a list of types, this function can
/// return a tuple. In that case, the result is Sized only if
/// all elements of the tuple are Sized.
///
/// This is generally the `struct_tail` if this is a struct, or a
/// tuple of them if this is an enum.
///
@ -1738,7 +1742,7 @@ impl<'tcx, 'container> AdtDefData<'tcx, 'container> {
None => {
let this = tcx.lookup_adt_def_master(self.did);
this.calculate_sized_constraint_inner(tcx, &mut Vec::new());
self.sized_constraint.unwrap(dep_node)
self.sized_constraint(tcx)
}
Some(ty) => ty
}
@ -1756,6 +1760,10 @@ impl<'tcx> AdtDefData<'tcx, 'tcx> {
{
let tys : Vec<_> = tys.into_iter()
.map(|ty| self.sized_constraint_for_ty(tcx, stack, ty))
.flat_map(|ty| match ty.sty {
ty::TyTuple(ref tys) => tys.clone(),
_ => vec![ty]
})
.filter(|ty| *ty != tcx.types.bool)
.collect();
@ -1766,6 +1774,7 @@ impl<'tcx> AdtDefData<'tcx, 'tcx> {
_ => tcx.mk_tup(tys)
}
}
fn sized_constraint_for_ty(
&'tcx self,
tcx: &ty::TyCtxt<'tcx>,

View File

@ -493,7 +493,7 @@ impl fmt::Debug for ty::BoundRegion {
BrAnon(n) => write!(f, "BrAnon({:?})", n),
BrFresh(n) => write!(f, "BrFresh({:?})", n),
BrNamed(did, name) => {
write!(f, "BrNamed({:?}, {:?})", did, name)
write!(f, "BrNamed({:?}:{:?}, {:?})", did.krate, did.index, name)
}
BrEnv => "BrEnv".fmt(f),
}