diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index a0f3f2734d976..cdf34c7f4d2f3 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -1747,8 +1747,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.register_unsize_obligations(span, &**u) } ty::UnsizeVtable(ref ty_trait, self_ty) => { - vtable::check_object_safety(self.tcx(), ty_trait, span); - + vtable::check_object_safety(self.tcx(), &ty_trait.principal, span); // If the type is `Foo+'a`, ensures that the type // being cast to `Foo+'a` implements `Foo`: vtable::register_object_cast_obligations(self, diff --git a/src/librustc_typeck/check/vtable.rs b/src/librustc_typeck/check/vtable.rs index c2b263885bd73..b9f7eb3f271b7 100644 --- a/src/librustc_typeck/check/vtable.rs +++ b/src/librustc_typeck/check/vtable.rs @@ -45,7 +45,7 @@ pub fn check_object_cast<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, // Ensure that if ~T is cast to ~Trait, then T : Trait push_cast_obligation(fcx, cast_expr, object_trait, referent_ty); - check_object_safety(fcx.tcx(), object_trait, source_expr.span); + check_object_safety(fcx.tcx(), &object_trait.principal, source_expr.span); } (&ty::ty_rptr(referent_region, ty::mt { ty: referent_ty, @@ -69,7 +69,7 @@ pub fn check_object_cast<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, target_region, referent_region); - check_object_safety(fcx.tcx(), object_trait, source_expr.span); + check_object_safety(fcx.tcx(), &object_trait.principal, source_expr.span); } } @@ -133,17 +133,32 @@ pub fn check_object_cast<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, // self by value, has no type parameters and does not use the `Self` type, except // in self position. pub fn check_object_safety<'tcx>(tcx: &ty::ctxt<'tcx>, - object_trait: &ty::TyTrait<'tcx>, + object_trait: &ty::TraitRef<'tcx>, + span: Span) { + + let mut object = object_trait.clone(); + if object.substs.types.len(SelfSpace) == 0 { + object.substs.types.push(SelfSpace, ty::mk_err()); + } + + let object = Rc::new(object); + for tr in traits::supertraits(tcx, object) { + check_object_safety_inner(tcx, &*tr, span); + } +} + +fn check_object_safety_inner<'tcx>(tcx: &ty::ctxt<'tcx>, + object_trait: &ty::TraitRef<'tcx>, span: Span) { // Skip the fn_once lang item trait since only the compiler should call // `call_once` which is the method which takes self by value. What could go // wrong? match tcx.lang_items.fn_once_trait() { - Some(def_id) if def_id == object_trait.principal.def_id => return, + Some(def_id) if def_id == object_trait.def_id => return, _ => {} } - let trait_items = ty::trait_items(tcx, object_trait.principal.def_id); + let trait_items = ty::trait_items(tcx, object_trait.def_id); let mut errors = Vec::new(); for item in trait_items.iter() { @@ -157,7 +172,7 @@ pub fn check_object_safety<'tcx>(tcx: &ty::ctxt<'tcx>, let mut errors = errors.iter().flat_map(|x| x.iter()).peekable(); if errors.peek().is_some() { - let trait_name = ty::item_path_str(tcx, object_trait.principal.def_id); + let trait_name = ty::item_path_str(tcx, object_trait.def_id); span_err!(tcx.sess, span, E0038, "cannot convert to a trait object because trait `{}` is not object-safe", trait_name); diff --git a/src/test/compile-fail/issue-18959.rs b/src/test/compile-fail/issue-18959.rs new file mode 100644 index 0000000000000..3d126790335a8 --- /dev/null +++ b/src/test/compile-fail/issue-18959.rs @@ -0,0 +1,26 @@ +// 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. + +pub trait Foo for Sized? { fn foo(&self, ext_thing: &T); } +pub trait Bar for Sized?: Foo { } +impl Bar for T { } + +pub struct Thing; +impl Foo for Thing { + fn foo(&self, _: &T) {} +} + +#[inline(never)] fn foo(b: &Bar) { b.foo(&0u) } + +fn main() { + let mut thing = Thing; + let test: &Bar = &mut thing; //~ ERROR cannot convert to a trait object because trait `Foo` + foo(test); +}