From 5fe0ad1c0fde4c93ec010e95457d5831d11f013d Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 10 Dec 2014 10:59:20 -0500 Subject: [PATCH] Implement `unsafe trait` semantics. --- src/librustc_typeck/coherence/mod.rs | 2 + src/librustc_typeck/coherence/unsafety.rs | 77 +++++++++++++++++++ src/test/compile-fail/trait-safety-fn-body.rs | 25 ++++++ .../trait-safety-inherent-impl.rs | 19 +++++ .../compile-fail/trait-safety-trait-impl.rs | 28 +++++++ 5 files changed, 151 insertions(+) create mode 100644 src/librustc_typeck/coherence/unsafety.rs create mode 100644 src/test/compile-fail/trait-safety-fn-body.rs create mode 100644 src/test/compile-fail/trait-safety-inherent-impl.rs create mode 100644 src/test/compile-fail/trait-safety-trait-impl.rs diff --git a/src/librustc_typeck/coherence/mod.rs b/src/librustc_typeck/coherence/mod.rs index 7bc79d6e4a4..a55f3c61919 100644 --- a/src/librustc_typeck/coherence/mod.rs +++ b/src/librustc_typeck/coherence/mod.rs @@ -50,6 +50,7 @@ use util::ppaux::Repr; mod orphan; mod overlap; +mod unsafety; fn get_base_type<'a, 'tcx>(inference_context: &InferCtxt<'a, 'tcx>, span: Span, @@ -620,6 +621,7 @@ pub fn check_coherence(crate_context: &CrateCtxt) { inference_context: new_infer_ctxt(crate_context.tcx), inherent_impls: RefCell::new(FnvHashMap::new()), }.check(crate_context.tcx.map.krate()); + unsafety::check(crate_context.tcx); orphan::check(crate_context.tcx); overlap::check(crate_context.tcx); } diff --git a/src/librustc_typeck/coherence/unsafety.rs b/src/librustc_typeck/coherence/unsafety.rs new file mode 100644 index 00000000000..07a84846c47 --- /dev/null +++ b/src/librustc_typeck/coherence/unsafety.rs @@ -0,0 +1,77 @@ +// 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. + +//! Unsafety checker: every impl either implements a trait defined in this +//! crate or pertains to a type defined in this crate. + +use middle::ty; +use syntax::ast::{Item, ItemImpl}; +use syntax::ast; +use syntax::ast_util; +use syntax::visit; +use util::ppaux::UserString; + +pub fn check(tcx: &ty::ctxt) { + let mut orphan = UnsafetyChecker { tcx: tcx }; + visit::walk_crate(&mut orphan, tcx.map.krate()); +} + +struct UnsafetyChecker<'cx, 'tcx:'cx> { + tcx: &'cx ty::ctxt<'tcx> +} + +impl<'cx, 'tcx,'v> visit::Visitor<'v> for UnsafetyChecker<'cx, 'tcx> { + fn visit_item(&mut self, item: &'v ast::Item) { + match item.node { + ast::ItemImpl(unsafety, _, _, _, _) => { + match ty::impl_trait_ref(self.tcx, ast_util::local_def(item.id)) { + None => { + // Inherent impl. + match unsafety { + ast::Unsafety::Normal => { /* OK */ } + ast::Unsafety::Unsafe => { + self.tcx.sess.span_err( + item.span, + "inherent impls cannot be declared as unsafe"); + } + } + } + + Some(trait_ref) => { + let trait_def = ty::lookup_trait_def(self.tcx, trait_ref.def_id); + match (trait_def.unsafety, unsafety) { + (ast::Unsafety::Normal, ast::Unsafety::Unsafe) => { + self.tcx.sess.span_err( + item.span, + format!("implementing the trait `{}` is not unsafe", + trait_ref.user_string(self.tcx)).as_slice()); + } + + (ast::Unsafety::Unsafe, ast::Unsafety::Normal) => { + self.tcx.sess.span_err( + item.span, + format!("the trait `{}` requires an `unsafe impl` declaration", + trait_ref.user_string(self.tcx)).as_slice()); + } + + (ast::Unsafety::Unsafe, ast::Unsafety::Unsafe) | + (ast::Unsafety::Normal, ast::Unsafety::Normal) => { + /* OK */ + } + } + } + } + } + _ => { } + } + + visit::walk_item(self, item); + } +} diff --git a/src/test/compile-fail/trait-safety-fn-body.rs b/src/test/compile-fail/trait-safety-fn-body.rs new file mode 100644 index 00000000000..d174092e4d0 --- /dev/null +++ b/src/test/compile-fail/trait-safety-fn-body.rs @@ -0,0 +1,25 @@ +// 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. + +// Check that an unsafe impl does not imply that unsafe actions are +// legal in the methods. + +unsafe trait UnsafeTrait { + fn foo(self) { } +} + +unsafe impl UnsafeTrait for *mut int { + fn foo(self) { + // Unsafe actions are not made legal by taking place in an unsafe trait: + *self += 1; //~ ERROR E0133 + } +} + +fn main() { } diff --git a/src/test/compile-fail/trait-safety-inherent-impl.rs b/src/test/compile-fail/trait-safety-inherent-impl.rs new file mode 100644 index 00000000000..285d4c1ba8d --- /dev/null +++ b/src/test/compile-fail/trait-safety-inherent-impl.rs @@ -0,0 +1,19 @@ +// 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. + +// Check that inherent impls cannot be unsafe. + +struct SomeStruct; + +unsafe impl SomeStruct { //~ ERROR inherent impls cannot be declared as unsafe + fn foo(self) { } +} + +fn main() { } diff --git a/src/test/compile-fail/trait-safety-trait-impl.rs b/src/test/compile-fail/trait-safety-trait-impl.rs new file mode 100644 index 00000000000..1bd6d763607 --- /dev/null +++ b/src/test/compile-fail/trait-safety-trait-impl.rs @@ -0,0 +1,28 @@ +// 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. + +// Check that unsafe traits require unsafe impls and that inherent +// impls cannot be unsafe. + +trait SafeTrait { + fn foo(self) { } +} + +unsafe trait UnsafeTrait { + fn foo(self) { } +} + +unsafe impl UnsafeTrait for u8 { } // OK + +impl UnsafeTrait for u16 { } //~ ERROR requires an `unsafe impl` declaration + +unsafe impl SafeTrait for u32 { } //~ ERROR the trait `SafeTrait` is not unsafe + +fn main() { }