]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/transform/check_unsafety.rs
Rollup merge of #70038 - DutchGhost:const-forget-tests, r=RalfJung
[rust.git] / src / librustc_mir / transform / check_unsafety.rs
1 use rustc::mir::visit::{MutatingUseContext, PlaceContext, Visitor};
2 use rustc::mir::*;
3 use rustc::ty::cast::CastTy;
4 use rustc::ty::query::Providers;
5 use rustc::ty::{self, TyCtxt};
6 use rustc_data_structures::fx::FxHashSet;
7 use rustc_errors::struct_span_err;
8 use rustc_hir as hir;
9 use rustc_hir::def_id::DefId;
10 use rustc_hir::intravisit;
11 use rustc_hir::Node;
12 use rustc_session::lint::builtin::{SAFE_PACKED_BORROWS, UNUSED_UNSAFE};
13 use rustc_span::symbol::{sym, Symbol};
14
15 use std::ops::Bound;
16
17 use crate::const_eval::{is_const_fn, is_min_const_fn};
18 use crate::util;
19
20 pub struct UnsafetyChecker<'a, 'tcx> {
21     body: &'a Body<'tcx>,
22     const_context: bool,
23     min_const_fn: bool,
24     violations: Vec<UnsafetyViolation>,
25     source_info: SourceInfo,
26     tcx: TyCtxt<'tcx>,
27     param_env: ty::ParamEnv<'tcx>,
28     /// Mark an `unsafe` block as used, so we don't lint it.
29     used_unsafe: FxHashSet<hir::HirId>,
30     inherited_blocks: Vec<(hir::HirId, bool)>,
31 }
32
33 impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> {
34     fn new(
35         const_context: bool,
36         min_const_fn: bool,
37         body: &'a Body<'tcx>,
38         tcx: TyCtxt<'tcx>,
39         param_env: ty::ParamEnv<'tcx>,
40     ) -> Self {
41         // sanity check
42         if min_const_fn {
43             assert!(const_context);
44         }
45         Self {
46             body,
47             const_context,
48             min_const_fn,
49             violations: vec![],
50             source_info: SourceInfo { span: body.span, scope: OUTERMOST_SOURCE_SCOPE },
51             tcx,
52             param_env,
53             used_unsafe: Default::default(),
54             inherited_blocks: vec![],
55         }
56     }
57 }
58
59 impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
60     fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
61         self.source_info = terminator.source_info;
62         match terminator.kind {
63             TerminatorKind::Goto { .. }
64             | TerminatorKind::SwitchInt { .. }
65             | TerminatorKind::Drop { .. }
66             | TerminatorKind::Yield { .. }
67             | TerminatorKind::Assert { .. }
68             | TerminatorKind::DropAndReplace { .. }
69             | TerminatorKind::GeneratorDrop
70             | TerminatorKind::Resume
71             | TerminatorKind::Abort
72             | TerminatorKind::Return
73             | TerminatorKind::Unreachable
74             | TerminatorKind::FalseEdges { .. }
75             | TerminatorKind::FalseUnwind { .. } => {
76                 // safe (at least as emitted during MIR construction)
77             }
78
79             TerminatorKind::Call { ref func, .. } => {
80                 let func_ty = func.ty(self.body, self.tcx);
81                 let sig = func_ty.fn_sig(self.tcx);
82                 if let hir::Unsafety::Unsafe = sig.unsafety() {
83                     self.require_unsafe(
84                         "call to unsafe function",
85                         "consult the function's documentation for information on how to avoid \
86                          undefined behavior",
87                         UnsafetyViolationKind::GeneralAndConstFn,
88                     )
89                 }
90             }
91         }
92         self.super_terminator(terminator, location);
93     }
94
95     fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
96         self.source_info = statement.source_info;
97         match statement.kind {
98             StatementKind::Assign(..)
99             | StatementKind::FakeRead(..)
100             | StatementKind::SetDiscriminant { .. }
101             | StatementKind::StorageLive(..)
102             | StatementKind::StorageDead(..)
103             | StatementKind::Retag { .. }
104             | StatementKind::AscribeUserType(..)
105             | StatementKind::Nop => {
106                 // safe (at least as emitted during MIR construction)
107             }
108
109             StatementKind::InlineAsm { .. } => self.require_unsafe(
110                 "use of inline assembly",
111                 "inline assembly is entirely unchecked and can cause undefined behavior",
112                 UnsafetyViolationKind::General,
113             ),
114         }
115         self.super_statement(statement, location);
116     }
117
118     fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
119         match rvalue {
120             Rvalue::Aggregate(box ref aggregate, _) => match aggregate {
121                 &AggregateKind::Array(..) | &AggregateKind::Tuple => {}
122                 &AggregateKind::Adt(ref def, ..) => {
123                     match self.tcx.layout_scalar_valid_range(def.did) {
124                         (Bound::Unbounded, Bound::Unbounded) => {}
125                         _ => self.require_unsafe(
126                             "initializing type with `rustc_layout_scalar_valid_range` attr",
127                             "initializing a layout restricted type's field with a value \
128                                 outside the valid range is undefined behavior",
129                             UnsafetyViolationKind::GeneralAndConstFn,
130                         ),
131                     }
132                 }
133                 &AggregateKind::Closure(def_id, _) | &AggregateKind::Generator(def_id, _, _) => {
134                     let UnsafetyCheckResult { violations, unsafe_blocks } =
135                         self.tcx.unsafety_check_result(def_id);
136                     self.register_violations(&violations, &unsafe_blocks);
137                 }
138             },
139             // casting pointers to ints is unsafe in const fn because the const evaluator cannot
140             // possibly know what the result of various operations like `address / 2` would be
141             // pointers during const evaluation have no integral address, only an abstract one
142             Rvalue::Cast(CastKind::Misc, ref operand, cast_ty)
143                 if self.const_context && self.tcx.features().const_raw_ptr_to_usize_cast =>
144             {
145                 let operand_ty = operand.ty(self.body, self.tcx);
146                 let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast");
147                 let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
148                 match (cast_in, cast_out) {
149                     (CastTy::Ptr(_), CastTy::Int(_)) | (CastTy::FnPtr, CastTy::Int(_)) => {
150                         self.require_unsafe(
151                             "cast of pointer to int",
152                             "casting pointers to integers in constants",
153                             UnsafetyViolationKind::General,
154                         );
155                     }
156                     _ => {}
157                 }
158             }
159             // raw pointer and fn pointer operations are unsafe as it is not clear whether one
160             // pointer would be "less" or "equal" to another, because we cannot know where llvm
161             // or the linker will place various statics in memory. Without this information the
162             // result of a comparison of addresses would differ between runtime and compile-time.
163             Rvalue::BinaryOp(_, ref lhs, _)
164                 if self.const_context && self.tcx.features().const_compare_raw_pointers =>
165             {
166                 if let ty::RawPtr(_) | ty::FnPtr(..) = lhs.ty(self.body, self.tcx).kind {
167                     self.require_unsafe(
168                         "pointer operation",
169                         "operations on pointers in constants",
170                         UnsafetyViolationKind::General,
171                     );
172                 }
173             }
174             _ => {}
175         }
176         self.super_rvalue(rvalue, location);
177     }
178
179     fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, _location: Location) {
180         // prevent
181         // * `&mut x.field`
182         // * `x.field = y;`
183         // * `&x.field` if `field`'s type has interior mutability
184         // because either of these would allow modifying the layout constrained field and
185         // insert values that violate the layout constraints.
186         if context.is_mutating_use() || context.is_borrow() {
187             self.check_mut_borrowing_layout_constrained_field(place, context.is_mutating_use());
188         }
189
190         for (i, elem) in place.projection.iter().enumerate() {
191             let proj_base = &place.projection[..i];
192
193             if context.is_borrow() {
194                 if util::is_disaligned(self.tcx, self.body, self.param_env, place) {
195                     let source_info = self.source_info;
196                     let lint_root = self.body.source_scopes[source_info.scope]
197                         .local_data
198                         .as_ref()
199                         .assert_crate_local()
200                         .lint_root;
201                     self.require_unsafe(
202                         "borrow of packed field",
203                         "fields of packed structs might be misaligned: dereferencing a \
204                         misaligned pointer or even just creating a misaligned reference \
205                         is undefined behavior",
206                         UnsafetyViolationKind::BorrowPacked(lint_root),
207                     );
208                 }
209             }
210             let old_source_info = self.source_info;
211             if let [] = proj_base {
212                 let decl = &self.body.local_decls[place.local];
213                 if decl.internal {
214                     if let LocalInfo::StaticRef { def_id, .. } = decl.local_info {
215                         if self.tcx.is_mutable_static(def_id) {
216                             self.require_unsafe(
217                                 "use of mutable static",
218                                 "mutable statics can be mutated by multiple threads: aliasing \
219                             violations or data races will cause undefined behavior",
220                                 UnsafetyViolationKind::General,
221                             );
222                             return;
223                         } else if self.tcx.is_foreign_item(def_id) {
224                             self.require_unsafe(
225                                 "use of extern static",
226                                 "extern statics are not controlled by the Rust type system: \
227                             invalid data, aliasing violations or data races will cause \
228                             undefined behavior",
229                                 UnsafetyViolationKind::General,
230                             );
231                             return;
232                         }
233                     } else {
234                         // Internal locals are used in the `move_val_init` desugaring.
235                         // We want to check unsafety against the source info of the
236                         // desugaring, rather than the source info of the RHS.
237                         self.source_info = self.body.local_decls[place.local].source_info;
238                     }
239                 }
240             }
241             let base_ty = Place::ty_from(place.local, proj_base, self.body, self.tcx).ty;
242             match base_ty.kind {
243                 ty::RawPtr(..) => self.require_unsafe(
244                     "dereference of raw pointer",
245                     "raw pointers may be NULL, dangling or unaligned; they can violate \
246                          aliasing rules and cause data races: all of these are undefined \
247                          behavior",
248                     UnsafetyViolationKind::General,
249                 ),
250                 ty::Adt(adt, _) => {
251                     if adt.is_union() {
252                         if context == PlaceContext::MutatingUse(MutatingUseContext::Store)
253                             || context == PlaceContext::MutatingUse(MutatingUseContext::Drop)
254                             || context == PlaceContext::MutatingUse(MutatingUseContext::AsmOutput)
255                         {
256                             let elem_ty = match elem {
257                                 ProjectionElem::Field(_, ty) => ty,
258                                 _ => span_bug!(
259                                     self.source_info.span,
260                                     "non-field projection {:?} from union?",
261                                     place
262                                 ),
263                             };
264                             if !elem_ty.is_copy_modulo_regions(
265                                 self.tcx,
266                                 self.param_env,
267                                 self.source_info.span,
268                             ) {
269                                 self.require_unsafe(
270                                     "assignment to non-`Copy` union field",
271                                     "the previous content of the field will be dropped, which \
272                                      causes undefined behavior if the field was not properly \
273                                      initialized",
274                                     UnsafetyViolationKind::GeneralAndConstFn,
275                                 )
276                             } else {
277                                 // write to non-move union, safe
278                             }
279                         } else {
280                             self.require_unsafe(
281                                 "access to union field",
282                                 "the field may not be properly initialized: using \
283                                  uninitialized data will cause undefined behavior",
284                                 UnsafetyViolationKind::GeneralAndConstFn,
285                             )
286                         }
287                     }
288                 }
289                 _ => {}
290             }
291             self.source_info = old_source_info;
292         }
293     }
294 }
295
296 impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> {
297     fn require_unsafe(
298         &mut self,
299         description: &'static str,
300         details: &'static str,
301         kind: UnsafetyViolationKind,
302     ) {
303         let source_info = self.source_info;
304         self.register_violations(
305             &[UnsafetyViolation {
306                 source_info,
307                 description: Symbol::intern(description),
308                 details: Symbol::intern(details),
309                 kind,
310             }],
311             &[],
312         );
313     }
314
315     fn register_violations(
316         &mut self,
317         violations: &[UnsafetyViolation],
318         unsafe_blocks: &[(hir::HirId, bool)],
319     ) {
320         let safety = self.body.source_scopes[self.source_info.scope]
321             .local_data
322             .as_ref()
323             .assert_crate_local()
324             .safety;
325         let within_unsafe = match safety {
326             // `unsafe` blocks are required in safe code
327             Safety::Safe => {
328                 for violation in violations {
329                     let mut violation = *violation;
330                     match violation.kind {
331                         UnsafetyViolationKind::GeneralAndConstFn
332                         | UnsafetyViolationKind::General => {}
333                         UnsafetyViolationKind::BorrowPacked(_) => {
334                             if self.min_const_fn {
335                                 // const fns don't need to be backwards compatible and can
336                                 // emit these violations as a hard error instead of a backwards
337                                 // compat lint
338                                 violation.kind = UnsafetyViolationKind::General;
339                             }
340                         }
341                     }
342                     if !self.violations.contains(&violation) {
343                         self.violations.push(violation)
344                     }
345                 }
346                 false
347             }
348             // `unsafe` function bodies allow unsafe without additional unsafe blocks
349             Safety::BuiltinUnsafe | Safety::FnUnsafe => true,
350             Safety::ExplicitUnsafe(hir_id) => {
351                 // mark unsafe block as used if there are any unsafe operations inside
352                 if !violations.is_empty() {
353                     self.used_unsafe.insert(hir_id);
354                 }
355                 // only some unsafety is allowed in const fn
356                 if self.min_const_fn {
357                     for violation in violations {
358                         match violation.kind {
359                             // these unsafe things are stable in const fn
360                             UnsafetyViolationKind::GeneralAndConstFn => {}
361                             // these things are forbidden in const fns
362                             UnsafetyViolationKind::General
363                             | UnsafetyViolationKind::BorrowPacked(_) => {
364                                 let mut violation = *violation;
365                                 // const fns don't need to be backwards compatible and can
366                                 // emit these violations as a hard error instead of a backwards
367                                 // compat lint
368                                 violation.kind = UnsafetyViolationKind::General;
369                                 if !self.violations.contains(&violation) {
370                                     self.violations.push(violation)
371                                 }
372                             }
373                         }
374                     }
375                 }
376                 true
377             }
378         };
379         self.inherited_blocks.extend(
380             unsafe_blocks.iter().map(|&(hir_id, is_used)| (hir_id, is_used && !within_unsafe)),
381         );
382     }
383     fn check_mut_borrowing_layout_constrained_field(
384         &mut self,
385         place: &Place<'tcx>,
386         is_mut_use: bool,
387     ) {
388         let mut cursor = place.projection.as_ref();
389         while let &[ref proj_base @ .., elem] = cursor {
390             cursor = proj_base;
391
392             match elem {
393                 // Modifications behind a dereference don't affect the value of
394                 // the pointer.
395                 ProjectionElem::Deref => return,
396                 ProjectionElem::Field(..) => {
397                     let ty =
398                         Place::ty_from(place.local, proj_base, &self.body.local_decls, self.tcx).ty;
399                     match ty.kind {
400                         ty::Adt(def, _) => match self.tcx.layout_scalar_valid_range(def.did) {
401                             (Bound::Unbounded, Bound::Unbounded) => {}
402                             _ => {
403                                 let (description, details) = if is_mut_use {
404                                     (
405                                         "mutation of layout constrained field",
406                                         "mutating layout constrained fields cannot statically be \
407                                         checked for valid values",
408                                     )
409
410                                 // Check `is_freeze` as late as possible to avoid cycle errors
411                                 // with opaque types.
412                                 } else if !place.ty(self.body, self.tcx).ty.is_freeze(
413                                     self.tcx,
414                                     self.param_env,
415                                     self.source_info.span,
416                                 ) {
417                                     (
418                                         "borrow of layout constrained field with interior \
419                                         mutability",
420                                         "references to fields of layout constrained fields \
421                                         lose the constraints. Coupled with interior mutability, \
422                                         the field can be changed to invalid values",
423                                     )
424                                 } else {
425                                     continue;
426                                 };
427                                 self.require_unsafe(
428                                     description,
429                                     details,
430                                     UnsafetyViolationKind::GeneralAndConstFn,
431                                 );
432                             }
433                         },
434                         _ => {}
435                     }
436                 }
437                 _ => {}
438             }
439         }
440     }
441 }
442
443 pub(crate) fn provide(providers: &mut Providers<'_>) {
444     *providers = Providers { unsafety_check_result, unsafe_derive_on_repr_packed, ..*providers };
445 }
446
447 struct UnusedUnsafeVisitor<'a> {
448     used_unsafe: &'a FxHashSet<hir::HirId>,
449     unsafe_blocks: &'a mut Vec<(hir::HirId, bool)>,
450 }
451
452 impl<'a, 'tcx> intravisit::Visitor<'tcx> for UnusedUnsafeVisitor<'a> {
453     type Map = intravisit::ErasedMap<'tcx>;
454
455     fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
456         intravisit::NestedVisitorMap::None
457     }
458
459     fn visit_block(&mut self, block: &'tcx hir::Block<'tcx>) {
460         intravisit::walk_block(self, block);
461
462         if let hir::BlockCheckMode::UnsafeBlock(hir::UnsafeSource::UserProvided) = block.rules {
463             self.unsafe_blocks.push((block.hir_id, self.used_unsafe.contains(&block.hir_id)));
464         }
465     }
466 }
467
468 fn check_unused_unsafe(
469     tcx: TyCtxt<'_>,
470     def_id: DefId,
471     used_unsafe: &FxHashSet<hir::HirId>,
472     unsafe_blocks: &mut Vec<(hir::HirId, bool)>,
473 ) {
474     let body_id =
475         tcx.hir().as_local_hir_id(def_id).and_then(|hir_id| tcx.hir().maybe_body_owned_by(hir_id));
476
477     let body_id = match body_id {
478         Some(body) => body,
479         None => {
480             debug!("check_unused_unsafe({:?}) - no body found", def_id);
481             return;
482         }
483     };
484     let body = tcx.hir().body(body_id);
485     debug!("check_unused_unsafe({:?}, body={:?}, used_unsafe={:?})", def_id, body, used_unsafe);
486
487     let mut visitor = UnusedUnsafeVisitor { used_unsafe, unsafe_blocks };
488     intravisit::Visitor::visit_body(&mut visitor, body);
489 }
490
491 fn unsafety_check_result(tcx: TyCtxt<'_>, def_id: DefId) -> UnsafetyCheckResult {
492     debug!("unsafety_violations({:?})", def_id);
493
494     // N.B., this borrow is valid because all the consumers of
495     // `mir_built` force this.
496     let body = &tcx.mir_built(def_id).borrow();
497
498     let param_env = tcx.param_env(def_id);
499
500     let id = tcx.hir().as_local_hir_id(def_id).unwrap();
501     let (const_context, min_const_fn) = match tcx.hir().body_owner_kind(id) {
502         hir::BodyOwnerKind::Closure => (false, false),
503         hir::BodyOwnerKind::Fn => (is_const_fn(tcx, def_id), is_min_const_fn(tcx, def_id)),
504         hir::BodyOwnerKind::Const | hir::BodyOwnerKind::Static(_) => (true, false),
505     };
506     let mut checker = UnsafetyChecker::new(const_context, min_const_fn, body, tcx, param_env);
507     // mir_built ensures that body has a computed cache, so we don't (and can't) attempt to
508     // recompute it here.
509     let body = body.unwrap_read_only();
510     checker.visit_body(body);
511
512     check_unused_unsafe(tcx, def_id, &checker.used_unsafe, &mut checker.inherited_blocks);
513     UnsafetyCheckResult {
514         violations: checker.violations.into(),
515         unsafe_blocks: checker.inherited_blocks.into(),
516     }
517 }
518
519 fn unsafe_derive_on_repr_packed(tcx: TyCtxt<'_>, def_id: DefId) {
520     let lint_hir_id = tcx
521         .hir()
522         .as_local_hir_id(def_id)
523         .unwrap_or_else(|| bug!("checking unsafety for non-local def id {:?}", def_id));
524
525     tcx.struct_span_lint_hir(SAFE_PACKED_BORROWS, lint_hir_id, tcx.def_span(def_id), |lint| {
526         // FIXME: when we make this a hard error, this should have its
527         // own error code.
528         let message = if tcx.generics_of(def_id).own_requires_monomorphization() {
529             "`#[derive]` can't be used on a `#[repr(packed)]` struct with \
530              type or const parameters (error E0133)"
531                 .to_string()
532         } else {
533             "`#[derive]` can't be used on a `#[repr(packed)]` struct that \
534              does not derive Copy (error E0133)"
535                 .to_string()
536         };
537         lint.build(&message).emit()
538     });
539 }
540
541 /// Returns the `HirId` for an enclosing scope that is also `unsafe`.
542 fn is_enclosed(
543     tcx: TyCtxt<'_>,
544     used_unsafe: &FxHashSet<hir::HirId>,
545     id: hir::HirId,
546 ) -> Option<(String, hir::HirId)> {
547     let parent_id = tcx.hir().get_parent_node(id);
548     if parent_id != id {
549         if used_unsafe.contains(&parent_id) {
550             Some(("block".to_string(), parent_id))
551         } else if let Some(Node::Item(&hir::Item {
552             kind: hir::ItemKind::Fn(ref sig, _, _), ..
553         })) = tcx.hir().find(parent_id)
554         {
555             match sig.header.unsafety {
556                 hir::Unsafety::Unsafe => Some(("fn".to_string(), parent_id)),
557                 hir::Unsafety::Normal => None,
558             }
559         } else {
560             is_enclosed(tcx, used_unsafe, parent_id)
561         }
562     } else {
563         None
564     }
565 }
566
567 fn report_unused_unsafe(tcx: TyCtxt<'_>, used_unsafe: &FxHashSet<hir::HirId>, id: hir::HirId) {
568     let span = tcx.sess.source_map().def_span(tcx.hir().span(id));
569     tcx.struct_span_lint_hir(UNUSED_UNSAFE, id, span, |lint| {
570         let msg = "unnecessary `unsafe` block";
571         let mut db = lint.build(msg);
572         db.span_label(span, msg);
573         if let Some((kind, id)) = is_enclosed(tcx, used_unsafe, id) {
574             db.span_label(
575                 tcx.sess.source_map().def_span(tcx.hir().span(id)),
576                 format!("because it's nested under this `unsafe` {}", kind),
577             );
578         }
579         db.emit();
580     });
581 }
582
583 fn builtin_derive_def_id(tcx: TyCtxt<'_>, def_id: DefId) -> Option<DefId> {
584     debug!("builtin_derive_def_id({:?})", def_id);
585     if let Some(impl_def_id) = tcx.impl_of_method(def_id) {
586         if tcx.has_attr(impl_def_id, sym::automatically_derived) {
587             debug!("builtin_derive_def_id({:?}) - is {:?}", def_id, impl_def_id);
588             Some(impl_def_id)
589         } else {
590             debug!("builtin_derive_def_id({:?}) - not automatically derived", def_id);
591             None
592         }
593     } else {
594         debug!("builtin_derive_def_id({:?}) - not a method", def_id);
595         None
596     }
597 }
598
599 pub fn check_unsafety(tcx: TyCtxt<'_>, def_id: DefId) {
600     debug!("check_unsafety({:?})", def_id);
601
602     // closures are handled by their parent fn.
603     if tcx.is_closure(def_id) {
604         return;
605     }
606
607     let UnsafetyCheckResult { violations, unsafe_blocks } = tcx.unsafety_check_result(def_id);
608
609     for &UnsafetyViolation { source_info, description, details, kind } in violations.iter() {
610         // Report an error.
611         match kind {
612             UnsafetyViolationKind::GeneralAndConstFn | UnsafetyViolationKind::General => {
613                 struct_span_err!(
614                     tcx.sess,
615                     source_info.span,
616                     E0133,
617                     "{} is unsafe and requires unsafe function or block",
618                     description
619                 )
620                 .span_label(source_info.span, &*description.as_str())
621                 .note(&details.as_str())
622                 .emit();
623             }
624             UnsafetyViolationKind::BorrowPacked(lint_hir_id) => {
625                 if let Some(impl_def_id) = builtin_derive_def_id(tcx, def_id) {
626                     tcx.unsafe_derive_on_repr_packed(impl_def_id);
627                 } else {
628                     tcx.struct_span_lint_hir(
629                         SAFE_PACKED_BORROWS,
630                         lint_hir_id,
631                         source_info.span,
632                         |lint| {
633                             lint.build(&format!(
634                                 "{} is unsafe and requires unsafe function or block (error E0133)",
635                                 description
636                             ))
637                             .note(&details.as_str())
638                             .emit()
639                         },
640                     )
641                 }
642             }
643         }
644     }
645
646     let mut unsafe_blocks: Vec<_> = unsafe_blocks.iter().collect();
647     unsafe_blocks.sort_by_cached_key(|(hir_id, _)| tcx.hir().hir_to_node_id(*hir_id));
648     let used_unsafe: FxHashSet<_> =
649         unsafe_blocks.iter().flat_map(|&&(id, used)| used.then_some(id)).collect();
650     for &(block_id, is_used) in unsafe_blocks {
651         if !is_used {
652             report_unused_unsafe(tcx, &used_unsafe, block_id);
653         }
654     }
655 }