them. Also fix some assertions and handling of builtin bounds.
cause: ObligationCause<'tcx>)
-> Ty<'tcx>
{
+ debug!("normalize_associated_type(trait_ref={}, item_name={})",
+ trait_ref.repr(infcx.tcx),
+ item_name.repr(infcx.tcx));
+
assert!(!trait_ref.has_escaping_regions());
let ty_var = infcx.next_ty_var();
});
let obligation = Obligation::new(cause, projection.as_predicate());
self.register_predicate(infcx, obligation);
+
+ debug!("normalize_associated_type: result={}", ty_var.repr(infcx.tcx));
+
ty_var
}
obligation: &TraitObligation<'tcx>)
-> Result<BuiltinBoundConditions<'tcx>,SelectionError<'tcx>>
{
- // TODO seems like we ought to skolemize here, oder?
+ // Note: these tests operate on types that may contain bound
+ // regions. To be proper, we ought to skolemize here, but we
+ // forego the skolemization and defer it until the
+ // confirmation step.
+
let self_ty = self.infcx.shallow_resolve(obligation.predicate.0.self_ty());
return match self_ty.sty {
ty::ty_infer(ty::IntVar(_)) |
-> VtableBuiltinData<PredicateObligation<'tcx>>
{
let derived_cause = self.derived_cause(obligation, BuiltinDerivedObligation);
- let obligations = nested.iter().map(|&t| {
- util::predicate_for_builtin_bound(
- self.tcx(),
- derived_cause.clone(),
- bound,
- obligation.recursion_depth + 1,
- t)
+ let obligations = nested.iter().map(|&bound_ty| {
+ // the obligation might be higher-ranked, e.g. for<'a> &'a
+ // int : Copy. In that case, we will wind up with
+ // late-bound regions in the `nested` vector. So for each
+ // one we instantiate to a skolemized region, do our work
+ // to produce something like `&'0 int : Copy`, and then
+ // re-bind it. This is a bit of busy-work but preserves
+ // the invariant that we only manipulate free regions, not
+ // bound ones.
+ self.infcx.try(|snapshot| {
+ let (skol_ty, skol_map) =
+ self.infcx().skolemize_late_bound_regions(&ty::Binder(bound_ty), snapshot);
+ let skol_predicate =
+ util::predicate_for_builtin_bound(
+ self.tcx(),
+ derived_cause.clone(),
+ bound,
+ obligation.recursion_depth + 1,
+ skol_ty);
+ match skol_predicate {
+ Ok(skol_predicate) => Ok(self.infcx().plug_leaks(skol_map, snapshot,
+ &skol_predicate)),
+ Err(ErrorReported) => Err(ErrorReported)
+ }
+ })
}).collect::<Result<_, _>>();
let mut obligations = match obligations {
Ok(o) => o,
impl<'tcx> RegionEscape for TraitRef<'tcx> {
fn has_regions_escaping_depth(&self, depth: u32) -> bool {
- self.substs.types.iter().any(|t| t.has_regions_escaping_depth(depth)) &&
- self.substs.regions().iter().any(|t| t.has_regions_escaping_depth(depth))
+ self.substs.types.iter().any(|t| t.has_regions_escaping_depth(depth)) ||
+ self.substs.regions.has_regions_escaping_depth(depth)
+ }
+}
+
+impl<'tcx> RegionEscape for subst::RegionSubsts {
+ fn has_regions_escaping_depth(&self, depth: u32) -> bool {
+ match *self {
+ subst::ErasedRegions => false,
+ subst::NonerasedRegions(ref r) => {
+ r.iter().any(|t| t.has_regions_escaping_depth(depth))
+ }
+ }
}
}
}
impl<'tcx> RegionEscape for TraitPredicate<'tcx> {
- fn has_regions_escaping_depth(&self, depth: uint) -> bool {
+ fn has_regions_escaping_depth(&self, depth: u32) -> bool {
self.trait_ref.has_regions_escaping_depth(depth)
}
}
}
impl<'tcx> RegionEscape for ProjectionPredicate<'tcx> {
- fn has_regions_escaping_depth(&self, depth: uint) -> bool {
+ fn has_regions_escaping_depth(&self, depth: u32) -> bool {
self.projection_ty.has_regions_escaping_depth(depth) ||
self.ty.has_regions_escaping_depth(depth)
}
}
impl<'tcx> RegionEscape for ProjectionTy<'tcx> {
- fn has_regions_escaping_depth(&self, depth: uint) -> bool {
+ fn has_regions_escaping_depth(&self, depth: u32) -> bool {
self.trait_ref.has_regions_escaping_depth(depth)
}
}
let mut normalizer = AssociatedTypeNormalizer { span: span,
body_id: body_id,
infcx: infcx,
- fulfillment_cx: fulfillment_cx };
+ fulfillment_cx: fulfillment_cx,
+ region_binders: 0 };
value.fold_with(&mut normalizer)
}
fulfillment_cx: &'a mut FulfillmentContext<'tcx>,
span: Span,
body_id: ast::NodeId,
+ region_binders: uint,
}
impl<'a,'tcx> TypeFolder<'tcx> for AssociatedTypeNormalizer<'a,'tcx> {
self.infcx.tcx
}
+ fn enter_region_binder(&mut self) {
+ self.region_binders += 1;
+ }
+
+ fn exit_region_binder(&mut self) {
+ self.region_binders -= 1;
+ }
+
fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
+ // We don't want to normalize associated types that occur inside of region
+ // binders, because they may contain bound regions, and we can't cope with that.
+ //
+ // Example:
+ //
+ // for<'a> fn(<T as Foo<&'a>>::A)
+ //
+ // Instead of normalizing `<T as Foo<&'a>>::A` here, we'll
+ // normalize it when we instantiate those bound regions (which
+ // should occur eventually).
+ let no_region_binders = self.region_binders == 0;
+
match ty.sty {
- ty::ty_projection(ref data) => {
+ ty::ty_projection(ref data) if no_region_binders => {
let cause =
ObligationCause::new(
self.span,
use astconv;
use middle::infer;
+use middle::region::CodeExtent;
use middle::subst;
use middle::ty::{mod, ToPolyTraitRef, Ty};
use rscope::RegionScope;
fcx.write_ty(expr.id, closure_type);
+ let fn_sig =
+ ty::liberate_late_bound_regions(fcx.tcx(), CodeExtent::from_node_id(body.id), &fn_ty.sig);
+
check_fn(fcx.ccx,
ast::Unsafety::Normal,
expr.id,
- &fn_ty.sig,
+ &fn_sig,
decl,
expr.id,
&*body,
decl,
abi::Rust,
expected_sig);
- let fty_sig = fn_ty.sig.clone();
+ let fn_sig = fn_ty.sig.clone();
let fty = ty::mk_closure(tcx, fn_ty);
debug!("check_expr_fn fty={}", fcx.infcx().ty_to_string(fty));
ty::UniqTraitStore => (ast::Unsafety::Normal, expr.id)
};
+ let fn_sig =
+ ty::liberate_late_bound_regions(tcx, CodeExtent::from_node_id(body.id), &fn_sig);
+
check_fn(fcx.ccx,
inherited_style,
inherited_style_id,
- &fty_sig,
+ &fn_sig,
&*decl,
expr.id,
&*body,
debug!("method_bounds after subst = {}",
method_bounds.repr(self.tcx()));
- // Substitute the type/early-bound-regions into the method
- // signature. In addition, the method signature may bind
- // late-bound regions, so instantiate those.
- let method_sig = self.fcx.instantiate_type_scheme(self.span,
- &all_substs,
- &pick.method_ty.fty.sig);
- debug!("late-bound lifetimes from method substituted, method_sig={}",
+ // Instantiate late-bound regions and substitute the trait
+ // parameters into the method type to get the actual method type.
+ //
+ // NB: Instantiate late-bound regions first so that
+ // `instantiate_type_scheme` can normalize associated types that
+ // may reference those regions.
+ let method_sig = self.replace_late_bound_regions_with_fresh_var(&pick.method_ty.fty.sig);
+ debug!("late-bound lifetimes from method instantiated, method_sig={}",
method_sig.repr(self.tcx()));
- let method_sig = self.replace_late_bound_regions_with_fresh_var(&method_sig);
- debug!("late-bound lifetimes from method instantiated, method_sig={}",
+ let method_sig = self.fcx.instantiate_type_scheme(self.span, &all_substs, &method_sig);
+ debug!("type scheme substituted, method_sig={}",
method_sig.repr(self.tcx()));
InstantiatedMethodSig {
debug!("lookup_in_trait_adjusted: method_num={} method_ty={}",
method_num, method_ty.repr(fcx.tcx()));
- // Substitute the trait parameters into the method type and
- // instantiate late-bound regions to get the actual method type.
- let bare_fn_ty = fcx.instantiate_type_scheme(span,
- &trait_ref.substs,
- &method_ty.fty);
+ // Instantiate late-bound regions and substitute the trait
+ // parameters into the method type to get the actual method type.
+ //
+ // NB: Instantiate late-bound regions first so that
+ // `instantiate_type_scheme` can normalize associated types that
+ // may reference those regions.
let fn_sig = fcx.infcx().replace_late_bound_regions_with_fresh_var(span,
infer::FnCall,
- &bare_fn_ty.sig).0;
+ &method_ty.fty.sig).0;
+ let fn_sig = fcx.instantiate_type_scheme(span, &trait_ref.substs, &fn_sig);
let transformed_self_ty = fn_sig.inputs[0];
let fty = ty::mk_bare_fn(tcx, None, tcx.mk_bare_fn(ty::BareFnTy {
sig: ty::Binder(fn_sig),
- unsafety: bare_fn_ty.unsafety,
- abi: bare_fn_ty.abi.clone(),
+ unsafety: method_ty.fty.unsafety,
+ abi: method_ty.fty.abi.clone(),
}));
debug!("lookup_in_trait_adjusted: matched method fty={} obligation={}",
ty::ty_bare_fn(_, ref fn_ty) => {
let inh = Inherited::new(ccx.tcx, param_env);
- // Compute the fty from point of view of inside fn
- // (replace any type-scheme with a type, and normalize
- // associated types appearing in the fn signature).
- let fn_ty = fn_ty.subst(ccx.tcx, &inh.param_env.free_substs);
- let fn_ty = inh.normalize_associated_types_in(body.span, body.id, &fn_ty);
-
- let fcx = check_fn(ccx, fn_ty.unsafety, id, &fn_ty.sig,
+ // Compute the fty from point of view of inside fn.
+ let fn_sig =
+ fn_ty.sig.subst(ccx.tcx, &inh.param_env.free_substs);
+ let fn_sig =
+ liberate_late_bound_regions(ccx.tcx, CodeExtent::from_node_id(body.id), &fn_sig);
+ let fn_sig =
+ inh.normalize_associated_types_in(body.span, body.id, &fn_sig);
+
+ let fcx = check_fn(ccx, fn_ty.unsafety, id, &fn_sig,
decl, id, body, &inh);
vtable::select_all_fcx_obligations_or_error(&fcx);
fn check_fn<'a, 'tcx>(ccx: &'a CrateCtxt<'a, 'tcx>,
unsafety: ast::Unsafety,
unsafety_id: ast::NodeId,
- fn_sig: &ty::PolyFnSig<'tcx>,
+ fn_sig: &ty::FnSig<'tcx>,
decl: &ast::FnDecl,
fn_id: ast::NodeId,
body: &ast::Block,
let tcx = ccx.tcx;
let err_count_on_creation = tcx.sess.err_count();
- // First, we have to replace any bound regions in the fn type with free ones.
- // The free region references will be bound the node_id of the body block.
- let fn_sig = liberate_late_bound_regions(tcx, CodeExtent::from_node_id(body.id), fn_sig);
-
let arg_tys = fn_sig.inputs[];
let ret_ty = fn_sig.output;
}
}
+ // We now need to check that the signature of the impl method is
+ // compatible with that of the trait method. We do this by
+ // checking that `impl_fty <: trait_fty`.
+ //
+ // FIXME. Unfortunately, this doesn't quite work right now because
+ // associated type normalization is not integrated into subtype
+ // checks. For the comparison to be valid, we need to
+ // normalize the associated types in the impl/trait methods
+ // first. However, because function types bind regions, just
+ // calling `normalize_associated_types_in` would have no effect on
+ // any associated types appearing in the fn arguments or return
+ // type.
+
+
// Compute skolemized form of impl and trait method tys.
let impl_fty = ty::mk_bare_fn(tcx, None, tcx.mk_bare_fn(impl_m.fty.clone()));
let impl_fty = impl_fty.subst(tcx, impl_to_skol_substs);
let trait_fty = ty::mk_bare_fn(tcx, None, tcx.mk_bare_fn(trait_m.fty.clone()));
let trait_fty = trait_fty.subst(tcx, &trait_to_skol_substs);
- let trait_fty =
- assoc::normalize_associated_types_in(&infcx,
- &mut fulfillment_cx,
- impl_m_span,
- impl_m_body_id,
- &trait_fty);
-
- // Check the impl method type IM is a subtype of the trait method
- // type TM. To see why this makes sense, think of a vtable. The
- // expected type of the function pointers in the vtable is the
- // type TM of the trait method. The actual type will be the type
- // IM of the impl method. Because we know that IM <: TM, that
- // means that anywhere a TM is expected, a IM will do instead. In
- // other words, anyone expecting to call a method with the type
- // from the trait, can safely call a method with the type from the
- // impl instead.
- debug!("checking trait method for compatibility: impl ty {}, trait ty {}",
- impl_fty.repr(tcx),
- trait_fty.repr(tcx));
- match infer::mk_subty(&infcx, false, infer::MethodCompatCheck(impl_m_span),
- impl_fty, trait_fty) {
- Ok(()) => {}
- Err(ref terr) => {
+
+ let err = infcx.try(|snapshot| {
+ let origin = infer::MethodCompatCheck(impl_m_span);
+
+ let (impl_sig, _) =
+ infcx.replace_late_bound_regions_with_fresh_var(impl_m_span,
+ infer::HigherRankedType,
+ &impl_m.fty.sig);
+ let impl_sig =
+ impl_sig.subst(tcx, impl_to_skol_substs);
+ let impl_sig =
+ assoc::normalize_associated_types_in(&infcx,
+ &mut fulfillment_cx,
+ impl_m_span,
+ impl_m_body_id,
+ &impl_sig);
+ let impl_fty = ty::mk_bare_fn(tcx, None, ty::BareFnTy { unsafety: impl_m.fty.unsafety,
+ abi: impl_m.fty.abi,
+ sig: ty::Binder(impl_sig) });
+ debug!("compare_impl_method: impl_fty={}",
+ impl_fty.repr(tcx));
+
+ let (trait_sig, skol_map) =
+ infcx.skolemize_late_bound_regions(&trait_m.fty.sig, snapshot);
+ let trait_sig =
+ trait_sig.subst(tcx, &trait_to_skol_substs);
+ let trait_sig =
+ assoc::normalize_associated_types_in(&infcx,
+ &mut fulfillment_cx,
+ impl_m_span,
+ impl_m_body_id,
+ &trait_sig);
+ let trait_fty = ty::mk_bare_fn(tcx, None, ty::BareFnTy { unsafety: trait_m.fty.unsafety,
+ abi: trait_m.fty.abi,
+ sig: ty::Binder(trait_sig) });
+
+ debug!("compare_impl_method: trait_fty={}",
+ trait_fty.repr(tcx));
+
+ try!(infer::mk_subty(&infcx, false, origin, impl_fty, trait_fty));
+
+ infcx.leak_check(&skol_map, snapshot)
+ });
+
+ match err {
+ Ok(()) => { }
+ Err(terr) => {
+ debug!("checking trait method for compatibility: impl ty {}, trait ty {}",
+ impl_fty.repr(tcx),
+ trait_fty.repr(tcx));
span_err!(tcx.sess, impl_m_span, E0053,
- "method `{}` has an incompatible type for trait: {}",
- token::get_name(trait_m.name),
- ty::type_err_to_str(tcx, terr));
- ty::note_and_explain_type_err(tcx, terr);
+ "method `{}` has an incompatible type for trait: {}",
+ token::get_name(trait_m.name),
+ ty::type_err_to_str(tcx, &terr));
+ return;
}
}
/// Also returns the substitution from the type parameters on `def_id` to the fresh variables.
/// Registers any trait obligations specified on `def_id` at the same time.
///
- /// Note that function is only intended to be used with types (notably, not impls). This is
+ /// Note that function is only intended to be used with types (notably, not fns). This is
/// because it doesn't do any instantiation of late-bound regions.
pub fn instantiate_type(&self,
span: Span,
}
};
- // Replace any bound regions that appear in the function
- // signature with region variables
+ // Replace any late-bound regions that appear in the function
+ // signature with region variables. We also have to
+ // renormalize the associated types at this point, since they
+ // previously appeared within a `Binder<>` and hence would not
+ // have been normalized before.
let fn_sig =
fcx.infcx().replace_late_bound_regions_with_fresh_var(call_expr.span,
infer::FnCall,
fn_sig).0;
+ let fn_sig =
+ fcx.normalize_associated_types_in(call_expr.span,
+ &fn_sig);
// Call the generic checker.
check_argument_types(fcx,
ty::ty_enum(def_id, substs) |
ty::ty_struct(def_id, substs) => {
- self.accumulate_from_adt(ty, def_id, substs)
+ let item_scheme = ty::lookup_item_type(self.tcx, def_id);
+ self.accumulate_from_adt(ty, def_id, &item_scheme.generics, substs)
}
ty::ty_vec(t, _) |
// TODO What region constraints are necessary here, if any??
// this seems like a minimal requirement:
- self.accumulate_from_ty(data.trait_ref.self_ty());
+ let trait_def = ty::lookup_trait_def(self.tcx, data.trait_ref.def_id);
+ self.accumulate_from_adt(ty, data.trait_ref.def_id,
+ &trait_def.generics, &data.trait_ref.substs)
}
ty::ty_tup(ref tuptys) => {
fn accumulate_from_adt(&mut self,
ty: Ty<'tcx>,
def_id: ast::DefId,
+ generics: &ty::Generics<'tcx>,
substs: &Substs<'tcx>)
{
// The generic declarations from the type, appropriately
// substituted for the actual substitutions.
- let generics =
- ty::lookup_item_type(self.tcx, def_id)
- .generics
- .subst(self.tcx, substs);
+ let generics = generics.subst(self.tcx, substs);
// Variance of each type/region parameter.
let variances = ty::item_variances(self.tcx, def_id);
//
// (I believe we should do the same for traits, but
// that will require an RFC. -nmatsakis)
- let bounds = type_scheme
-.generics.to_bounds(self.tcx(), substs);
+ let bounds = type_scheme.generics.to_bounds(self.tcx(), substs);
let bounds = filter_to_trait_obligations(bounds);
self.fcx.add_obligations_for_parameters(
traits::ObligationCause::new(self.span,
fn visit_path_parameters(&mut self, path_span: Span, path_parameters: &'v PathParameters) {
walk_path_parameters(self, path_span, path_parameters)
}
+ fn visit_assoc_type_binding(&mut self, type_binding: &'v TypeBinding) {
+ walk_assoc_type_binding(self, type_binding)
+ }
fn visit_attribute(&mut self, _attr: &'v Attribute) {}
}
for lifetime in data.lifetimes.iter() {
visitor.visit_lifetime_ref(lifetime);
}
+ for binding in data.bindings.iter() {
+ visitor.visit_assoc_type_binding(&**binding);
+ }
}
ast::ParenthesizedParameters(ref data) => {
for typ in data.inputs.iter() {
}
}
+pub fn walk_assoc_type_binding<'v, V: Visitor<'v>>(visitor: &mut V,
+ type_binding: &'v TypeBinding) {
+ visitor.visit_ident(type_binding.span, type_binding.ident);
+ visitor.visit_ty(&*type_binding.ty);
+}
+
pub fn walk_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v Pat) {
match pattern.node {
PatEnum(ref path, ref children) => {