diff options
| -rw-r--r-- | src/librustc_typeck/check/vtable.rs | 103 |
1 files changed, 98 insertions, 5 deletions
diff --git a/src/librustc_typeck/check/vtable.rs b/src/librustc_typeck/check/vtable.rs index 5cf759a4dce..e8bd5c088fc 100644 --- a/src/librustc_typeck/check/vtable.rs +++ b/src/librustc_typeck/check/vtable.rs @@ -9,7 +9,7 @@ // except according to those terms. use check::{FnCtxt, structurally_resolved_type}; -use middle::subst::{FnSpace}; +use middle::subst::{FnSpace, SelfSpace}; use middle::traits; use middle::traits::{Obligation, ObligationCause}; use middle::traits::report_fulfillment_errors; @@ -141,15 +141,15 @@ pub fn check_object_safety<'tcx>(tcx: &ty::ctxt<'tcx>, } fn check_object_safety_inner<'tcx>(tcx: &ty::ctxt<'tcx>, - object_trait: &ty::PolyTraitRef<'tcx>, - span: Span) { + object_trait: &ty::PolyTraitRef<'tcx>, + span: Span) { let trait_items = ty::trait_items(tcx, object_trait.def_id()); let mut errors = Vec::new(); for item in trait_items.iter() { match *item { ty::MethodTraitItem(ref m) => { - errors.push(check_object_safety_of_method(tcx, &**m)) + errors.push(check_object_safety_of_method(tcx, object_trait, &**m)) } ty::TypeTraitItem(_) => {} } @@ -173,6 +173,7 @@ fn check_object_safety_inner<'tcx>(tcx: &ty::ctxt<'tcx>, /// type is not known (that's the whole point of a trait instance, after all, to obscure the /// self type) and (b) the call must go through a vtable and hence cannot be monomorphized. fn check_object_safety_of_method<'tcx>(tcx: &ty::ctxt<'tcx>, + object_trait: &ty::PolyTraitRef<'tcx>, method: &ty::Method<'tcx>) -> Vec<String> { let mut msgs = Vec::new(); @@ -196,7 +197,7 @@ fn check_object_safety_inner<'tcx>(tcx: &ty::ctxt<'tcx>, // reason (a) above let check_for_self_ty = |ty| { - if ty::type_has_self(ty) { + if contains_illegal_self_type_reference(tcx, object_trait.def_id(), ty) { Some(format!( "cannot call a method (`{}`) whose type contains \ a self-type (`{}`) through a trait object", @@ -225,6 +226,98 @@ fn check_object_safety_inner<'tcx>(tcx: &ty::ctxt<'tcx>, msgs } + + fn contains_illegal_self_type_reference<'tcx>(tcx: &ty::ctxt<'tcx>, + trait_def_id: ast::DefId, + ty: Ty<'tcx>) + -> bool + { + // This is somewhat subtle. In general, we want to forbid + // references to `Self` in the argument and return types, + // since the value of `Self` is erased. However, there is one + // exception: it is ok to reference `Self` in order to access + // an associated type of the current trait, since we retain + // the value of those associated types in the object type + // itself. + // + // ```rust + // trait SuperTrait { + // type X; + // } + // + // trait Trait : SuperTrait { + // type Y; + // fn foo(&self, x: Self) // bad + // fn foo(&self) -> Self // bad + // fn foo(&self) -> Option<Self> // bad + // fn foo(&self) -> Self::Y // OK, desugars to next example + // fn foo(&self) -> <Self as Trait>::Y // OK + // fn foo(&self) -> Self::X // OK, desugars to next example + // fn foo(&self) -> <Self as SuperTrait>::X // OK + // } + // ``` + // + // However, it is not as simple as allowing `Self` in a projected + // type, because there are illegal ways to use `Self` as well: + // + // ```rust + // trait Trait : SuperTrait { + // ... + // fn foo(&self) -> <Self as SomeOtherTrait>::X; + // } + // ``` + // + // Here we will not have the type of `X` recorded in the + // object type, and we cannot resolve `Self as SomeOtherTrait` + // without knowing what `Self` is. + + let mut supertraits: Option<Vec<ty::PolyTraitRef<'tcx>>> = None; + let mut error = false; + ty::maybe_walk_ty(ty, |ty| { + match ty.sty { + ty::ty_param(ref param_ty) => { + if param_ty.space == SelfSpace { + error = true; + } + + false // no contained types to walk + } + + ty::ty_projection(ref data) => { + // This is a projected type `<Foo as SomeTrait>::X`. + + // Compute supertraits of current trait lazilly. + if supertraits.is_none() { + let trait_def = ty::lookup_trait_def(tcx, trait_def_id); + let trait_ref = ty::Binder(trait_def.trait_ref.clone()); + supertraits = Some(traits::supertraits(tcx, trait_ref).collect()); + } + + // Determine whether the trait reference `Foo as + // SomeTrait` is in fact a supertrait of the + // current trait. In that case, this type is + // legal, because the type `X` will be specified + // in the object type. Note that we can just use + // direct equality here because all of these types + // are part of the formal parameter listing, and + // hence there should be no inference variables. + let projection_trait_ref = ty::Binder(data.trait_ref.clone()); + let is_supertrait_of_current_trait = + supertraits.as_ref().unwrap().contains(&projection_trait_ref); + + if is_supertrait_of_current_trait { + false // do not walk contained types, do not report error, do collect $200 + } else { + true // DO walk contained types, POSSIBLY reporting an error + } + } + + _ => true, // walk contained types, if any + } + }); + + error + } } pub fn register_object_cast_obligations<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, |
