]> git.lizzy.rs Git - rust.git/commitdiff
rustc: check supertraits for object safety
authorCorey Richardson <corey@octayn.net>
Thu, 27 Nov 2014 16:28:51 +0000 (11:28 -0500)
committerCorey Richardson <corey@octayn.net>
Sat, 6 Dec 2014 06:27:21 +0000 (22:27 -0800)
Closes #18959

Technically, this causes code that once compiled to no longer compile, but
that code probably never ran.

[breaking-change]

src/librustc_typeck/check/mod.rs
src/librustc_typeck/check/vtable.rs
src/test/compile-fail/issue-18959.rs [new file with mode: 0644]

index a0f3f2734d97640805224293112872d3e17c8a24..cdf34c7f4d2f3c8dc845d84053407341581d2bbb 100644 (file)
@@ -1747,8 +1747,7 @@ fn register_unsize_obligations(&self,
                 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,
index c2b263885bd73bcd69f6c118dcb7e774bbb2ae60..b9f7eb3f271b7b2bcc4aa28ad82938218b95c3ff 100644 (file)
@@ -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 @@ fn push_cast_obligation<'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 (file)
index 0000000..3d12679
--- /dev/null
@@ -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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+pub trait Foo for Sized? { fn foo<T>(&self, ext_thing: &T); }
+pub trait Bar for Sized?: Foo { }
+impl<T: Foo> Bar for T { }
+
+pub struct Thing;
+impl Foo for Thing {
+    fn foo<T>(&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);
+}