Rewrite constrained type params code to operate generically over

multiple kinds of parameters (regions and types, specifically)
This commit is contained in:
Niko Matsakis 2015-04-15 09:13:52 -04:00
parent 4a1f556999
commit ad3cbacd02
3 changed files with 89 additions and 38 deletions

View File

@ -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(&param_ty) {
if constrained_parameters.contains(&Parameter::Type(param_ty)) {
continue;
}
let span = self.ty_param_span(ast_generics, item, space, index);

View File

@ -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();
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));
}
identify_constrained_type_params(tcx,
impl_predicates.predicates.as_slice(),
impl_trait_ref,
&mut input_parameters);
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(&param_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",

View File

@ -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<Parameter> {
ty.walk()
.flat_map(|ty| parameters_for_type_shallow(ty).into_iter())
.collect()
}
pub fn parameters_for_trait_ref<'tcx>(trait_ref: &Rc<ty::TraitRef<'tcx>>) -> Vec<Parameter> {
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<Parameter> {
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<Parameter> {
substs.regions()
.iter()
.filter_map(|r| parameters_for_region(r))
.collect()
}
fn parameters_for_region(region: &ty::Region) -> Option<Parameter> {
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<Rc<ty::TraitRef<'tcx>>>,
input_parameters: &mut HashSet<ty::ParamTy>)
input_parameters: &mut HashSet<Parameter>)
{
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));
}
}