From de806bc057caa599fce959ce56259e6e919f1041 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 26 Dec 2014 07:07:55 -0500 Subject: [PATCH] Teach `project` to project associated types out of object types. --- src/librustc/middle/traits/project.rs | 55 ++++++++++++++++++++++++--- src/librustc/middle/ty.rs | 25 ++++++++++++ src/librustc_typeck/check/vtable.rs | 53 ++++++++++++++++++++------ 3 files changed, 116 insertions(+), 17 deletions(-) diff --git a/src/librustc/middle/traits/project.rs b/src/librustc/middle/traits/project.rs index 083227d02bd0..0594c0ac3b83 100644 --- a/src/librustc/middle/traits/project.rs +++ b/src/librustc/middle/traits/project.rs @@ -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>; @@ -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>) +{ + 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>, diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index e989f5a2cc2b..98539b4d1526 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -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> + { + // 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, diff --git a/src/librustc_typeck/check/vtable.rs b/src/librustc_typeck/check/vtable.rs index e8bd5c088fca..f5cbfcdcc521 100644 --- a/src/librustc_typeck/check/vtable.rs +++ b/src/librustc_typeck/check/vtable.rs @@ -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