From 57aaa9bf87a233753d34fb19e223ed82b42f95a3 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 5 Jan 2015 06:08:03 -0500 Subject: [PATCH] Make supertrait references work in object types too. --- src/librustc_typeck/astconv.rs | 56 +++++++++++++++---- ...ociated-type-doubleendediterator-object.rs | 27 +++++++++ 2 files changed, 71 insertions(+), 12 deletions(-) create mode 100644 src/test/run-pass/associated-type-doubleendediterator-object.rs diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs index e216338b1e3a..c616f4feaff0 100644 --- a/src/librustc_typeck/astconv.rs +++ b/src/librustc_typeck/astconv.rs @@ -627,7 +627,8 @@ fn ast_path_to_trait_ref<'a,'tcx>( } Some(ref mut v) => { for binding in assoc_bindings.iter() { - match ast_type_binding_to_projection_predicate(this, trait_ref.clone(), binding) { + match ast_type_binding_to_projection_predicate(this, trait_ref.clone(), + self_ty, binding) { Ok(pp) => { v.push(pp); } Err(ErrorReported) => { } } @@ -640,10 +641,13 @@ fn ast_path_to_trait_ref<'a,'tcx>( fn ast_type_binding_to_projection_predicate<'tcx>( this: &AstConv<'tcx>, - trait_ref: Rc>, + mut trait_ref: Rc>, + self_ty: Option>, binding: &ConvertedBinding<'tcx>) -> Result, ErrorReported> { + let tcx = this.tcx(); + // Given something like `U : SomeTrait`, we want to produce a // predicate like `::T = X`. This is somewhat // subtle in the event that `T` is defined in a supertrait of @@ -671,39 +675,67 @@ fn ast_type_binding_to_projection_predicate<'tcx>( }); } - // Otherwise, we have to walk through the supertraits to find those that do. - let mut candidates: Vec<_> = - traits::supertraits(this.tcx(), trait_ref.to_poly_trait_ref()) + // Otherwise, we have to walk through the supertraits to find + // those that do. This is complicated by the fact that, for an + // object type, the `Self` type is not present in the + // substitutions (after all, it's being constructed right now), + // but the `supertraits` iterator really wants one. To handle + // this, we currently insert a dummy type and then remove it + // later. Yuck. + + let dummy_self_ty = ty::mk_infer(tcx, ty::FreshTy(0)); + if self_ty.is_none() { // if converting for an object type + let mut dummy_substs = trait_ref.substs.clone(); + assert!(dummy_substs.self_ty().is_none()); + dummy_substs.types.push(SelfSpace, dummy_self_ty); + trait_ref = Rc::new(ty::TraitRef::new(trait_ref.def_id, + tcx.mk_substs(dummy_substs))); + } + + let mut candidates: Vec = + traits::supertraits(tcx, trait_ref.to_poly_trait_ref()) .filter(|r| trait_defines_associated_type_named(this, r.def_id(), binding.item_name)) .collect(); + // If converting for an object type, then remove the dummy-ty from `Self` now. + // Yuckety yuck. + if self_ty.is_none() { + for candidate in candidates.iter_mut() { + let mut dummy_substs = candidate.0.substs.clone(); + assert!(dummy_substs.self_ty() == Some(dummy_self_ty)); + dummy_substs.types.pop(SelfSpace); + *candidate = ty::Binder(Rc::new(ty::TraitRef::new(candidate.def_id(), + tcx.mk_substs(dummy_substs)))); + } + } + if candidates.len() > 1 { - this.tcx().sess.span_err( + tcx.sess.span_err( binding.span, format!("ambiguous associated type: `{}` defined in multiple supertraits `{}`", token::get_name(binding.item_name), - candidates.user_string(this.tcx())).as_slice()); + candidates.user_string(tcx)).as_slice()); return Err(ErrorReported); } let candidate = match candidates.pop() { Some(c) => c, None => { - this.tcx().sess.span_err( + tcx.sess.span_err( binding.span, format!("no associated type `{}` defined in `{}`", token::get_name(binding.item_name), - trait_ref.user_string(this.tcx())).as_slice()); + trait_ref.user_string(tcx)).as_slice()); return Err(ErrorReported); } }; - if ty::binds_late_bound_regions(this.tcx(), &candidate) { - this.tcx().sess.span_err( + if ty::binds_late_bound_regions(tcx, &candidate) { + tcx.sess.span_err( binding.span, format!("associated type `{}` defined in higher-ranked supertrait `{}`", token::get_name(binding.item_name), - candidate.user_string(this.tcx())).as_slice()); + candidate.user_string(tcx)).as_slice()); return Err(ErrorReported); } diff --git a/src/test/run-pass/associated-type-doubleendediterator-object.rs b/src/test/run-pass/associated-type-doubleendediterator-object.rs new file mode 100644 index 000000000000..429027cbf304 --- /dev/null +++ b/src/test/run-pass/associated-type-doubleendediterator-object.rs @@ -0,0 +1,27 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn pairwise_sub(mut t: Box>) -> int { + let mut result = 0; + loop { + let front = t.next(); + let back = t.next_back(); + match (front, back) { + (Some(f), Some(b)) => { result += b - f; } + _ => { return result; } + } + } +} + +fn main() { + let v = vec!(1, 2, 3, 4, 5, 6); + let r = pairwise_sub(box v.into_iter()); + assert_eq!(r, 9); +}