Teach project to project associated types out of object types.

This commit is contained in:
Niko Matsakis 2014-12-26 07:07:55 -05:00
parent 5b53b11ad9
commit de806bc057
3 changed files with 116 additions and 17 deletions

View File

@ -19,7 +19,7 @@ use super::VtableImplData;
use middle::infer;
use middle::subst::Subst;
use middle::ty::{mod, ToPolyTraitRef, Ty};
use middle::ty::{mod, AsPredicate, ToPolyTraitRef, Ty};
use std::fmt;
use util::ppaux::Repr;
@ -46,7 +46,7 @@ pub enum ProjectionError<'tcx> {
#[deriving(Clone)]
pub struct MismatchedProjectionTypes<'tcx> {
pub err: ty::type_err<'tcx> // TODO expected/actual/etc
pub err: ty::type_err<'tcx>
}
pub type ProjectionResult<'tcx, T> = Result<T, ProjectionError<'tcx>>;
@ -123,9 +123,20 @@ pub fn project_type<'cx,'tcx>(
obligation,
&mut candidates);
let () = try!(assemble_candidates_from_impls(selcx,
obligation,
&mut candidates));
let () = assemble_candidates_from_object_type(selcx,
obligation,
&mut candidates);
if candidates.vec.is_empty() {
// TODO This `if` is not necessarily wrong, but it needs an
// explanation, and it should probably be accompanied by a
// similar rule in `select.rs`. Currently it's *needed*
// because the impl-trait-for-trait branch has not landed.
let () = try!(assemble_candidates_from_impls(selcx,
obligation,
&mut candidates));
}
debug!("{} candidates, ambiguous={}",
candidates.vec.len(),
@ -155,9 +166,21 @@ fn assemble_candidates_from_param_env<'cx,'tcx>(
obligation: &ProjectionTyObligation<'tcx>,
candidate_set: &mut ProjectionTyCandidateSet<'tcx>)
{
let infcx = selcx.infcx();
let env_predicates = selcx.param_env().caller_bounds.predicates.clone();
let env_predicates = env_predicates.iter().cloned().collect();
assemble_candidates_from_predicates(selcx, obligation, candidate_set, env_predicates);
}
fn assemble_candidates_from_predicates<'cx,'tcx>(
selcx: &mut SelectionContext<'cx,'tcx>,
obligation: &ProjectionTyObligation<'tcx>,
candidate_set: &mut ProjectionTyCandidateSet<'tcx>,
env_predicates: Vec<ty::Predicate<'tcx>>)
{
debug!("assemble_candidates_from_predicates(obligation={}, env_predicates={})",
obligation.repr(selcx.tcx()),
env_predicates.repr(selcx.tcx()));
let infcx = selcx.infcx();
for predicate in elaborate_predicates(selcx.tcx(), env_predicates) {
match predicate {
ty::Predicate::Projection(ref data) => {
@ -183,6 +206,26 @@ fn assemble_candidates_from_param_env<'cx,'tcx>(
}
}
fn assemble_candidates_from_object_type<'cx,'tcx>(
selcx: &mut SelectionContext<'cx,'tcx>,
obligation: &ProjectionTyObligation<'tcx>,
candidate_set: &mut ProjectionTyCandidateSet<'tcx>)
{
let infcx = selcx.infcx();
let trait_ref = infcx.resolve_type_vars_if_possible(&obligation.predicate.trait_ref);
debug!("assemble_candidates_from_object_type(trait_ref={})",
trait_ref.repr(infcx.tcx));
let self_ty = trait_ref.self_ty();
let data = match self_ty.sty {
ty::ty_trait(ref data) => data,
_ => { return; }
};
let env_predicates = data.projection_bounds_with_self_ty(self_ty).iter()
.map(|p| p.as_predicate())
.collect();
assemble_candidates_from_predicates(selcx, obligation, candidate_set, env_predicates)
}
fn assemble_candidates_from_impls<'cx,'tcx>(
selcx: &mut SelectionContext<'cx,'tcx>,
obligation: &ProjectionTyObligation<'tcx>,

View File

@ -1401,6 +1401,31 @@ impl<'tcx> TyTrait<'tcx> {
substs: tcx.mk_substs(self.principal.0.substs.with_self_ty(self_ty)),
}))
}
pub fn projection_bounds_with_self_ty(&self, self_ty: Ty<'tcx>)
-> Vec<ty::PolyProjectionPredicate<'tcx>>
{
// otherwise the escaping regions would be captured by the binders
assert!(!self_ty.has_escaping_regions());
self.bounds.projection_bounds.iter()
.map(|in_poly_projection_predicate| {
let in_projection_ty = &in_poly_projection_predicate.0.projection_ty;
let trait_ref =
Rc::new(ty::TraitRef::new(
in_projection_ty.trait_ref.def_id,
in_projection_ty.trait_ref.substs.with_self_ty(self_ty)));
let projection_ty = ty::ProjectionTy {
trait_ref: trait_ref,
item_name: in_projection_ty.item_name
};
ty::Binder(ty::ProjectionPredicate {
projection_ty: projection_ty,
ty: in_poly_projection_predicate.0.ty
})
})
.collect()
}
}
/// A complete reference to a trait. These take numerous guises in syntax,

View File

@ -13,12 +13,12 @@ use middle::subst::{FnSpace, SelfSpace};
use middle::traits;
use middle::traits::{Obligation, ObligationCause};
use middle::traits::report_fulfillment_errors;
use middle::ty::{mod, Ty, AsPredicate, ToPolyTraitRef};
use middle::ty::{mod, Ty, AsPredicate};
use middle::infer;
use std::rc::Rc;
use syntax::ast;
use syntax::codemap::Span;
use util::ppaux::{Repr, ty_to_string};
use util::nodemap::FnvHashSet;
use util::ppaux::{Repr, UserString};
pub fn check_object_cast<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
cast_expr: &ast::Expr,
@ -133,10 +133,33 @@ pub fn check_object_safety<'tcx>(tcx: &ty::ctxt<'tcx>,
object_trait: &ty::TyTrait<'tcx>,
span: Span)
{
// Also check that the type `object_trait` specifies all
// associated types for all supertraits.
let mut associated_types: FnvHashSet<(ast::DefId, ast::Name)> = FnvHashSet::new();
let object_trait_ref =
object_trait.principal_trait_ref_with_self_ty(tcx, tcx.types.err);
for tr in traits::supertraits(tcx, object_trait_ref) {
for tr in traits::supertraits(tcx, object_trait_ref.clone()) {
check_object_safety_inner(tcx, &tr, span);
let trait_def = ty::lookup_trait_def(tcx, object_trait_ref.def_id());
for &associated_type_name in trait_def.associated_type_names.iter() {
associated_types.insert((object_trait_ref.def_id(), associated_type_name));
}
}
for projection_bound in object_trait.bounds.projection_bounds.iter() {
let pair = (projection_bound.0.projection_ty.trait_ref.def_id,
projection_bound.0.projection_ty.item_name);
associated_types.remove(&pair);
}
for (trait_def_id, name) in associated_types.into_iter() {
tcx.sess.span_err(
span,
format!("the value of the associated type `{}` (from the trait `{}`) must be specified",
name.user_string(tcx),
ty::item_path_str(tcx, trait_def_id)).as_slice());
}
}
@ -201,7 +224,7 @@ fn check_object_safety_inner<'tcx>(tcx: &ty::ctxt<'tcx>,
Some(format!(
"cannot call a method (`{}`) whose type contains \
a self-type (`{}`) through a trait object",
method_name, ty_to_string(tcx, ty)))
method_name, ty.user_string(tcx)))
} else {
None
}
@ -343,15 +366,15 @@ pub fn register_object_cast_obligations<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
referent_ty.repr(fcx.tcx()),
object_trait_ty.repr(fcx.tcx()));
let cause = ObligationCause::new(span,
fcx.body_id,
traits::ObjectCastObligation(object_trait_ty));
// Create the obligation for casting from T to Trait.
let object_trait_ref =
object_trait.principal_trait_ref_with_self_ty(fcx.tcx(), referent_ty);
let object_obligation =
Obligation::new(
ObligationCause::new(span,
fcx.body_id,
traits::ObjectCastObligation(object_trait_ty)),
object_trait_ref.as_predicate());
Obligation::new(cause.clone(), object_trait_ref.as_predicate());
fcx.register_predicate(object_obligation);
// Create additional obligations for all the various builtin
@ -362,7 +385,15 @@ pub fn register_object_cast_obligations<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
fcx.register_builtin_bound(
referent_ty,
builtin_bound,
ObligationCause::new(span, fcx.body_id, traits::ObjectCastObligation(object_trait_ty)));
cause.clone());
}
// Finally, create obligations for the projection predicates.
let projection_bounds = object_trait.projection_bounds_with_self_ty(referent_ty);
for projection_bound in projection_bounds.iter() {
let projection_obligation =
Obligation::new(cause.clone(), projection_bound.as_predicate());
fcx.register_predicate(projection_obligation);
}
object_trait_ref