]> git.proxmox.com Git - rustc.git/blobdiff - src/librustc_mir/transform/check_unsafety.rs
New upstream version 1.28.0~beta.14+dfsg1
[rustc.git] / src / librustc_mir / transform / check_unsafety.rs
index ac76c8e95d2b020384dfc565cb309f274b2e2dfe..5f8f9acae838515a45f81ffdef12ef3579a130aa 100644 (file)
 
 use rustc_data_structures::fx::FxHashSet;
 use rustc_data_structures::indexed_vec::IndexVec;
+use rustc_data_structures::sync::Lrc;
 
-use rustc::ty::maps::Providers;
+use rustc::ty::query::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::lint::builtin::{SAFE_EXTERN_STATICS, SAFE_PACKED_BORROWS, UNUSED_UNSAFE};
 use rustc::mir::*;
-use rustc::mir::visit::{LvalueContext, Visitor};
+use rustc::mir::visit::{PlaceContext, Visitor};
 
 use syntax::ast;
+use syntax::symbol::Symbol;
 
-use std::rc::Rc;
-
+use util;
 
 pub struct UnsafetyChecker<'a, 'tcx: 'a> {
     mir: &'a Mir<'tcx>,
-    visibility_scope_info: &'a IndexVec<VisibilityScope, VisibilityScopeInfo>,
+    source_scope_local_data: &'a IndexVec<SourceScope, SourceScopeLocalData>,
     violations: Vec<UnsafetyViolation>,
     source_info: SourceInfo,
     tcx: TyCtxt<'a, 'tcx, 'tcx>,
@@ -39,16 +38,16 @@ pub struct UnsafetyChecker<'a, 'tcx: 'a> {
 
 impl<'a, 'gcx, 'tcx> UnsafetyChecker<'a, 'tcx> {
     fn new(mir: &'a Mir<'tcx>,
-           visibility_scope_info: &'a IndexVec<VisibilityScope, VisibilityScopeInfo>,
+           source_scope_local_data: &'a IndexVec<SourceScope, SourceScopeLocalData>,
            tcx: TyCtxt<'a, 'tcx, 'tcx>,
            param_env: ty::ParamEnv<'tcx>) -> Self {
         Self {
             mir,
-            visibility_scope_info,
+            source_scope_local_data,
             violations: vec![],
             source_info: SourceInfo {
                 span: mir.span,
-                scope: ARGUMENT_VISIBILITY_SCOPE
+                scope: OUTERMOST_SOURCE_SCOPE
             },
             tcx,
             param_env,
@@ -74,8 +73,11 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
             TerminatorKind::DropAndReplace { .. } |
             TerminatorKind::GeneratorDrop |
             TerminatorKind::Resume |
+            TerminatorKind::Abort |
             TerminatorKind::Return |
-            TerminatorKind::Unreachable => {
+            TerminatorKind::Unreachable |
+            TerminatorKind::FalseEdges { .. } |
+            TerminatorKind::FalseUnwind { .. } => {
                 // safe (at least as emitted during MIR construction)
             }
 
@@ -98,11 +100,13 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
         self.source_info = statement.source_info;
         match statement.kind {
             StatementKind::Assign(..) |
+            StatementKind::ReadForMatch(..) |
             StatementKind::SetDiscriminant { .. } |
             StatementKind::StorageLive(..) |
             StatementKind::StorageDead(..) |
             StatementKind::EndRegion(..) |
             StatementKind::Validate(..) |
+            StatementKind::UserAssertTy(..) |
             StatementKind::Nop => {
                 // safe (at least as emitted during MIR construction)
             }
@@ -135,16 +139,29 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
         self.super_rvalue(rvalue, location);
     }
 
-    fn visit_lvalue(&mut self,
-                    lvalue: &Lvalue<'tcx>,
-                    context: LvalueContext<'tcx>,
+    fn visit_place(&mut self,
+                    place: &Place<'tcx>,
+                    context: PlaceContext<'tcx>,
                     location: Location) {
-        match lvalue {
-            &Lvalue::Projection(box Projection {
+        if let PlaceContext::Borrow { .. } = context {
+            if util::is_disaligned(self.tcx, self.mir, self.param_env, place) {
+                let source_info = self.source_info;
+                let lint_root =
+                    self.source_scope_local_data[source_info.scope].lint_root;
+                self.register_violations(&[UnsafetyViolation {
+                    source_info,
+                    description: Symbol::intern("borrow of packed field").as_interned_str(),
+                    kind: UnsafetyViolationKind::BorrowPacked(lint_root)
+                }], &[]);
+            }
+        }
+
+        match place {
+            &Place::Projection(box Projection {
                 ref base, ref elem
             }) => {
                 let old_source_info = self.source_info;
-                if let &Lvalue::Local(local) = base {
+                if let &Place::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
@@ -157,86 +174,73 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
                     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")
+                    ty::TyAdt(adt, _) => {
+                        if adt.is_union() {
+                            if context == PlaceContext::Store ||
+                                context == PlaceContext::AsmOutput ||
+                                context == PlaceContext::Drop
+                            {
+                                let elem_ty = match elem {
+                                    &ProjectionElem::Field(_, ty) => ty,
+                                    _ => span_bug!(
+                                        self.source_info.span,
+                                        "non-field projection {:?} from union?",
+                                        place)
+                                };
+                                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 {
-                                // write to non-move union, safe
+                                self.require_unsafe("access to union field")
                             }
-                        } else {
-                            self.require_unsafe("access to union field")
                         }
                     }
                     _ => {}
                 }
                 self.source_info = old_source_info;
             }
-            &Lvalue::Local(..) => {
+            &Place::Local(..) => {
                 // locals are safe
             }
-            &Lvalue::Static(box Static { def_id, ty: _ }) => {
-                if self.is_static_mut(def_id) {
+            &Place::Static(box Static { def_id, ty: _ }) => {
+                if self.tcx.is_static(def_id) == Some(hir::Mutability::MutMutable) {
                     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.source_scope_local_data[source_info.scope].lint_root;
                     self.register_violations(&[UnsafetyViolation {
                         source_info,
-                        description: "use of extern static",
-                        lint_node_id: Some(lint_root)
+                        description: Symbol::intern("use of extern static").as_interned_str(),
+                        kind: UnsafetyViolationKind::ExternStatic(lint_root)
                     }], &[]);
                 }
             }
-        }
-        self.super_lvalue(lvalue, context, location);
+        };
+        self.super_place(place, 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
+            source_info,
+            description: Symbol::intern(description).as_interned_str(),
+            kind: UnsafetyViolationKind::General,
         }], &[]);
     }
 
     fn register_violations(&mut self,
                            violations: &[UnsafetyViolation],
                            unsafe_blocks: &[(ast::NodeId, bool)]) {
-        let within_unsafe = match self.visibility_scope_info[self.source_info.scope].safety {
+        let within_unsafe = match self.source_scope_local_data[self.source_info.scope].safety {
             Safety::Safe => {
                 for violation in violations {
                     if !self.violations.contains(violation) {
@@ -263,6 +267,7 @@ impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> {
 pub(crate) fn provide(providers: &mut Providers) {
     *providers = Providers {
         unsafety_check_result,
+        unsafe_derive_on_repr_packed,
         ..*providers
     };
 }
@@ -322,20 +327,20 @@ fn unsafety_check_result<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId)
     // `mir_built` force this.
     let mir = &tcx.mir_built(def_id).borrow();
 
-    let visibility_scope_info = match mir.visibility_scope_info {
-        ClearOnDecode::Set(ref data) => data,
-        ClearOnDecode::Clear => {
+    let source_scope_local_data = match mir.source_scope_local_data {
+        ClearCrossCrate::Set(ref data) => data,
+        ClearCrossCrate::Clear => {
             debug!("unsafety_violations: {:?} - remote, skipping", def_id);
             return UnsafetyCheckResult {
-                violations: Rc::new([]),
-                unsafe_blocks: Rc::new([])
+                violations: Lrc::new([]),
+                unsafe_blocks: Lrc::new([])
             }
         }
     };
 
     let param_env = tcx.param_env(def_id);
     let mut checker = UnsafetyChecker::new(
-        mir, visibility_scope_info, tcx, param_env);
+        mir, source_scope_local_data, tcx, param_env);
     checker.visit_mir(mir);
 
     check_unused_unsafe(tcx, def_id, &checker.used_unsafe, &mut checker.inherited_blocks);
@@ -345,6 +350,27 @@ fn unsafety_check_result<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId)
     }
 }
 
+fn unsafe_derive_on_repr_packed<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) {
+    let lint_node_id = match tcx.hir.as_local_node_id(def_id) {
+        Some(node_id) => node_id,
+        None => bug!("checking unsafety for non-local def id {:?}", def_id)
+    };
+
+    // FIXME: when we make this a hard error, this should have its
+    // own error code.
+    let message = if tcx.generics_of(def_id).own_counts().types != 0 {
+        format!("#[derive] can't be used on a #[repr(packed)] struct with \
+                 type parameters (error E0133)")
+    } else {
+        format!("#[derive] can't be used on a #[repr(packed)] struct that \
+                 does not derive Copy (error E0133)")
+    };
+    tcx.lint_node(SAFE_PACKED_BORROWS,
+                  lint_node_id,
+                  tcx.def_span(def_id),
+                  &message);
+}
+
 /// Return the NodeId for an enclosing scope that is also `unsafe`
 fn is_enclosed(tcx: TyCtxt,
                used_unsafe: &FxHashSet<ast::NodeId>,
@@ -354,10 +380,13 @@ fn is_enclosed(tcx: TyCtxt,
         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, _, _, _, _),
+            node: hir::ItemFn(_, fn_unsafety, _, _, _, _),
             ..
         })) = tcx.hir.find(parent_id) {
-            Some(("fn".to_string(), parent_id))
+            match fn_unsafety {
+                hir::Unsafety::Unsafe => Some(("fn".to_string(), parent_id)),
+                hir::Unsafety::Normal => None,
+            }
         } else {
             is_enclosed(tcx, used_unsafe, parent_id)
         }
@@ -367,23 +396,40 @@ fn is_enclosed(tcx: TyCtxt,
 }
 
 fn report_unused_unsafe(tcx: TyCtxt, used_unsafe: &FxHashSet<ast::NodeId>, id: ast::NodeId) {
-    let span = tcx.hir.span(id);
-    let mut db = tcx.struct_span_lint_node(UNUSED_UNSAFE, id, span, "unnecessary `unsafe` block");
-    db.span_label(span, "unnecessary `unsafe` block");
+    let span = tcx.sess.codemap().def_span(tcx.hir.span(id));
+    let msg = "unnecessary `unsafe` block";
+    let mut db = tcx.struct_span_lint_node(UNUSED_UNSAFE, id, span, msg);
+    db.span_label(span, msg);
     if let Some((kind, id)) = is_enclosed(tcx, used_unsafe, id) {
-        db.span_note(tcx.hir.span(id),
-                     &format!("because it's nested under this `unsafe` {}", kind));
+        db.span_label(tcx.sess.codemap().def_span(tcx.hir.span(id)),
+                      format!("because it's nested under this `unsafe` {}", kind));
     }
     db.emit();
 }
 
+fn builtin_derive_def_id<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> Option<DefId> {
+    debug!("builtin_derive_def_id({:?})", def_id);
+    if let Some(impl_def_id) = tcx.impl_of_method(def_id) {
+        if tcx.has_attr(impl_def_id, "automatically_derived") {
+            debug!("builtin_derive_def_id({:?}) - is {:?}", def_id, impl_def_id);
+            Some(impl_def_id)
+        } else {
+            debug!("builtin_derive_def_id({:?}) - not automatically derived", def_id);
+            None
+        }
+    } else {
+        debug!("builtin_derive_def_id({:?}) - not a method", def_id);
+        None
+    }
+}
+
 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,
-        _ => {}
-    };
+
+    // closures are handled by their parent fn.
+    if tcx.is_closure(def_id) {
+        return;
+    }
 
     let UnsafetyCheckResult {
         violations,
@@ -391,21 +437,35 @@ pub fn check_unsafety<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) {
     } = tcx.unsafety_check_result(def_id);
 
     for &UnsafetyViolation {
-        source_info, description, lint_node_id
+        source_info, description, kind
     } in violations.iter() {
         // 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();
+        match kind {
+            UnsafetyViolationKind::General => {
+                struct_span_err!(
+                    tcx.sess, source_info.span, E0133,
+                    "{} requires unsafe function or block", description)
+                    .span_label(source_info.span, &description.as_str()[..])
+                    .emit();
+            }
+            UnsafetyViolationKind::ExternStatic(lint_node_id) => {
+                tcx.lint_node(SAFE_EXTERN_STATICS,
+                              lint_node_id,
+                              source_info.span,
+                              &format!("{} requires unsafe function or \
+                                        block (error E0133)", &description.as_str()[..]));
+            }
+            UnsafetyViolationKind::BorrowPacked(lint_node_id) => {
+                if let Some(impl_def_id) = builtin_derive_def_id(tcx, def_id) {
+                    tcx.unsafe_derive_on_repr_packed(impl_def_id);
+                } else {
+                    tcx.lint_node(SAFE_PACKED_BORROWS,
+                                  lint_node_id,
+                                  source_info.span,
+                                  &format!("{} requires unsafe function or \
+                                            block (error E0133)", &description.as_str()[..]));
+                }
+            }
         }
     }