Skip to content

Commit

Permalink
Rewrite constrained type params code to operate generically over
Browse files Browse the repository at this point in the history
multiple kinds of parameters (regions and types, specifically)
  • Loading branch information
nikomatsakis committed Apr 17, 2015
1 parent 4a1f556 commit ad3cbac
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 39 deletions.
13 changes: 7 additions & 6 deletions src/librustc_typeck/check/wf.rs
Expand Up @@ -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};
Expand Down Expand Up @@ -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(),
Expand All @@ -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);
Expand Down
24 changes: 11 additions & 13 deletions src/librustc_typeck/collect.rs
Expand Up @@ -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;
Expand Down Expand Up @@ -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(&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",
Expand Down
92 changes: 72 additions & 20 deletions src/librustc_typeck/constrained_type_params.rs
Expand Up @@ -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));
}
}

Expand Down

0 comments on commit ad3cbac

Please sign in to comment.