1 use crate::build
::ExprCategory
;
2 use crate::thir
::visit
::{self, Visitor}
;
4 use rustc_errors
::struct_span_err
;
6 use rustc_middle
::mir
::BorrowKind
;
7 use rustc_middle
::thir
::*;
8 use rustc_middle
::ty
::{self, ParamEnv, TyCtxt}
;
9 use rustc_session
::lint
::builtin
::{UNSAFE_OP_IN_UNSAFE_FN, UNUSED_UNSAFE}
;
10 use rustc_session
::lint
::Level
;
11 use rustc_span
::def_id
::{DefId, LocalDefId}
;
12 use rustc_span
::symbol
::Symbol
;
17 struct UnsafetyVisitor
<'a
, 'tcx
> {
20 /// The `HirId` of the current scope, which would be the `HirId`
21 /// of the current HIR node, modulo adjustments. Used for lint levels.
22 hir_context
: hir
::HirId
,
23 /// The current "safety context". This notably tracks whether we are in an
24 /// `unsafe` block, and whether it has been used.
25 safety_context
: SafetyContext
,
26 body_unsafety
: BodyUnsafety
,
27 /// The `#[target_feature]` attributes of the body. Used for checking
28 /// calls to functions with `#[target_feature]` (RFC 2396).
29 body_target_features
: &'tcx Vec
<Symbol
>,
30 in_possible_lhs_union_assign
: bool
,
31 in_union_destructure
: bool
,
32 param_env
: ParamEnv
<'tcx
>,
36 impl<'tcx
> UnsafetyVisitor
<'_
, 'tcx
> {
37 fn in_safety_context(&mut self, safety_context
: SafetyContext
, f
: impl FnOnce(&mut Self)) {
39 SafetyContext
::UnsafeBlock { span: enclosing_span, .. }
,
40 SafetyContext
::UnsafeBlock { span: block_span, hir_id, .. }
,
41 ) = (self.safety_context
, safety_context
)
43 self.warn_unused_unsafe(
46 Some((self.tcx
.sess
.source_map().guess_head_span(enclosing_span
), "block")),
50 let prev_context
= self.safety_context
;
51 self.safety_context
= safety_context
;
55 if let SafetyContext
::UnsafeBlock { used: false, span, hir_id }
= self.safety_context
{
56 self.warn_unused_unsafe(
59 if self.unsafe_op_in_unsafe_fn_allowed() {
60 self.body_unsafety
.unsafe_fn_sig_span().map(|span
| (span
, "fn"))
66 self.safety_context
= prev_context
;
70 fn requires_unsafe(&mut self, span
: Span
, kind
: UnsafeOpKind
) {
71 let (description
, note
) = kind
.description_and_note();
72 let unsafe_op_in_unsafe_fn_allowed
= self.unsafe_op_in_unsafe_fn_allowed();
73 match self.safety_context
{
74 SafetyContext
::BuiltinUnsafeBlock
=> {}
75 SafetyContext
::UnsafeBlock { ref mut used, .. }
=> {
76 if !self.body_unsafety
.is_unsafe() || !unsafe_op_in_unsafe_fn_allowed
{
77 // Mark this block as useful
81 SafetyContext
::UnsafeFn
if unsafe_op_in_unsafe_fn_allowed
=> {}
82 SafetyContext
::UnsafeFn
=> {
83 // unsafe_op_in_unsafe_fn is disallowed
84 self.tcx
.struct_span_lint_hir(
85 UNSAFE_OP_IN_UNSAFE_FN
,
90 "{} is unsafe and requires unsafe block (error E0133)",
93 .span_label(span
, description
)
99 SafetyContext
::Safe
=> {
100 let fn_sugg
= if unsafe_op_in_unsafe_fn_allowed { " function or" }
else { "" }
;
105 "{} is unsafe and requires unsafe{} block",
109 .span_label(span
, description
)
116 fn warn_unused_unsafe(
120 enclosing_unsafe
: Option
<(Span
, &'
static str)>,
122 let block_span
= self.tcx
.sess
.source_map().guess_head_span(block_span
);
123 self.tcx
.struct_span_lint_hir(UNUSED_UNSAFE
, hir_id
, block_span
, |lint
| {
124 let msg
= "unnecessary `unsafe` block";
125 let mut db
= lint
.build(msg
);
126 db
.span_label(block_span
, msg
);
127 if let Some((span
, kind
)) = enclosing_unsafe
{
128 db
.span_label(span
, format
!("because it's nested under this `unsafe` {}", kind
));
134 /// Whether the `unsafe_op_in_unsafe_fn` lint is `allow`ed at the current HIR node.
135 fn unsafe_op_in_unsafe_fn_allowed(&self) -> bool
{
136 self.tcx
.lint_level_at_node(UNSAFE_OP_IN_UNSAFE_FN
, self.hir_context
).0 == Level
::Allow
140 // Searches for accesses to layout constrained fields.
141 struct LayoutConstrainedPlaceVisitor
<'a
, 'tcx
> {
143 thir
: &'a Thir
<'tcx
>,
147 impl<'a
, 'tcx
> LayoutConstrainedPlaceVisitor
<'a
, 'tcx
> {
148 fn new(thir
: &'a Thir
<'tcx
>, tcx
: TyCtxt
<'tcx
>) -> Self {
149 Self { found: false, thir, tcx }
153 impl<'a
, 'tcx
> Visitor
<'a
, 'tcx
> for LayoutConstrainedPlaceVisitor
<'a
, 'tcx
> {
154 fn thir(&self) -> &'a Thir
<'tcx
> {
158 fn visit_expr(&mut self, expr
: &Expr
<'tcx
>) {
160 ExprKind
::Field { lhs, .. }
=> {
161 if let ty
::Adt(adt_def
, _
) = self.thir
[lhs
].ty
.kind() {
162 if (Bound
::Unbounded
, Bound
::Unbounded
)
163 != self.tcx
.layout_scalar_valid_range(adt_def
.did
)
168 visit
::walk_expr(self, expr
);
171 // Keep walking through the expression as long as we stay in the same
172 // place, i.e. the expression is a place expression and not a dereference
173 // (since dereferencing something leads us to a different place).
174 ExprKind
::Deref { .. }
=> {}
175 ref kind
if ExprCategory
::of(kind
).map_or(true, |cat
| cat
== ExprCategory
::Place
) => {
176 visit
::walk_expr(self, expr
);
184 impl<'a
, 'tcx
> Visitor
<'a
, 'tcx
> for UnsafetyVisitor
<'a
, 'tcx
> {
185 fn thir(&self) -> &'a Thir
<'tcx
> {
189 fn visit_block(&mut self, block
: &Block
) {
190 match block
.safety_mode
{
191 // compiler-generated unsafe code should not count towards the usefulness of
192 // an outer unsafe block
193 BlockSafety
::BuiltinUnsafe
=> {
194 self.in_safety_context(SafetyContext
::BuiltinUnsafeBlock
, |this
| {
195 visit
::walk_block(this
, block
)
198 BlockSafety
::ExplicitUnsafe(hir_id
) => {
199 self.in_safety_context(
200 SafetyContext
::UnsafeBlock { span: block.span, hir_id, used: false }
,
201 |this
| visit
::walk_block(this
, block
),
204 BlockSafety
::Safe
=> {
205 visit
::walk_block(self, block
);
210 fn visit_pat(&mut self, pat
: &Pat
<'tcx
>) {
211 if self.in_union_destructure
{
213 // binding to a variable allows getting stuff out of variable
214 PatKind
::Binding { .. }
215 // match is conditional on having this value
216 | PatKind
::Constant { .. }
217 | PatKind
::Variant { .. }
218 | PatKind
::Leaf { .. }
219 | PatKind
::Deref { .. }
220 | PatKind
::Range { .. }
221 | PatKind
::Slice { .. }
222 | PatKind
::Array { .. }
=> {
223 self.requires_unsafe(pat
.span
, AccessToUnionField
);
224 return; // we can return here since this already requires unsafe
226 // wildcard doesn't take anything
228 // these just wrap other patterns
230 PatKind
::AscribeUserType { .. }
=> {}
235 PatKind
::Leaf { .. }
=> {
236 if let ty
::Adt(adt_def
, ..) = pat
.ty
.kind() {
237 if adt_def
.is_union() {
238 let old_in_union_destructure
=
239 std
::mem
::replace(&mut self.in_union_destructure
, true);
240 visit
::walk_pat(self, pat
);
241 self.in_union_destructure
= old_in_union_destructure
;
242 } else if (Bound
::Unbounded
, Bound
::Unbounded
)
243 != self.tcx
.layout_scalar_valid_range(adt_def
.did
)
245 let old_inside_adt
= std
::mem
::replace(&mut self.inside_adt
, true);
246 visit
::walk_pat(self, pat
);
247 self.inside_adt
= old_inside_adt
;
249 visit
::walk_pat(self, pat
);
252 visit
::walk_pat(self, pat
);
255 PatKind
::Binding { mode: BindingMode::ByRef(borrow_kind), ty, .. }
=> {
257 if let ty
::Ref(_
, ty
, _
) = ty
.kind() {
259 BorrowKind
::Shallow
| BorrowKind
::Shared
| BorrowKind
::Unique
=> {
260 if !ty
.is_freeze(self.tcx
.at(pat
.span
), self.param_env
) {
261 self.requires_unsafe(pat
.span
, BorrowOfLayoutConstrainedField
);
264 BorrowKind
::Mut { .. }
=> {
265 self.requires_unsafe(pat
.span
, MutationOfLayoutConstrainedField
);
271 "BindingMode::ByRef in pattern, but found non-reference type {}",
276 visit
::walk_pat(self, pat
);
278 PatKind
::Deref { .. }
=> {
279 let old_inside_adt
= std
::mem
::replace(&mut self.inside_adt
, false);
280 visit
::walk_pat(self, pat
);
281 self.inside_adt
= old_inside_adt
;
284 visit
::walk_pat(self, pat
);
289 fn visit_expr(&mut self, expr
: &Expr
<'tcx
>) {
290 // could we be in a the LHS of an assignment of a union?
292 ExprKind
::Field { .. }
293 | ExprKind
::VarRef { .. }
294 | ExprKind
::UpvarRef { .. }
295 | ExprKind
::Scope { .. }
296 | ExprKind
::Cast { .. }
=> {}
298 ExprKind
::AddressOf { .. }
299 | ExprKind
::Adt { .. }
300 | ExprKind
::Array { .. }
301 | ExprKind
::Binary { .. }
302 | ExprKind
::Block { .. }
303 | ExprKind
::Borrow { .. }
304 | ExprKind
::Literal { .. }
305 | ExprKind
::ConstBlock { .. }
306 | ExprKind
::Deref { .. }
307 | ExprKind
::Index { .. }
308 | ExprKind
::NeverToAny { .. }
309 | ExprKind
::PlaceTypeAscription { .. }
310 | ExprKind
::ValueTypeAscription { .. }
311 | ExprKind
::Pointer { .. }
312 | ExprKind
::Repeat { .. }
313 | ExprKind
::StaticRef { .. }
314 | ExprKind
::ThreadLocalRef { .. }
315 | ExprKind
::Tuple { .. }
316 | ExprKind
::Unary { .. }
317 | ExprKind
::Call { .. }
318 | ExprKind
::Assign { .. }
319 | ExprKind
::AssignOp { .. }
320 | ExprKind
::Break { .. }
321 | ExprKind
::Closure { .. }
322 | ExprKind
::Continue { .. }
323 | ExprKind
::Return { .. }
324 | ExprKind
::Yield { .. }
325 | ExprKind
::Loop { .. }
326 | ExprKind
::Match { .. }
327 | ExprKind
::Box { .. }
328 | ExprKind
::If { .. }
329 | ExprKind
::InlineAsm { .. }
330 | ExprKind
::LlvmInlineAsm { .. }
331 | ExprKind
::LogicalOp { .. }
332 | ExprKind
::Use { .. }
=> self.in_possible_lhs_union_assign
= false,
335 ExprKind
::Scope { value, lint_level: LintLevel::Explicit(hir_id), region_scope: _ }
=> {
336 let prev_id
= self.hir_context
;
337 self.hir_context
= hir_id
;
338 self.visit_expr(&self.thir
[value
]);
339 self.hir_context
= prev_id
;
340 return; // don't visit the whole expression
342 ExprKind
::Call { fun, ty: _, args: _, from_hir_call: _, fn_span: _ }
=> {
343 if self.thir
[fun
].ty
.fn_sig(self.tcx
).unsafety() == hir
::Unsafety
::Unsafe
{
344 self.requires_unsafe(expr
.span
, CallToUnsafeFunction
);
345 } else if let &ty
::FnDef(func_did
, _
) = self.thir
[fun
].ty
.kind() {
346 // If the called function has target features the calling function hasn't,
347 // the call requires `unsafe`. Don't check this on wasm
348 // targets, though. For more information on wasm see the
349 // is_like_wasm check in typeck/src/collect.rs
350 if !self.tcx
.sess
.target
.options
.is_like_wasm
353 .codegen_fn_attrs(func_did
)
356 .all(|feature
| self.body_target_features
.contains(feature
))
358 self.requires_unsafe(expr
.span
, CallToFunctionWith
);
362 ExprKind
::Deref { arg }
=> {
363 if let ExprKind
::StaticRef { def_id, .. }
= self.thir
[arg
].kind
{
364 if self.tcx
.is_mutable_static(def_id
) {
365 self.requires_unsafe(expr
.span
, UseOfMutableStatic
);
366 } else if self.tcx
.is_foreign_item(def_id
) {
367 self.requires_unsafe(expr
.span
, UseOfExternStatic
);
369 } else if self.thir
[arg
].ty
.is_unsafe_ptr() {
370 self.requires_unsafe(expr
.span
, DerefOfRawPointer
);
373 ExprKind
::InlineAsm { .. }
| ExprKind
::LlvmInlineAsm { .. }
=> {
374 self.requires_unsafe(expr
.span
, UseOfInlineAssembly
);
376 ExprKind
::Adt(box Adt
{
383 }) => match self.tcx
.layout_scalar_valid_range(adt_def
.did
) {
384 (Bound
::Unbounded
, Bound
::Unbounded
) => {}
385 _
=> self.requires_unsafe(expr
.span
, InitializingTypeWith
),
394 let closure_id
= closure_id
.expect_local();
395 let closure_def
= if let Some((did
, const_param_id
)) =
396 ty
::WithOptConstParam
::try_lookup(closure_id
, self.tcx
)
398 ty
::WithOptConstParam { did, const_param_did: Some(const_param_id) }
400 ty
::WithOptConstParam
::unknown(closure_id
)
402 let (closure_thir
, expr
) = self.tcx
.thir_body(closure_def
);
403 let closure_thir
= &closure_thir
.borrow();
404 let hir_context
= self.tcx
.hir().local_def_id_to_hir_id(closure_id
);
405 let mut closure_visitor
=
406 UnsafetyVisitor { thir: closure_thir, hir_context, ..*self }
;
407 closure_visitor
.visit_expr(&closure_thir
[expr
]);
408 // Unsafe blocks can be used in closures, make sure to take it into account
409 self.safety_context
= closure_visitor
.safety_context
;
411 ExprKind
::Field { lhs, .. }
=> {
412 // assigning to union field is okay for AccessToUnionField
413 if let ty
::Adt(adt_def
, _
) = &self.thir
[lhs
].ty
.kind() {
414 if adt_def
.is_union() {
415 if self.in_possible_lhs_union_assign
{
416 // FIXME: trigger AssignToDroppingUnionField unsafety if needed
418 self.requires_unsafe(expr
.span
, AccessToUnionField
);
423 ExprKind
::Assign { lhs, rhs }
| ExprKind
::AssignOp { lhs, rhs, .. }
=> {
424 // First, check whether we are mutating a layout constrained field
425 let mut visitor
= LayoutConstrainedPlaceVisitor
::new(self.thir
, self.tcx
);
426 visit
::walk_expr(&mut visitor
, &self.thir
[lhs
]);
428 self.requires_unsafe(expr
.span
, MutationOfLayoutConstrainedField
);
431 // Second, check for accesses to union fields
432 // don't have any special handling for AssignOp since it causes a read *and* write to lhs
433 if matches
!(expr
.kind
, ExprKind
::Assign { .. }
) {
434 // assigning to a union is safe, check here so it doesn't get treated as a read later
435 self.in_possible_lhs_union_assign
= true;
436 visit
::walk_expr(self, &self.thir()[lhs
]);
437 self.in_possible_lhs_union_assign
= false;
438 visit
::walk_expr(self, &self.thir()[rhs
]);
439 return; // we have already visited everything by now
442 ExprKind
::Borrow { borrow_kind, arg }
=> match borrow_kind
{
443 BorrowKind
::Shallow
| BorrowKind
::Shared
| BorrowKind
::Unique
=> {
446 .is_freeze(self.tcx
.at(self.thir
[arg
].span
), self.param_env
)
448 let mut visitor
= LayoutConstrainedPlaceVisitor
::new(self.thir
, self.tcx
);
449 visit
::walk_expr(&mut visitor
, expr
);
451 self.requires_unsafe(expr
.span
, BorrowOfLayoutConstrainedField
);
455 BorrowKind
::Mut { .. }
=> {
456 let mut visitor
= LayoutConstrainedPlaceVisitor
::new(self.thir
, self.tcx
);
457 visit
::walk_expr(&mut visitor
, expr
);
459 self.requires_unsafe(expr
.span
, MutationOfLayoutConstrainedField
);
465 visit
::walk_expr(self, expr
);
469 #[derive(Clone, Copy)]
474 UnsafeBlock { span: Span, hir_id: hir::HirId, used: bool }
,
477 #[derive(Clone, Copy)]
479 /// The body is not unsafe.
481 /// The body is an unsafe function. The span points to
482 /// the signature of the function.
487 /// Returns whether the body is unsafe.
488 fn is_unsafe(&self) -> bool
{
489 matches
!(self, BodyUnsafety
::Unsafe(_
))
492 /// If the body is unsafe, returns the `Span` of its signature.
493 fn unsafe_fn_sig_span(self) -> Option
<Span
> {
495 BodyUnsafety
::Unsafe(span
) => Some(span
),
496 BodyUnsafety
::Safe
=> None
,
501 #[derive(Clone, Copy, PartialEq)]
503 CallToUnsafeFunction
,
505 InitializingTypeWith
,
509 #[allow(dead_code)] // FIXME
510 AssignToDroppingUnionField
,
512 #[allow(dead_code)] // FIXME
513 MutationOfLayoutConstrainedField
,
514 #[allow(dead_code)] // FIXME
515 BorrowOfLayoutConstrainedField
,
522 pub fn description_and_note(&self) -> (&'
static str, &'
static str) {
524 CallToUnsafeFunction
=> (
525 "call to unsafe function",
526 "consult the function's documentation for information on how to avoid undefined \
529 UseOfInlineAssembly
=> (
530 "use of inline assembly",
531 "inline assembly is entirely unchecked and can cause undefined behavior",
533 InitializingTypeWith
=> (
534 "initializing type with `rustc_layout_scalar_valid_range` attr",
535 "initializing a layout restricted type's field with a value outside the valid \
536 range is undefined behavior",
538 UseOfMutableStatic
=> (
539 "use of mutable static",
540 "mutable statics can be mutated by multiple threads: aliasing violations or data \
541 races will cause undefined behavior",
543 UseOfExternStatic
=> (
544 "use of extern static",
545 "extern statics are not controlled by the Rust type system: invalid data, \
546 aliasing violations or data races will cause undefined behavior",
548 DerefOfRawPointer
=> (
549 "dereference of raw pointer",
550 "raw pointers may be null, dangling or unaligned; they can violate aliasing rules \
551 and cause data races: all of these are undefined behavior",
553 AssignToDroppingUnionField
=> (
554 "assignment to union field that might need dropping",
555 "the previous content of the field will be dropped, which causes undefined \
556 behavior if the field was not properly initialized",
558 AccessToUnionField
=> (
559 "access to union field",
560 "the field may not be properly initialized: using uninitialized data will cause \
563 MutationOfLayoutConstrainedField
=> (
564 "mutation of layout constrained field",
565 "mutating layout constrained fields cannot statically be checked for valid values",
567 BorrowOfLayoutConstrainedField
=> (
568 "borrow of layout constrained field with interior mutability",
569 "references to fields of layout constrained fields lose the constraints. Coupled \
570 with interior mutability, the field can be changed to invalid values",
572 CallToFunctionWith
=> (
573 "call to function with `#[target_feature]`",
574 "can only be called if the required target features are available",
580 pub fn check_unsafety
<'tcx
>(tcx
: TyCtxt
<'tcx
>, def
: ty
::WithOptConstParam
<LocalDefId
>) {
581 // THIR unsafeck is gated under `-Z thir-unsafeck`
582 if !tcx
.sess
.opts
.debugging_opts
.thir_unsafeck
{
586 // Closures are handled by their owner, if it has a body
587 if tcx
.is_closure(def
.did
.to_def_id()) {
588 let owner
= tcx
.hir().local_def_id_to_hir_id(def
.did
).owner
;
589 let owner_hir_id
= tcx
.hir().local_def_id_to_hir_id(owner
);
591 if tcx
.hir().maybe_body_owned_by(owner_hir_id
).is_some() {
592 tcx
.ensure().thir_check_unsafety(owner
);
597 let (thir
, expr
) = tcx
.thir_body(def
);
598 let thir
= &thir
.borrow();
599 // If `thir` is empty, a type error occured, skip this body.
600 if thir
.exprs
.is_empty() {
604 let hir_id
= tcx
.hir().local_def_id_to_hir_id(def
.did
);
605 let body_unsafety
= tcx
.hir().fn_sig_by_hir_id(hir_id
).map_or(BodyUnsafety
::Safe
, |fn_sig
| {
606 if fn_sig
.header
.unsafety
== hir
::Unsafety
::Unsafe
{
607 BodyUnsafety
::Unsafe(fn_sig
.span
)
612 let body_target_features
= &tcx
.codegen_fn_attrs(def
.did
).target_features
;
614 if body_unsafety
.is_unsafe() { SafetyContext::UnsafeFn }
else { SafetyContext::Safe }
;
615 let mut visitor
= UnsafetyVisitor
{
621 body_target_features
,
622 in_possible_lhs_union_assign
: false,
623 in_union_destructure
: false,
624 param_env
: tcx
.param_env(def
.did
),
627 visitor
.visit_expr(&thir
[expr
]);
630 crate fn thir_check_unsafety
<'tcx
>(tcx
: TyCtxt
<'tcx
>, def_id
: LocalDefId
) {
631 if let Some(def
) = ty
::WithOptConstParam
::try_lookup(def_id
, tcx
) {
632 tcx
.thir_check_unsafety_for_const_arg(def
)
634 check_unsafety(tcx
, ty
::WithOptConstParam
::unknown(def_id
))
638 crate fn thir_check_unsafety_for_const_arg
<'tcx
>(
640 (did
, param_did
): (LocalDefId
, DefId
),
642 check_unsafety(tcx
, ty
::WithOptConstParam { did, const_param_did: Some(param_did) }
)