diff --git a/src/librustc/middle/traits/fulfill.rs b/src/librustc/middle/traits/fulfill.rs index 0d2023ead4fcc5aadbaad6b62a8c70b2ed983575..e72d5b405c4e4dae1117e1773ec4990955071d6c 100644 --- a/src/librustc/middle/traits/fulfill.rs +++ b/src/librustc/middle/traits/fulfill.rs @@ -109,6 +109,10 @@ pub fn normalize_associated_type<'a>(&mut self, 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(); @@ -120,6 +124,9 @@ pub fn normalize_associated_type<'a>(&mut self, }); 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 } diff --git a/src/librustc/middle/traits/select.rs b/src/librustc/middle/traits/select.rs index 2e4bdadb8c231644662649895204e7a69b03999b..9301531e84d5e1b2e22bbede4b3806c0a01d3057 100644 --- a/src/librustc/middle/traits/select.rs +++ b/src/librustc/middle/traits/select.rs @@ -1141,7 +1141,11 @@ fn builtin_bound(&mut self, obligation: &TraitObligation<'tcx>) -> Result,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(_)) | @@ -1627,13 +1631,31 @@ fn vtable_builtin_data(&mut self, -> VtableBuiltinData> { 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::>(); let mut obligations = match obligations { Ok(o) => o, diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 6225a0f3fba18607ce877559b25f347ab1010fa9..583d2ce3b67c238d30477587087525125d5f7bab 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -6903,8 +6903,19 @@ fn has_regions_escaping_depth(&self, depth: u32) -> bool { 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)) + } + } } } @@ -6921,7 +6932,7 @@ fn has_regions_escaping_depth(&self, depth: u32) -> bool { } 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) } } @@ -6933,14 +6944,14 @@ fn has_regions_escaping_depth(&self, depth: u32) -> bool { } 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) } } diff --git a/src/librustc_typeck/check/assoc.rs b/src/librustc_typeck/check/assoc.rs index e737bb1b237fb87dd48a7c6b3ab99ab5b0eb9579..b59f9291e4fc66dc8f2095aea8057f2fbad0b6a9 100644 --- a/src/librustc_typeck/check/assoc.rs +++ b/src/librustc_typeck/check/assoc.rs @@ -33,7 +33,8 @@ pub fn normalize_associated_types_in<'a,'tcx,T>(infcx: &InferCtxt<'a,'tcx>, 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) } @@ -42,6 +43,7 @@ struct AssociatedTypeNormalizer<'a,'tcx:'a> { fulfillment_cx: &'a mut FulfillmentContext<'tcx>, span: Span, body_id: ast::NodeId, + region_binders: uint, } impl<'a,'tcx> TypeFolder<'tcx> for AssociatedTypeNormalizer<'a,'tcx> { @@ -49,9 +51,29 @@ fn tcx(&self) -> &ty::ctxt<'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(>::A) + // + // Instead of normalizing `>::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, diff --git a/src/librustc_typeck/check/closure.rs b/src/librustc_typeck/check/closure.rs index 19108ca710bc1a3153f3877794cfe81329aa1140..340cc9b355c0252043b9c8bfb859c0e8c2d92ad6 100644 --- a/src/librustc_typeck/check/closure.rs +++ b/src/librustc_typeck/check/closure.rs @@ -14,6 +14,7 @@ use astconv; use middle::infer; +use middle::region::CodeExtent; use middle::subst; use middle::ty::{mod, ToPolyTraitRef, Ty}; use rscope::RegionScope; @@ -132,10 +133,13 @@ fn check_unboxed_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, 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, @@ -310,7 +314,7 @@ fn check_boxed_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, 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)); @@ -325,10 +329,13 @@ fn check_boxed_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, 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, diff --git a/src/librustc_typeck/check/method/confirm.rs b/src/librustc_typeck/check/method/confirm.rs index de6824fa5ddd1e822a5dede46b6fc8ba7c58c0dd..1aa9adaf9ec4d4422fe3ae65601375fbdf602a50 100644 --- a/src/librustc_typeck/check/method/confirm.rs +++ b/src/librustc_typeck/check/method/confirm.rs @@ -424,17 +424,18 @@ fn instantiate_method_sig(&mut self, 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 { diff --git a/src/librustc_typeck/check/method/mod.rs b/src/librustc_typeck/check/method/mod.rs index cb71fbce8ac4cf22b179d514981e098da5130503..5b6acdbf3ada5fff99b347b0ea71aa77bd852b5e 100644 --- a/src/librustc_typeck/check/method/mod.rs +++ b/src/librustc_typeck/check/method/mod.rs @@ -190,19 +190,21 @@ pub fn lookup_in_trait_adjusted<'a, 'tcx>(fcx: &'a FnCtxt<'a, 'tcx>, 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={}", diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 139ef010d212424b9ddaa4a7d53ff53669090c97..e485e49148fe4831a0ed9416a582e1eab9dc4875 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -432,13 +432,15 @@ fn check_bare_fn<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, 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); @@ -542,7 +544,7 @@ fn visit_item(&mut self, _: &ast::Item) { } 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, @@ -552,10 +554,6 @@ fn check_fn<'a, 'tcx>(ccx: &'a CrateCtxt<'a, 'tcx>, 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; @@ -1161,39 +1159,80 @@ fn compare_impl_method<'tcx>(tcx: &ty::ctxt<'tcx>, } } + // 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; } } @@ -1791,7 +1830,7 @@ fn register_unsize_obligations(&self, /// 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, @@ -3061,12 +3100,18 @@ fn check_call(fcx: &FnCtxt, } }; - // 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, diff --git a/src/librustc_typeck/check/regionmanip.rs b/src/librustc_typeck/check/regionmanip.rs index 14405b9a92b59c0d199406e3120fde12a3e1a300..c0b281450a2c0bb2956c95ff918fbb1ab09b410e 100644 --- a/src/librustc_typeck/check/regionmanip.rs +++ b/src/librustc_typeck/check/regionmanip.rs @@ -104,7 +104,8 @@ fn accumulate_from_ty(&mut self, ty: Ty<'tcx>) { 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, _) | @@ -127,7 +128,9 @@ fn accumulate_from_ty(&mut self, ty: Ty<'tcx>) { // 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) => { @@ -222,14 +225,12 @@ fn push_param_constraint(&mut self, 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); diff --git a/src/librustc_typeck/check/wf.rs b/src/librustc_typeck/check/wf.rs index 21c81c7be9bfac65c5706f1b852ae76be4cd0120..2a3f528809cfd883776e85f39b997dfe95a5ce3f 100644 --- a/src/librustc_typeck/check/wf.rs +++ b/src/librustc_typeck/check/wf.rs @@ -349,8 +349,7 @@ fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { // // (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, diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index 1cd21ccac7a0ef41b7d757b3ee2cb51b65333c0f..40ca6354ca6d14d114378799746d40b2de55527c 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -133,6 +133,9 @@ fn visit_path_segment(&mut self, path_span: Span, path_segment: &'v PathSegment) 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) {} } @@ -467,6 +470,9 @@ pub fn walk_path_parameters<'v, V: Visitor<'v>>(visitor: &mut V, 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() { @@ -479,6 +485,12 @@ pub fn walk_path_parameters<'v, V: Visitor<'v>>(visitor: &mut V, } } +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) => {