diff --git a/src/librustc_typeck/check/wf.rs b/src/librustc_typeck/check/wf.rs index 7f2af1d1b6262..83e0c398590f5 100644 --- a/src/librustc_typeck/check/wf.rs +++ b/src/librustc_typeck/check/wf.rs @@ -10,7 +10,7 @@ use astconv::AstConv; use check::{FnCtxt, Inherited, blank_fn_ctxt, vtable, regionck}; -use constrained_type_params::identify_constrained_type_params; +use constrained_type_params::{identify_constrained_type_params, Parameter}; use CrateCtxt; use middle::region; use middle::subst::{self, TypeSpace, FnSpace, ParamSpace, SelfSpace}; @@ -287,10 +287,11 @@ impl<'ccx, 'tcx> CheckTypeWellFormedVisitor<'ccx, 'tcx> { let mut constrained_parameters: HashSet<_> = variances.types - .iter_enumerated() - .filter(|&(_, _, &variance)| variance != ty::Bivariant) - .map(|(space, index, _)| self.param_ty(ast_generics, space, index)) - .collect(); + .iter_enumerated() + .filter(|&(_, _, &variance)| variance != ty::Bivariant) + .map(|(space, index, _)| self.param_ty(ast_generics, space, index)) + .map(|p| Parameter::Type(p)) + .collect(); identify_constrained_type_params(self.tcx(), ty_predicates.predicates.as_slice(), @@ -299,7 +300,7 @@ impl<'ccx, 'tcx> CheckTypeWellFormedVisitor<'ccx, 'tcx> { for (space, index, _) in variances.types.iter_enumerated() { let param_ty = self.param_ty(ast_generics, space, index); - if constrained_parameters.contains(¶m_ty) { + if constrained_parameters.contains(&Parameter::Type(param_ty)) { continue; } let span = self.ty_param_span(ast_generics, item, space, index); diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 72adad75865ee..dfcf375bdbc5f 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -66,7 +66,7 @@ There are some shortcomings in this design: use astconv::{self, AstConv, ty_of_arg, ast_ty_to_ty, ast_region_to_region}; use middle::def; -use constrained_type_params::identify_constrained_type_params; +use constrained_type_params as ctp; use middle::lang_items::SizedTraitLangItem; use middle::region; use middle::resolve_lifetime; @@ -2200,23 +2200,21 @@ fn enforce_impl_ty_params_are_constrained<'tcx>(tcx: &ty::ctxt<'tcx>, // reachable from there, to start (if this is an inherent impl, // then just examine the self type). let mut input_parameters: HashSet<_> = - impl_trait_ref.iter() - .flat_map(|t| t.input_types().iter()) // Types in trait ref, if any - .chain(Some(impl_scheme.ty).iter()) // Self type, always - .flat_map(|t| t.walk()) - .filter_map(|t| t.as_opt_param_ty()) - .collect(); - - identify_constrained_type_params(tcx, - impl_predicates.predicates.as_slice(), - impl_trait_ref, - &mut input_parameters); + 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); for (index, ty_param) in ast_generics.ty_params.iter().enumerate() { let param_ty = ty::ParamTy { space: TypeSpace, idx: index as u32, name: ty_param.ident.name }; - if !input_parameters.contains(¶m_ty) { + if !input_parameters.contains(&ctp::Parameter::Type(param_ty)) { span_err!(tcx.sess, ty_param.span, E0207, "the type parameter `{}` is not constrained by the \ impl trait, self type, or predicates", diff --git a/src/librustc_typeck/constrained_type_params.rs b/src/librustc_typeck/constrained_type_params.rs index 83d7e98500007..fad8fbef2a4d4 100644 --- a/src/librustc_typeck/constrained_type_params.rs +++ b/src/librustc_typeck/constrained_type_params.rs @@ -8,49 +8,101 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use middle::ty::{self}; +use middle::subst; +use middle::ty::{self, Ty}; use std::collections::HashSet; use std::rc::Rc; +#[derive(Clone, PartialEq, Eq, Hash, Debug)] +pub enum Parameter { + Type(ty::ParamTy), + Region(ty::EarlyBoundRegion), +} + +pub fn parameters_for_type<'tcx>(ty: Ty<'tcx>) -> Vec { + ty.walk() + .flat_map(|ty| parameters_for_type_shallow(ty).into_iter()) + .collect() +} + +pub fn parameters_for_trait_ref<'tcx>(trait_ref: &Rc>) -> Vec { + let mut region_parameters = + parameters_for_regions_in_substs(&trait_ref.substs); + + let type_parameters = + trait_ref.substs.types.iter() + .flat_map(|ty| parameters_for_type(ty).into_iter()); + + region_parameters.extend(type_parameters); + + region_parameters +} + +fn parameters_for_type_shallow<'tcx>(ty: Ty<'tcx>) -> Vec { + match ty.sty { + ty::ty_param(ref d) => + vec![Parameter::Type(d.clone())], + ty::ty_rptr(region, _) => + parameters_for_region(region).into_iter().collect(), + ty::ty_struct(_, substs) | + ty::ty_enum(_, substs) => + parameters_for_regions_in_substs(substs), + ty::ty_trait(ref data) => + parameters_for_regions_in_substs(&data.principal.skip_binder().substs), + _ => + vec![], + } +} + +fn parameters_for_regions_in_substs(substs: &subst::Substs) -> Vec { + substs.regions() + .iter() + .filter_map(|r| parameters_for_region(r)) + .collect() +} + +fn parameters_for_region(region: &ty::Region) -> Option { + match *region { + ty::ReEarlyBound(data) => Some(Parameter::Region(data)), + _ => None, + } +} + pub fn identify_constrained_type_params<'tcx>(_tcx: &ty::ctxt<'tcx>, predicates: &[ty::Predicate<'tcx>], impl_trait_ref: Option>>, - input_parameters: &mut HashSet) + input_parameters: &mut HashSet) { loop { let num_inputs = input_parameters.len(); - let projection_predicates = + let poly_projection_predicates = // : iterator over PolyProjectionPredicate predicates.iter() .filter_map(|predicate| { match *predicate { - // Ignore higher-ranked binders. For the purposes - // of this check, they don't matter because they - // only affect named regions, and we're just - // concerned about type parameters here. - ty::Predicate::Projection(ref data) => Some(data.0.clone()), + ty::Predicate::Projection(ref data) => Some(data.clone()), _ => None, } }); - for projection in projection_predicates { + 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(); + // Special case: watch out for some kind of sneaky attempt - // to project out an associated type defined by this very trait. - if Some(projection.projection_ty.trait_ref.clone()) == impl_trait_ref { + // 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 relies_only_on_inputs = - projection.projection_ty.trait_ref.input_types() - .iter() - .flat_map(|t| t.walk()) - .filter_map(|t| t.as_opt_param_ty()) - .all(|t| input_parameters.contains(&t)); - + 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( - projection.ty.walk().filter_map(|t| t.as_opt_param_ty())); + input_parameters.extend(parameters_for_type(projection.ty)); } }