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>,
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,
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)
}
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)
}
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
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) {
pub(crate) fn provide(providers: &mut Providers) {
*providers = Providers {
unsafety_check_result,
+ unsafe_derive_on_repr_packed,
..*providers
};
}
// `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);
}
}
+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>,
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)
}
}
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,
} = 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()[..]));
+ }
+ }
}
}