diff --git a/src/librustc/dep_graph/dep_node.rs b/src/librustc/dep_graph/dep_node.rs index b492caf10bb0..7a78765365db 100644 --- a/src/librustc/dep_graph/dep_node.rs +++ b/src/librustc/dep_graph/dep_node.rs @@ -445,6 +445,7 @@ define_dep_nodes!( <'tcx> [] BorrowCheckKrate, [] BorrowCheck(DefId), [] MirBorrowCheck(DefId), + [] UnsafetyViolations(DefId), [] RvalueCheck(DefId), [] Reachability, diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs index 6e0f49bba90f..6b79f0cde1a8 100644 --- a/src/librustc/diagnostics.rs +++ b/src/librustc/diagnostics.rs @@ -479,40 +479,6 @@ fn main() { ``` "##, -E0133: r##" -Unsafe code was used outside of an unsafe function or block. - -Erroneous code example: - -```compile_fail,E0133 -unsafe fn f() { return; } // This is the unsafe code - -fn main() { - f(); // error: call to unsafe function requires unsafe function or block -} -``` - -Using unsafe functionality is potentially dangerous and disallowed by safety -checks. Examples: - -* Dereferencing raw pointers -* Calling functions via FFI -* Calling functions marked unsafe - -These safety checks can be relaxed for a section of the code by wrapping the -unsafe instructions with an `unsafe` block. For instance: - -``` -unsafe fn f() { return; } - -fn main() { - unsafe { f(); } // ok! -} -``` - -See also https://doc.rust-lang.org/book/first-edition/unsafe.html -"##, - // This shouldn't really ever trigger since the repeated value error comes first E0136: r##" A binary can only have one entry point, and by default that entry point is the diff --git a/src/librustc/ich/impls_mir.rs b/src/librustc/ich/impls_mir.rs index 068830d688c3..4bda89690b7a 100644 --- a/src/librustc/ich/impls_mir.rs +++ b/src/librustc/ich/impls_mir.rs @@ -33,6 +33,7 @@ impl_stable_hash_for!(struct mir::LocalDecl<'tcx> { }); impl_stable_hash_for!(struct mir::UpvarDecl { debug_name, by_ref }); impl_stable_hash_for!(struct mir::BasicBlockData<'tcx> { statements, terminator, is_cleanup }); +impl_stable_hash_for!(struct mir::UnsafetyViolation { source_info, description, lint_node_id }); impl<'gcx> HashStable> for mir::Terminator<'gcx> { @@ -364,7 +365,26 @@ for mir::ProjectionElem<'gcx, V, T> } impl_stable_hash_for!(struct mir::VisibilityScopeData { span, parent_scope }); -impl_stable_hash_for!(struct mir::VisibilityScopeInfo { lint_root }); +impl_stable_hash_for!(struct mir::VisibilityScopeInfo { + lint_root, safety +}); + +impl<'gcx> HashStable> for mir::Safety { + fn hash_stable(&self, + hcx: &mut StableHashingContext<'gcx>, + hasher: &mut StableHasher) { + mem::discriminant(self).hash_stable(hcx, hasher); + + match *self { + mir::Safety::Safe | + mir::Safety::BuiltinUnsafe | + mir::Safety::FnUnsafe => {} + mir::Safety::ExplicitUnsafe(node_id) => { + node_id.hash_stable(hcx, hasher); + } + } + } +} impl<'gcx> HashStable> for mir::Operand<'gcx> { fn hash_stable(&self, diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index cd39ef709463..1e90aa47267f 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -111,7 +111,6 @@ pub mod middle { pub mod dataflow; pub mod dead; pub mod dependency_format; - pub mod effect; pub mod entry; pub mod exported_symbols; pub mod free_region; diff --git a/src/librustc/middle/effect.rs b/src/librustc/middle/effect.rs deleted file mode 100644 index 7290353e48b0..000000000000 --- a/src/librustc/middle/effect.rs +++ /dev/null @@ -1,316 +0,0 @@ -// Copyright 2012-2013 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. - -//! Enforces the Rust effect system. Currently there is just one effect, -//! `unsafe`. -use self::RootUnsafeContext::*; - -use ty::{self, TyCtxt}; -use lint; -use lint::builtin::UNUSED_UNSAFE; - -use hir::def::Def; -use hir::intravisit::{self, FnKind, Visitor, NestedVisitorMap}; -use hir::{self, PatKind}; -use syntax::ast; -use syntax_pos::Span; -use util::nodemap::FxHashSet; - -#[derive(Copy, Clone)] -struct UnsafeContext { - push_unsafe_count: usize, - root: RootUnsafeContext, -} - -impl UnsafeContext { - fn new(root: RootUnsafeContext) -> UnsafeContext { - UnsafeContext { root: root, push_unsafe_count: 0 } - } -} - -#[derive(Copy, Clone, PartialEq)] -enum RootUnsafeContext { - SafeContext, - UnsafeFn, - UnsafeBlock(ast::NodeId), -} - -struct EffectCheckVisitor<'a, 'tcx: 'a> { - tcx: TyCtxt<'a, 'tcx, 'tcx>, - tables: &'a ty::TypeckTables<'tcx>, - body_id: hir::BodyId, - - /// Whether we're in an unsafe context. - unsafe_context: UnsafeContext, - used_unsafe: FxHashSet, -} - -impl<'a, 'tcx> EffectCheckVisitor<'a, 'tcx> { - fn require_unsafe_ext(&mut self, node_id: ast::NodeId, span: Span, - description: &str, is_lint: bool) { - if self.unsafe_context.push_unsafe_count > 0 { return; } - match self.unsafe_context.root { - SafeContext => { - if is_lint { - self.tcx.lint_node(lint::builtin::SAFE_EXTERN_STATICS, - node_id, - span, - &format!("{} requires unsafe function or \ - block (error E0133)", description)); - } else { - // Report an error. - struct_span_err!( - self.tcx.sess, span, E0133, - "{} requires unsafe function or block", description) - .span_label(span, description) - .emit(); - } - } - UnsafeBlock(block_id) => { - // OK, but record this. - debug!("effect: recording unsafe block as used: {}", block_id); - self.used_unsafe.insert(block_id); - } - UnsafeFn => {} - } - } - - fn require_unsafe(&mut self, span: Span, description: &str) { - self.require_unsafe_ext(ast::DUMMY_NODE_ID, span, description, false) - } -} - -impl<'a, 'tcx> Visitor<'tcx> for EffectCheckVisitor<'a, 'tcx> { - fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> { - NestedVisitorMap::None - } - - fn visit_nested_body(&mut self, body: hir::BodyId) { - let old_tables = self.tables; - let old_body_id = self.body_id; - self.tables = self.tcx.body_tables(body); - self.body_id = body; - let body = self.tcx.hir.body(body); - self.visit_body(body); - self.tables = old_tables; - self.body_id = old_body_id; - } - - fn visit_fn(&mut self, fn_kind: FnKind<'tcx>, fn_decl: &'tcx hir::FnDecl, - body_id: hir::BodyId, span: Span, id: ast::NodeId) { - - let (is_item_fn, is_unsafe_fn) = match fn_kind { - FnKind::ItemFn(_, _, unsafety, ..) => - (true, unsafety == hir::Unsafety::Unsafe), - FnKind::Method(_, sig, ..) => - (true, sig.unsafety == hir::Unsafety::Unsafe), - _ => (false, false), - }; - - let old_unsafe_context = self.unsafe_context; - if is_unsafe_fn { - self.unsafe_context = UnsafeContext::new(UnsafeFn) - } else if is_item_fn { - self.unsafe_context = UnsafeContext::new(SafeContext) - } - - intravisit::walk_fn(self, fn_kind, fn_decl, body_id, span, id); - - self.unsafe_context = old_unsafe_context - } - - fn visit_block(&mut self, block: &'tcx hir::Block) { - let old_unsafe_context = self.unsafe_context; - match block.rules { - hir::UnsafeBlock(source) => { - // By default only the outermost `unsafe` block is - // "used" and so nested unsafe blocks are pointless - // (the inner ones are unnecessary and we actually - // warn about them). As such, there are two cases when - // we need to create a new context, when we're - // - outside `unsafe` and found a `unsafe` block - // (normal case) - // - inside `unsafe`, found an `unsafe` block - // created internally to the compiler - // - // The second case is necessary to ensure that the - // compiler `unsafe` blocks don't accidentally "use" - // external blocks (e.g. `unsafe { println("") }`, - // expands to `unsafe { ... unsafe { ... } }` where - // the inner one is compiler generated). - if self.unsafe_context.root == SafeContext || source == hir::CompilerGenerated { - self.unsafe_context.root = UnsafeBlock(block.id) - } - } - hir::PushUnsafeBlock(..) => { - self.unsafe_context.push_unsafe_count = - self.unsafe_context.push_unsafe_count.checked_add(1).unwrap(); - } - hir::PopUnsafeBlock(..) => { - self.unsafe_context.push_unsafe_count = - self.unsafe_context.push_unsafe_count.checked_sub(1).unwrap(); - } - hir::DefaultBlock => {} - } - - intravisit::walk_block(self, block); - - self.unsafe_context = old_unsafe_context; - - // Don't warn about generated blocks, that'll just pollute the output. - match block.rules { - hir::UnsafeBlock(hir::UserProvided) => {} - _ => return, - } - if self.used_unsafe.contains(&block.id) { - return - } - - /// Return the NodeId for an enclosing scope that is also `unsafe` - fn is_enclosed(tcx: TyCtxt, - used_unsafe: &FxHashSet, - id: ast::NodeId) -> Option<(String, ast::NodeId)> { - let parent_id = tcx.hir.get_parent_node(id); - if parent_id != id { - if used_unsafe.contains(&parent_id) { - Some(("block".to_string(), parent_id)) - } else if let Some(hir::map::NodeItem(&hir::Item { - node: hir::ItemFn(_, hir::Unsafety::Unsafe, _, _, _, _), - .. - })) = tcx.hir.find(parent_id) { - Some(("fn".to_string(), parent_id)) - } else { - is_enclosed(tcx, used_unsafe, parent_id) - } - } else { - None - } - } - - let mut db = self.tcx.struct_span_lint_node(UNUSED_UNSAFE, - block.id, - block.span, - "unnecessary `unsafe` block"); - db.span_label(block.span, "unnecessary `unsafe` block"); - if let Some((kind, id)) = is_enclosed(self.tcx, &self.used_unsafe, block.id) { - db.span_note(self.tcx.hir.span(id), - &format!("because it's nested under this `unsafe` {}", kind)); - } - db.emit(); - } - - fn visit_expr(&mut self, expr: &'tcx hir::Expr) { - match expr.node { - hir::ExprMethodCall(..) => { - let def_id = self.tables.type_dependent_defs()[expr.hir_id].def_id(); - let sig = self.tcx.fn_sig(def_id); - debug!("effect: method call case, signature is {:?}", - sig); - - if sig.0.unsafety == hir::Unsafety::Unsafe { - self.require_unsafe(expr.span, - "invocation of unsafe method") - } - } - hir::ExprCall(ref base, _) => { - let base_type = self.tables.expr_ty_adjusted(base); - debug!("effect: call case, base type is {:?}", - base_type); - match base_type.sty { - ty::TyFnDef(..) | ty::TyFnPtr(_) => { - if base_type.fn_sig(self.tcx).unsafety() == hir::Unsafety::Unsafe { - self.require_unsafe(expr.span, "call to unsafe function") - } - } - _ => {} - } - } - hir::ExprUnary(hir::UnDeref, ref base) => { - let base_type = self.tables.expr_ty_adjusted(base); - debug!("effect: unary case, base type is {:?}", - base_type); - if let ty::TyRawPtr(_) = base_type.sty { - self.require_unsafe(expr.span, "dereference of raw pointer") - } - } - hir::ExprInlineAsm(..) => { - self.require_unsafe(expr.span, "use of inline assembly"); - } - hir::ExprPath(hir::QPath::Resolved(_, ref path)) => { - if let Def::Static(def_id, mutbl) = path.def { - if mutbl { - self.require_unsafe(expr.span, "use of mutable static"); - } else if match self.tcx.hir.get_if_local(def_id) { - Some(hir::map::NodeForeignItem(..)) => true, - Some(..) => false, - None => self.tcx.is_foreign_item(def_id), - } { - self.require_unsafe_ext(expr.id, expr.span, "use of extern static", true); - } - } - } - hir::ExprField(ref base_expr, field) => { - if let ty::TyAdt(adt, ..) = self.tables.expr_ty_adjusted(base_expr).sty { - if adt.is_union() { - self.require_unsafe(field.span, "access to union field"); - } - } - } - hir::ExprAssign(ref lhs, ref rhs) => { - if let hir::ExprField(ref base_expr, field) = lhs.node { - if let ty::TyAdt(adt, ..) = self.tables.expr_ty_adjusted(base_expr).sty { - if adt.is_union() { - let field_ty = self.tables.expr_ty_adjusted(lhs); - let owner_def_id = self.tcx.hir.body_owner_def_id(self.body_id); - let param_env = self.tcx.param_env(owner_def_id); - if field_ty.moves_by_default(self.tcx, param_env, field.span) { - self.require_unsafe(field.span, - "assignment to non-`Copy` union field"); - } - // Do not walk the field expr again. - intravisit::walk_expr(self, base_expr); - intravisit::walk_expr(self, rhs); - return - } - } - } - } - _ => {} - } - - intravisit::walk_expr(self, expr); - } - - fn visit_pat(&mut self, pat: &'tcx hir::Pat) { - if let PatKind::Struct(_, ref fields, _) = pat.node { - if let ty::TyAdt(adt, ..) = self.tables.pat_ty(pat).sty { - if adt.is_union() { - for field in fields { - self.require_unsafe(field.span, "matching on union field"); - } - } - } - } - - intravisit::walk_pat(self, pat); - } -} - -pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { - let mut visitor = EffectCheckVisitor { - tcx, - tables: &ty::TypeckTables::empty(None), - body_id: hir::BodyId { node_id: ast::CRATE_NODE_ID }, - unsafe_context: UnsafeContext::new(SafeContext), - used_unsafe: FxHashSet(), - }; - - tcx.hir.krate().visit_all_item_likes(&mut visitor.as_deep_visitor()); -} diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index 09da1d1f820b..a5fe48284536 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -290,6 +290,19 @@ impl<'tcx> Mir<'tcx> { pub struct VisibilityScopeInfo { /// A NodeId with lint levels equivalent to this scope's lint levels. pub lint_root: ast::NodeId, + /// The unsafe block that contains this node. + pub safety: Safety, +} + +#[derive(Copy, Clone, Debug)] +pub enum Safety { + Safe, + /// Unsafe because of a PushUnsafeBlock + BuiltinUnsafe, + /// Unsafe because of an unsafe fn + FnUnsafe, + /// Unsafe because of an `unsafe` block + ExplicitUnsafe(ast::NodeId) } impl_stable_hash_for!(struct Mir<'tcx> { @@ -346,7 +359,7 @@ impl serialize::Decodable for ClearOnDecode { /// Grouped information about the source code origin of a MIR entity. /// Intended to be inspected by diagnostics and debuginfo. /// Most passes can work with it as a whole, within a single function. -#[derive(Copy, Clone, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash)] pub struct SourceInfo { /// Source span for the AST pertaining to this MIR entity. pub span: Span, @@ -447,7 +460,7 @@ pub struct LocalDecl<'tcx> { /// True if this is an internal local /// /// These locals are not based on types in the source code and are only used - /// for drop flags at the moment. + /// for a few desugarings at the moment. /// /// The generator transformation will sanity check the locals which are live /// across a suspension point against the type components of the generator @@ -455,6 +468,9 @@ pub struct LocalDecl<'tcx> { /// flag drop flags to avoid triggering this check as they are introduced /// after typeck. /// + /// Unsafety checking will also ignore dereferences of these locals, + /// so they can be used for raw pointers only used in a desugaring. + /// /// This should be sound because the drop flags are fully algebraic, and /// therefore don't affect the OIBIT or outlives properties of the /// generator. @@ -1634,6 +1650,13 @@ impl Location { } } +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub struct UnsafetyViolation { + pub source_info: SourceInfo, + pub description: &'static str, + pub lint_node_id: Option, +} + /// The layout of generator state #[derive(Clone, Debug, RustcEncodable, RustcDecodable)] pub struct GeneratorLayout<'tcx> { diff --git a/src/librustc/ty/maps/mod.rs b/src/librustc/ty/maps/mod.rs index d264fecb5215..4bd2d5be6d71 100644 --- a/src/librustc/ty/maps/mod.rs +++ b/src/librustc/ty/maps/mod.rs @@ -159,6 +159,10 @@ define_maps! { <'tcx> /// expression defining the closure. [] fn closure_kind: ClosureKind(DefId) -> ty::ClosureKind, + /// Unsafety violations for this def ID. + [] fn unsafety_violations: UnsafetyViolations(DefId) + -> Rc<[mir::UnsafetyViolation]>, + /// The signature of functions and closures. [] fn fn_sig: FnSignature(DefId) -> ty::PolyFnSig<'tcx>, diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 5116a3087d28..4725f6e2c783 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -1081,10 +1081,6 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, "intrinsic checking", || middle::intrinsicck::check_crate(tcx)); - time(time_passes, - "effect checking", - || middle::effect::check_crate(tcx)); - time(time_passes, "match checking", || check_match::check_crate(tcx)); @@ -1105,6 +1101,11 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, "MIR borrow checking", || for def_id in tcx.body_owners() { tcx.mir_borrowck(def_id) }); + time(time_passes, + "MIR effect checking", + || for def_id in tcx.body_owners() { + mir::transform::check_unsafety::check_unsafety(tcx, def_id) + }); // Avoid overwhelming user with errors if type checking failed. // I'm not sure how helpful this is, to be honest, but it avoids // a diff --git a/src/librustc_mir/build/block.rs b/src/librustc_mir/build/block.rs index db04596dd589..1fc96dbf4519 100644 --- a/src/librustc_mir/build/block.rs +++ b/src/librustc_mir/build/block.rs @@ -21,7 +21,15 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { ast_block: &'tcx hir::Block, source_info: SourceInfo) -> BlockAnd<()> { - let Block { region_scope, opt_destruction_scope, span, stmts, expr, targeted_by_break } = + let Block { + region_scope, + opt_destruction_scope, + span, + stmts, + expr, + targeted_by_break, + safety_mode + } = self.hir.mirror(ast_block); self.in_opt_scope(opt_destruction_scope.map(|de|(de, source_info)), block, move |this| { this.in_scope((region_scope, source_info), LintLevel::Inherited, block, move |this| { @@ -30,13 +38,15 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { let exit_block = this.cfg.start_new_block(); let block_exit = this.in_breakable_scope( None, exit_block, destination.clone(), |this| { - this.ast_block_stmts(destination, block, span, stmts, expr) + this.ast_block_stmts(destination, block, span, stmts, expr, + safety_mode) }); this.cfg.terminate(unpack!(block_exit), source_info, TerminatorKind::Goto { target: exit_block }); exit_block.unit() } else { - this.ast_block_stmts(destination, block, span, stmts, expr) + this.ast_block_stmts(destination, block, span, stmts, expr, + safety_mode) } }) }) @@ -47,7 +57,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { mut block: BasicBlock, span: Span, stmts: Vec>, - expr: Option>) + expr: Option>, + safety_mode: BlockSafety) -> BlockAnd<()> { let this = self; @@ -69,6 +80,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // First we build all the statements in the block. let mut let_scope_stack = Vec::with_capacity(8); let outer_visibility_scope = this.visibility_scope; + let outer_push_unsafe_count = this.push_unsafe_count; + let outer_unpushed_unsafe = this.unpushed_unsafe; + this.update_visibility_scope_for_safety_mode(span, safety_mode); + let source_info = this.source_info(span); for stmt in stmts { let Stmt { kind, opt_destruction_scope } = this.hir.mirror(stmt); @@ -137,6 +152,48 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } // Restore the original visibility scope. this.visibility_scope = outer_visibility_scope; + this.push_unsafe_count = outer_push_unsafe_count; + this.unpushed_unsafe = outer_unpushed_unsafe; block.unit() } + + /// If we are changing the safety mode, create a new visibility scope + fn update_visibility_scope_for_safety_mode(&mut self, + span: Span, + safety_mode: BlockSafety) + { + debug!("update_visibility_scope_for({:?}, {:?})", span, safety_mode); + let new_unsafety = match safety_mode { + BlockSafety::Safe => None, + BlockSafety::ExplicitUnsafe(node_id) => { + assert_eq!(self.push_unsafe_count, 0); + match self.unpushed_unsafe { + Safety::Safe => {} + _ => return + } + self.unpushed_unsafe = Safety::ExplicitUnsafe(node_id); + Some(Safety::ExplicitUnsafe(node_id)) + } + BlockSafety::PushUnsafe => { + self.push_unsafe_count += 1; + Some(Safety::BuiltinUnsafe) + } + BlockSafety::PopUnsafe => { + self.push_unsafe_count = + self.push_unsafe_count.checked_sub(1).unwrap_or_else(|| { + span_bug!(span, "unsafe count underflow") + }); + if self.push_unsafe_count == 0 { + Some(self.unpushed_unsafe) + } else { + None + } + } + }; + + if let Some(unsafety) = new_unsafety { + self.visibility_scope = self.new_visibility_scope( + span, LintLevel::Inherited, Some(unsafety)); + } + } } diff --git a/src/librustc_mir/build/expr/into.rs b/src/librustc_mir/build/expr/into.rs index 65e16657a031..cdbcb43370fe 100644 --- a/src/librustc_mir/build/expr/into.rs +++ b/src/librustc_mir/build/expr/into.rs @@ -228,9 +228,22 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { let val = args.next().expect("1 argument to `move_val_init`"); assert!(args.next().is_none(), ">2 arguments to `move_val_init`"); - let topmost_scope = this.topmost_scope(); - let ptr = unpack!(block = this.as_temp(block, Some(topmost_scope), ptr)); - this.into(&Lvalue::Local(ptr).deref(), block, val) + let ptr = this.hir.mirror(ptr); + let ptr_ty = ptr.ty; + // Create an *internal* temp for the pointer, so that unsafety + // checking won't complain about the raw pointer assignment. + let ptr_temp = this.local_decls.push(LocalDecl { + mutability: Mutability::Mut, + ty: ptr_ty, + name: None, + source_info, + lexical_scope: source_info.scope, + internal: true, + is_user_variable: false + }); + let ptr_temp = Lvalue::Local(ptr_temp); + let block = unpack!(this.into(&ptr_temp, block, ptr)); + this.into(&ptr_temp.deref(), block, val) } else { let args: Vec<_> = args.into_iter() diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs index b856e6345dc6..f04dede6e400 100644 --- a/src/librustc_mir/build/matches/mod.rs +++ b/src/librustc_mir/build/matches/mod.rs @@ -182,11 +182,13 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { self.visit_bindings(pattern, &mut |this, mutability, name, var, span, ty| { if var_scope.is_none() { var_scope = Some(this.new_visibility_scope(scope_span, - LintLevel::Inherited)); + LintLevel::Inherited, + None)); // If we have lints, create a new visibility scope // that marks the lints for the locals. if lint_level.is_explicit() { - this.new_visibility_scope(scope_span, lint_level); + this.visibility_scope = + this.new_visibility_scope(scope_span, lint_level, None); } } let source_info = SourceInfo { diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs index 9c2c5bbcdb8b..68ef646184c2 100644 --- a/src/librustc_mir/build/mod.rs +++ b/src/librustc_mir/build/mod.rs @@ -72,14 +72,14 @@ pub fn mir_build<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> Mir<'t // is a constant "initializer" expression. match expr.node { hir::ExprClosure(_, _, body, _, _) => body, - _ => hir::BodyId { node_id: expr.id } + _ => hir::BodyId { node_id: expr.id }, } } hir::map::NodeVariant(variant) => return create_constructor_shim(tcx, id, &variant.node.data), hir::map::NodeStructCtor(ctor) => return create_constructor_shim(tcx, id, ctor), - _ => unsupported() + _ => unsupported(), }; let src = MirSource::from_node(tcx, id); @@ -109,6 +109,12 @@ pub fn mir_build<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> Mir<'t _ => None, }; + // FIXME: safety in closures + let safety = match fn_sig.unsafety { + hir::Unsafety::Normal => Safety::Safe, + hir::Unsafety::Unsafe => Safety::FnUnsafe, + }; + let body = tcx.hir.body(body_id); let explicit_arguments = body.arguments @@ -127,7 +133,8 @@ pub fn mir_build<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> Mir<'t (None, fn_sig.output()) }; - build::construct_fn(cx, id, arguments, abi, return_ty, yield_ty, body) + build::construct_fn(cx, id, arguments, safety, abi, + return_ty, yield_ty, body) } else { build::construct_const(cx, body_id) }; @@ -271,6 +278,13 @@ struct Builder<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { /// see the `scope` module for more details scopes: Vec>, + /// The current unsafe block in scope, even if it is hidden by + /// a PushUnsafeBlock + unpushed_unsafe: Safety, + + /// The number of `push_unsafe_block` levels in scope + push_unsafe_count: usize, + /// the current set of breakables; see the `scope` module for more /// details breakable_scopes: Vec>, @@ -360,6 +374,7 @@ macro_rules! unpack { fn construct_fn<'a, 'gcx, 'tcx, A>(hir: Cx<'a, 'gcx, 'tcx>, fn_id: ast::NodeId, arguments: A, + safety: Safety, abi: Abi, return_ty: Ty<'gcx>, yield_ty: Option>, @@ -374,6 +389,7 @@ fn construct_fn<'a, 'gcx, 'tcx, A>(hir: Cx<'a, 'gcx, 'tcx>, let mut builder = Builder::new(hir.clone(), span, arguments.len(), + safety, return_ty); let call_site_scope = region::Scope::CallSite(body.value.hir_id.local_id); @@ -444,7 +460,7 @@ fn construct_const<'a, 'gcx, 'tcx>(hir: Cx<'a, 'gcx, 'tcx>, let ty = hir.tables().expr_ty_adjusted(ast_expr); let owner_id = tcx.hir.body_owner(body_id); let span = tcx.hir.span(owner_id); - let mut builder = Builder::new(hir.clone(), span, 0, ty); + let mut builder = Builder::new(hir.clone(), span, 0, Safety::Safe, ty); let mut block = START_BLOCK; let expr = builder.hir.mirror(ast_expr); @@ -465,7 +481,7 @@ fn construct_error<'a, 'gcx, 'tcx>(hir: Cx<'a, 'gcx, 'tcx>, let owner_id = hir.tcx().hir.body_owner(body_id); let span = hir.tcx().hir.span(owner_id); let ty = hir.tcx().types.err; - let mut builder = Builder::new(hir, span, 0, ty); + let mut builder = Builder::new(hir, span, 0, Safety::Safe, ty); let source_info = builder.source_info(span); builder.cfg.terminate(START_BLOCK, source_info, TerminatorKind::Unreachable); builder.finish(vec![], ty, None) @@ -475,6 +491,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { fn new(hir: Cx<'a, 'gcx, 'tcx>, span: Span, arg_count: usize, + safety: Safety, return_ty: Ty<'tcx>) -> Builder<'a, 'gcx, 'tcx> { let lint_level = LintLevel::Explicit(hir.root_lint_level); @@ -487,6 +504,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { visibility_scopes: IndexVec::new(), visibility_scope: ARGUMENT_VISIBILITY_SCOPE, visibility_scope_info: IndexVec::new(), + push_unsafe_count: 0, + unpushed_unsafe: safety, breakable_scopes: vec![], local_decls: IndexVec::from_elem_n(LocalDecl::new_return_pointer(return_ty, span), 1), @@ -498,7 +517,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { assert_eq!(builder.cfg.start_new_block(), START_BLOCK); assert_eq!( - builder.new_visibility_scope(span, lint_level), + builder.new_visibility_scope(span, lint_level, Some(safety)), ARGUMENT_VISIBILITY_SCOPE); builder.visibility_scopes[ARGUMENT_VISIBILITY_SCOPE].parent_scope = None; diff --git a/src/librustc_mir/build/scope.rs b/src/librustc_mir/build/scope.rs index 62613c7168a3..20d3efdfffc3 100644 --- a/src/librustc_mir/build/scope.rs +++ b/src/librustc_mir/build/scope.rs @@ -330,7 +330,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { if !same_lint_scopes { self.visibility_scope = - self.new_visibility_scope(region_scope.1.span, lint_level); + self.new_visibility_scope(region_scope.1.span, lint_level, + None); } } self.push_scope(region_scope); @@ -500,19 +501,27 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { /// Creates a new visibility scope, nested in the current one. pub fn new_visibility_scope(&mut self, span: Span, - lint_level: LintLevel) -> VisibilityScope { - debug!("new_visibility_scope({:?}, {:?})", span, lint_level); + lint_level: LintLevel, + safety: Option) -> VisibilityScope { let parent = self.visibility_scope; - let info = if let LintLevel::Explicit(lint_level) = lint_level { - VisibilityScopeInfo { lint_root: lint_level } - } else { - self.visibility_scope_info[parent].clone() - }; + debug!("new_visibility_scope({:?}, {:?}, {:?}) - parent({:?})={:?}", + span, lint_level, safety, + parent, self.visibility_scope_info.get(parent)); let scope = self.visibility_scopes.push(VisibilityScopeData { span, parent_scope: Some(parent), }); - self.visibility_scope_info.push(info); + let scope_info = VisibilityScopeInfo { + lint_root: if let LintLevel::Explicit(lint_root) = lint_level { + lint_root + } else { + self.visibility_scope_info[parent].lint_root + }, + safety: safety.unwrap_or_else(|| { + self.visibility_scope_info[parent].safety + }) + }; + self.visibility_scope_info.push(scope_info); scope } diff --git a/src/librustc_mir/diagnostics.rs b/src/librustc_mir/diagnostics.rs index 26436d54ac88..2c4afb0aa0e0 100644 --- a/src/librustc_mir/diagnostics.rs +++ b/src/librustc_mir/diagnostics.rs @@ -195,6 +195,40 @@ instead of using a `const fn`, or refactoring the code to a functional style to avoid mutation if possible. "##, +E0133: r##" +Unsafe code was used outside of an unsafe function or block. + +Erroneous code example: + +```compile_fail,E0133 +unsafe fn f() { return; } // This is the unsafe code + +fn main() { + f(); // error: call to unsafe function requires unsafe function or block +} +``` + +Using unsafe functionality is potentially dangerous and disallowed by safety +checks. Examples: + +* Dereferencing raw pointers +* Calling functions via FFI +* Calling functions marked unsafe + +These safety checks can be relaxed for a section of the code by wrapping the +unsafe instructions with an `unsafe` block. For instance: + +``` +unsafe fn f() { return; } + +fn main() { + unsafe { f(); } // ok! +} +``` + +See also https://doc.rust-lang.org/book/first-edition/unsafe.html +"##, + E0381: r##" It is not allowed to use or capture an uninitialized variable. For example: diff --git a/src/librustc_mir/hair/cx/block.rs b/src/librustc_mir/hair/cx/block.rs index 1c1fc2dcafa5..a8172a60174f 100644 --- a/src/librustc_mir/hair/cx/block.rs +++ b/src/librustc_mir/hair/cx/block.rs @@ -30,6 +30,16 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Block { span: self.span, stmts, expr: self.expr.to_ref(), + safety_mode: match self.rules { + hir::BlockCheckMode::DefaultBlock => + BlockSafety::Safe, + hir::BlockCheckMode::UnsafeBlock(..) => + BlockSafety::ExplicitUnsafe(self.id), + hir::BlockCheckMode::PushUnsafeBlock(..) => + BlockSafety::PushUnsafe, + hir::BlockCheckMode::PopUnsafeBlock(..) => + BlockSafety::PopUnsafe + }, } } } diff --git a/src/librustc_mir/hair/mod.rs b/src/librustc_mir/hair/mod.rs index 0c8dba5159f4..09a31f9ab8fa 100644 --- a/src/librustc_mir/hair/mod.rs +++ b/src/librustc_mir/hair/mod.rs @@ -52,6 +52,15 @@ pub struct Block<'tcx> { pub span: Span, pub stmts: Vec>, pub expr: Option>, + pub safety_mode: BlockSafety, +} + +#[derive(Copy, Clone, Debug)] +pub enum BlockSafety { + Safe, + ExplicitUnsafe(ast::NodeId), + PushUnsafe, + PopUnsafe } #[derive(Clone, Debug)] diff --git a/src/librustc_mir/transform/check_unsafety.rs b/src/librustc_mir/transform/check_unsafety.rs new file mode 100644 index 000000000000..49ce36223994 --- /dev/null +++ b/src/librustc_mir/transform/check_unsafety.rs @@ -0,0 +1,387 @@ +// Copyright 2017 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. + +use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::indexed_vec::IndexVec; + +use rustc::ty::maps::Providers; +use rustc::ty::{self, TyCtxt}; +use rustc::hir; +use rustc::hir::def::Def; +use rustc::hir::def_id::DefId; +use rustc::hir::map::{DefPathData, Node}; +use rustc::lint::builtin::{SAFE_EXTERN_STATICS, UNUSED_UNSAFE}; +use rustc::mir::*; +use rustc::mir::visit::{LvalueContext, Visitor}; + +use syntax::ast; + +use std::rc::Rc; + + +pub struct UnsafetyChecker<'a, 'tcx: 'a> { + mir: &'a Mir<'tcx>, + visibility_scope_info: &'a IndexVec, + violations: Vec, + source_info: SourceInfo, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + used_unsafe: FxHashSet, +} + +impl<'a, 'gcx, 'tcx> UnsafetyChecker<'a, 'tcx> { + fn new(mir: &'a Mir<'tcx>, + visibility_scope_info: &'a IndexVec, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + param_env: ty::ParamEnv<'tcx>) -> Self { + Self { + mir, + visibility_scope_info, + violations: vec![], + source_info: SourceInfo { + span: mir.span, + scope: ARGUMENT_VISIBILITY_SCOPE + }, + tcx, + param_env, + used_unsafe: FxHashSet(), + } + } +} + +impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> { + fn visit_terminator(&mut self, + block: BasicBlock, + terminator: &Terminator<'tcx>, + location: Location) + { + self.source_info = terminator.source_info; + match terminator.kind { + TerminatorKind::Goto { .. } | + TerminatorKind::SwitchInt { .. } | + TerminatorKind::Drop { .. } | + TerminatorKind::Yield { .. } | + TerminatorKind::Assert { .. } | + TerminatorKind::DropAndReplace { .. } | + TerminatorKind::GeneratorDrop | + TerminatorKind::Resume | + TerminatorKind::Return | + TerminatorKind::Unreachable => { + // safe (at least as emitted during MIR construction) + } + + TerminatorKind::Call { ref func, .. } => { + let func_ty = func.ty(self.mir, self.tcx); + let sig = func_ty.fn_sig(self.tcx); + if let hir::Unsafety::Unsafe = sig.unsafety() { + self.require_unsafe("call to unsafe function") + } + } + } + self.super_terminator(block, terminator, location); + } + + fn visit_statement(&mut self, + block: BasicBlock, + statement: &Statement<'tcx>, + location: Location) + { + self.source_info = statement.source_info; + match statement.kind { + StatementKind::Assign(..) | + StatementKind::SetDiscriminant { .. } | + StatementKind::StorageLive(..) | + StatementKind::StorageDead(..) | + StatementKind::EndRegion(..) | + StatementKind::Validate(..) | + StatementKind::Nop => { + // safe (at least as emitted during MIR construction) + } + + StatementKind::InlineAsm { .. } => { + self.require_unsafe("use of inline assembly") + }, + } + self.super_statement(block, statement, location); + } + + fn visit_rvalue(&mut self, + rvalue: &Rvalue<'tcx>, + location: Location) + { + if let &Rvalue::Aggregate( + box AggregateKind::Closure(def_id, _), + _ + ) = rvalue { + let unsafety_violations = self.tcx.unsafety_violations(def_id); + self.register_violations(&unsafety_violations); + } + self.super_rvalue(rvalue, location); + } + + fn visit_lvalue(&mut self, + lvalue: &Lvalue<'tcx>, + context: LvalueContext<'tcx>, + location: Location) { + match lvalue { + &Lvalue::Projection(box Projection { + ref base, ref elem + }) => { + let old_source_info = self.source_info; + if let &Lvalue::Local(local) = base { + if self.mir.local_decls[local].internal { + // Internal locals are used in the `move_val_init` desugaring. + // We want to check unsafety against the source info of the + // desugaring, rather than the source info of the RHS. + self.source_info = self.mir.local_decls[local].source_info; + } + } + let base_ty = base.ty(self.mir, self.tcx).to_ty(self.tcx); + match base_ty.sty { + ty::TyRawPtr(..) => { + self.require_unsafe("dereference of raw pointer") + } + ty::TyAdt(adt, _) if adt.is_union() => { + if context == LvalueContext::Store || + context == LvalueContext::Drop + { + let elem_ty = match elem { + &ProjectionElem::Field(_, ty) => ty, + _ => span_bug!( + self.source_info.span, + "non-field projection {:?} from union?", + lvalue) + }; + if elem_ty.moves_by_default(self.tcx, self.param_env, + self.source_info.span) { + self.require_unsafe( + "assignment to non-`Copy` union field") + } else { + // write to non-move union, safe + } + } else { + self.require_unsafe("access to union field") + } + } + _ => {} + } + self.source_info = old_source_info; + } + &Lvalue::Local(..) => { + // locals are safe + } + &Lvalue::Static(box Static { def_id, ty: _ }) => { + if self.is_static_mut(def_id) { + self.require_unsafe("use of mutable static"); + } else if self.tcx.is_foreign_item(def_id) { + let source_info = self.source_info; + let lint_root = + self.visibility_scope_info[source_info.scope].lint_root; + self.register_violations(&[UnsafetyViolation { + source_info, + description: "use of extern static", + lint_node_id: Some(lint_root) + }]); + } + } + } + self.super_lvalue(lvalue, context, location); + } +} + +impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> { + fn is_static_mut(&self, def_id: DefId) -> bool { + if let Some(node) = self.tcx.hir.get_if_local(def_id) { + match node { + Node::NodeItem(&hir::Item { + node: hir::ItemStatic(_, hir::MutMutable, _), .. + }) => true, + Node::NodeForeignItem(&hir::ForeignItem { + node: hir::ForeignItemStatic(_, mutbl), .. + }) => mutbl, + _ => false + } + } else { + match self.tcx.describe_def(def_id) { + Some(Def::Static(_, mutbl)) => mutbl, + _ => false + } + } + } + fn require_unsafe(&mut self, + description: &'static str) + { + let source_info = self.source_info; + self.register_violations(&[UnsafetyViolation { + source_info, description, lint_node_id: None + }]); + } + + fn register_violations(&mut self, violations: &[UnsafetyViolation]) { + match self.visibility_scope_info[self.source_info.scope].safety { + Safety::Safe => { + for violation in violations { + if !self.violations.contains(violation) { + self.violations.push(violation.clone()) + } + } + } + Safety::BuiltinUnsafe | Safety::FnUnsafe => {} + Safety::ExplicitUnsafe(node_id) => { + if !violations.is_empty() { + self.used_unsafe.insert(node_id); + } + } + } + } +} + +pub(crate) fn provide(providers: &mut Providers) { + *providers = Providers { + unsafety_violations, + ..*providers + }; +} + +struct UnusedUnsafeVisitor<'a, 'tcx: 'a> { + tcx: TyCtxt<'a, 'tcx, 'tcx>, + used_unsafe: FxHashSet +} + +impl<'a, 'tcx> hir::intravisit::Visitor<'tcx> for UnusedUnsafeVisitor<'a, 'tcx> { + fn nested_visit_map<'this>(&'this mut self) -> + hir::intravisit::NestedVisitorMap<'this, 'tcx> + { + hir::intravisit::NestedVisitorMap::None + } + + fn visit_block(&mut self, block: &'tcx hir::Block) { + hir::intravisit::walk_block(self, block); + + if let hir::UnsafeBlock(hir::UserProvided) = block.rules { + if !self.used_unsafe.contains(&block.id) { + self.report_unused_unsafe(block); + } + } + } +} + +impl<'a, 'tcx> UnusedUnsafeVisitor<'a, 'tcx> { + /// Return the NodeId for an enclosing scope that is also `unsafe` + fn is_enclosed(&self, id: ast::NodeId) -> Option<(String, ast::NodeId)> { + let parent_id = self.tcx.hir.get_parent_node(id); + if parent_id != id { + if self.used_unsafe.contains(&parent_id) { + Some(("block".to_string(), parent_id)) + } else if let Some(hir::map::NodeItem(&hir::Item { + node: hir::ItemFn(_, hir::Unsafety::Unsafe, _, _, _, _), + .. + })) = self.tcx.hir.find(parent_id) { + Some(("fn".to_string(), parent_id)) + } else { + self.is_enclosed(parent_id) + } + } else { + None + } + } + + fn report_unused_unsafe(&self, block: &'tcx hir::Block) { + let mut db = self.tcx.struct_span_lint_node(UNUSED_UNSAFE, + block.id, + block.span, + "unnecessary `unsafe` block"); + db.span_label(block.span, "unnecessary `unsafe` block"); + if let Some((kind, id)) = self.is_enclosed(block.id) { + db.span_note(self.tcx.hir.span(id), + &format!("because it's nested under this `unsafe` {}", kind)); + } + db.emit(); + } +} + +fn check_unused_unsafe<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + def_id: DefId, + used_unsafe: FxHashSet) +{ + let body_id = + tcx.hir.as_local_node_id(def_id).and_then(|node_id| { + tcx.hir.maybe_body_owned_by(node_id) + }); + + let body_id = match body_id { + Some(body) => body, + None => { + debug!("check_unused_unsafe({:?}) - no body found", def_id); + return + } + }; + let body = tcx.hir.body(body_id); + debug!("check_unused_unsafe({:?}, body={:?}, used_unsafe={:?})", + def_id, body, used_unsafe); + + hir::intravisit::Visitor::visit_body( + &mut UnusedUnsafeVisitor { tcx, used_unsafe }, + body); +} + +fn unsafety_violations<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> + Rc<[UnsafetyViolation]> +{ + debug!("unsafety_violations({:?})", def_id); + + // NB: this borrow is valid because all the consumers of + // `mir_const` force this. + let mir = &tcx.mir_const(def_id).borrow(); + + let visibility_scope_info = match mir.visibility_scope_info { + ClearOnDecode::Set(ref data) => data, + ClearOnDecode::Clear => { + debug!("unsafety_violations: {:?} - remote, skipping", def_id); + return Rc::new([]) + } + }; + + let param_env = tcx.param_env(def_id); + let mut checker = UnsafetyChecker::new( + mir, visibility_scope_info, tcx, param_env); + checker.visit_mir(mir); + + check_unused_unsafe(tcx, def_id, checker.used_unsafe); + checker.violations.into() +} + +pub fn check_unsafety<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) { + debug!("check_unsafety({:?})", def_id); + match tcx.def_key(def_id).disambiguated_data.data { + // closures are handled by their parent fn. + DefPathData::ClosureExpr => return, + _ => {} + }; + + for &UnsafetyViolation { + source_info, description, lint_node_id + } in &*tcx.unsafety_violations(def_id) { + // Report an error. + if let Some(lint_node_id) = lint_node_id { + tcx.lint_node(SAFE_EXTERN_STATICS, + lint_node_id, + source_info.span, + &format!("{} requires unsafe function or \ + block (error E0133)", description)); + } else { + struct_span_err!( + tcx.sess, source_info.span, E0133, + "{} requires unsafe function or block", description) + .span_label(source_info.span, description) + .emit(); + } + } +} diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs index e0f2a40ab073..7245b2daa126 100644 --- a/src/librustc_mir/transform/mod.rs +++ b/src/librustc_mir/transform/mod.rs @@ -26,6 +26,7 @@ use transform; pub mod add_validation; pub mod clean_end_regions; +pub mod check_unsafety; pub mod simplify_branches; pub mod simplify; pub mod erase_regions; @@ -46,6 +47,7 @@ pub mod nll; pub(crate) fn provide(providers: &mut Providers) { self::qualify_consts::provide(providers); + self::check_unsafety::provide(providers); *providers = Providers { mir_keys, mir_const, @@ -116,6 +118,7 @@ fn mir_validated<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> &'tcx // directly need the result or `mir_const_qualif`, so we can just force it. ty::queries::mir_const_qualif::force(tcx, DUMMY_SP, def_id); } + ty::queries::unsafety_violations::force(tcx, DUMMY_SP, def_id); let mut mir = tcx.mir_const(def_id).steal(); transform::run_suite(tcx, source, MIR_VALIDATED, &mut mir); diff --git a/src/librustc_mir/transform/type_check.rs b/src/librustc_mir/transform/type_check.rs index ab5998a34805..5311b80d4bdf 100644 --- a/src/librustc_mir/transform/type_check.rs +++ b/src/librustc_mir/transform/type_check.rs @@ -487,6 +487,16 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { if self.is_box_free(func) { self.check_box_free_inputs(mir, term, &sig, args); + // THIS IS A TEST. TEST TEST. + if let ClearOnDecode::Set(ref data) = mir.visibility_scope_info { + let lint_node_id = data[term.source_info.scope].lint_root; + tcx.struct_span_lint_node( + ::rustc::lint::builtin::TRIVIAL_NUMERIC_CASTS, + lint_node_id, + term.source_info.span, + "hi I'm a lint") + .emit(); + } } else { self.check_call_inputs(mir, term, &sig, args); } diff --git a/src/test/compile-fail/borrowck/borrowck-move-from-unsafe-ptr.rs b/src/test/compile-fail/borrowck/borrowck-move-from-unsafe-ptr.rs index 7284fa7a850f..9a39ff6206bf 100644 --- a/src/test/compile-fail/borrowck/borrowck-move-from-unsafe-ptr.rs +++ b/src/test/compile-fail/borrowck/borrowck-move-from-unsafe-ptr.rs @@ -9,8 +9,8 @@ // except according to those terms. -fn foo(x: *const Box) -> Box { - let y = *x; //~ ERROR dereference of raw pointer requires unsafe function or block +unsafe fn foo(x: *const Box) -> Box { + let y = *x; //~ ERROR cannot move out of dereference of raw pointer return y; } diff --git a/src/test/compile-fail/issue-43733.rs b/src/test/compile-fail/issue-43733.rs index f10531e407d6..0ae4cafa88b6 100644 --- a/src/test/compile-fail/issue-43733.rs +++ b/src/test/compile-fail/issue-43733.rs @@ -16,6 +16,8 @@ type Foo = std::cell::RefCell; #[cfg(target_thread_local)] static __KEY: std::thread::__FastLocalKeyInner = std::thread::__FastLocalKeyInner::new(); +//~^^ ERROR Sync` is not satisfied +//~^^^ ERROR Sync` is not satisfied #[cfg(not(target_thread_local))] static __KEY: std::thread::__OsLocalKeyInner = @@ -25,7 +27,7 @@ fn __getit() -> std::option::Option< &'static std::cell::UnsafeCell< std::option::Option>> { - __KEY.get() //~ ERROR invocation of unsafe method requires unsafe + __KEY.get() //~ ERROR call to unsafe function requires unsafe } static FOO: std::thread::LocalKey = diff --git a/src/test/compile-fail/union/union-unsafe.rs b/src/test/compile-fail/union/union-unsafe.rs index 2e018e696a41..e57d65dcb891 100644 --- a/src/test/compile-fail/union/union-unsafe.rs +++ b/src/test/compile-fail/union/union-unsafe.rs @@ -42,8 +42,8 @@ fn main() { let mut u1 = U1 { a: 10 }; // OK let a = u1.a; //~ ERROR access to union field requires unsafe u1.a = 11; // OK - let U1 { a } = u1; //~ ERROR matching on union field requires unsafe - if let U1 { a: 12 } = u1 {} //~ ERROR matching on union field requires unsafe + let U1 { a } = u1; //~ ERROR access to union field requires unsafe + if let U1 { a: 12 } = u1 {} //~ ERROR access to union field requires unsafe // let U1 { .. } = u1; // OK let mut u2 = U2 { a: String::from("old") }; // OK diff --git a/src/test/compile-fail/unsafe-fn-assign-deref-ptr.rs b/src/test/compile-fail/unsafe-fn-assign-deref-ptr.rs index cff10329b858..f30da250f6ac 100644 --- a/src/test/compile-fail/unsafe-fn-assign-deref-ptr.rs +++ b/src/test/compile-fail/unsafe-fn-assign-deref-ptr.rs @@ -1,3 +1,4 @@ + // Copyright 2012 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. @@ -9,7 +10,7 @@ // except according to those terms. -fn f(p: *const u8) { +fn f(p: *mut u8) { *p = 0; //~ ERROR dereference of raw pointer requires unsafe function or block return; } diff --git a/src/test/compile-fail/unsafe-move-val-init.rs b/src/test/compile-fail/unsafe-move-val-init.rs new file mode 100644 index 000000000000..84a8c84a0dbd --- /dev/null +++ b/src/test/compile-fail/unsafe-move-val-init.rs @@ -0,0 +1,20 @@ +// Copyright 2017 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. + +#![feature(core_intrinsics)] + +use std::intrinsics; + +// `move_val_init` has an odd desugaring, check that it is still treated +// as unsafe. +fn main() { + intrinsics::move_val_init(1 as *mut u32, 1); + //~^ ERROR dereference of raw pointer requires unsafe function or block +}