@@ -2,14 +2,16 @@ use rustc_data_structures::fx::FxHashSet;
22use rustc_errors:: struct_span_err;
33use rustc_hir as hir;
44use rustc_hir:: def_id:: { DefId , LocalDefId } ;
5+ use rustc_hir:: hir_id:: HirId ;
56use rustc_hir:: intravisit;
67use rustc_hir:: Node ;
78use rustc_middle:: mir:: visit:: { MutatingUseContext , PlaceContext , Visitor } ;
89use rustc_middle:: mir:: * ;
910use rustc_middle:: ty:: cast:: CastTy ;
1011use rustc_middle:: ty:: query:: Providers ;
1112use rustc_middle:: ty:: { self , TyCtxt } ;
12- use rustc_session:: lint:: builtin:: { SAFE_PACKED_BORROWS , UNUSED_UNSAFE } ;
13+ use rustc_session:: lint:: builtin:: { SAFE_PACKED_BORROWS , UNSAFE_OP_IN_UNSAFE_FN , UNUSED_UNSAFE } ;
14+ use rustc_session:: lint:: Level ;
1315use rustc_span:: symbol:: { sym, Symbol } ;
1416
1517use std:: ops:: Bound ;
@@ -202,25 +204,30 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
202204
203205 if context. is_borrow ( ) {
204206 if util:: is_disaligned ( self . tcx , self . body , self . param_env , * place) {
205- let source_info = self . source_info ;
206- let lint_root = self . body . source_scopes [ source_info. scope ]
207- . local_data
208- . as_ref ( )
209- . assert_crate_local ( )
210- . lint_root ;
211207 self . require_unsafe (
212208 "borrow of packed field" ,
213209 "fields of packed structs might be misaligned: dereferencing a \
214210 misaligned pointer or even just creating a misaligned reference \
215211 is undefined behavior",
216- UnsafetyViolationKind :: BorrowPacked ( lint_root ) ,
212+ UnsafetyViolationKind :: BorrowPacked ,
217213 ) ;
218214 }
219215 }
220216
221217 for ( i, elem) in place. projection . iter ( ) . enumerate ( ) {
222218 let proj_base = & place. projection [ ..i] ;
223- let old_source_info = self . source_info ;
219+ if context. is_borrow ( ) {
220+ if util:: is_disaligned ( self . tcx , self . body , self . param_env , * place) {
221+ self . require_unsafe (
222+ "borrow of packed field" ,
223+ "fields of packed structs might be misaligned: dereferencing a \
224+ misaligned pointer or even just creating a misaligned reference \
225+ is undefined behavior",
226+ UnsafetyViolationKind :: BorrowPacked ,
227+ ) ;
228+ }
229+ }
230+ let source_info = self . source_info ;
224231 if let [ ] = proj_base {
225232 let decl = & self . body . local_decls [ place. local ] ;
226233 if decl. internal {
@@ -301,7 +308,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
301308 }
302309 _ => { }
303310 }
304- self . source_info = old_source_info ;
311+ self . source_info = source_info ;
305312 }
306313 }
307314}
@@ -314,9 +321,15 @@ impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> {
314321 kind : UnsafetyViolationKind ,
315322 ) {
316323 let source_info = self . source_info ;
324+ let lint_root = self . body . source_scopes [ self . source_info . scope ]
325+ . local_data
326+ . as_ref ( )
327+ . assert_crate_local ( )
328+ . lint_root ;
317329 self . register_violations (
318330 & [ UnsafetyViolation {
319331 source_info,
332+ lint_root,
320333 description : Symbol :: intern ( description) ,
321334 details : Symbol :: intern ( details) ,
322335 kind,
@@ -343,22 +356,42 @@ impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> {
343356 match violation. kind {
344357 UnsafetyViolationKind :: GeneralAndConstFn
345358 | UnsafetyViolationKind :: General => { }
346- UnsafetyViolationKind :: BorrowPacked ( _ ) => {
359+ UnsafetyViolationKind :: BorrowPacked => {
347360 if self . min_const_fn {
348361 // const fns don't need to be backwards compatible and can
349362 // emit these violations as a hard error instead of a backwards
350363 // compat lint
351364 violation. kind = UnsafetyViolationKind :: General ;
352365 }
353366 }
367+ UnsafetyViolationKind :: UnsafeFn
368+ | UnsafetyViolationKind :: UnsafeFnBorrowPacked => {
369+ bug ! ( "`UnsafetyViolationKind::UnsafeFn` in an `Safe` context" )
370+ }
371+ }
372+ if !self . violations . contains ( & violation) {
373+ self . violations . push ( violation)
374+ }
375+ }
376+ false
377+ }
378+ // With the RFC 2585, no longer allow `unsafe` operations in `unsafe fn`s
379+ Safety :: FnUnsafe if self . tcx . features ( ) . unsafe_block_in_unsafe_fn => {
380+ for violation in violations {
381+ let mut violation = * violation;
382+
383+ if violation. kind == UnsafetyViolationKind :: BorrowPacked {
384+ violation. kind = UnsafetyViolationKind :: UnsafeFnBorrowPacked ;
385+ } else {
386+ violation. kind = UnsafetyViolationKind :: UnsafeFn ;
354387 }
355388 if !self . violations . contains ( & violation) {
356389 self . violations . push ( violation)
357390 }
358391 }
359392 false
360393 }
361- // `unsafe` function bodies allow unsafe without additional unsafe blocks
394+ // `unsafe` function bodies allow unsafe without additional unsafe blocks (before RFC 2585)
362395 Safety :: BuiltinUnsafe | Safety :: FnUnsafe => true ,
363396 Safety :: ExplicitUnsafe ( hir_id) => {
364397 // mark unsafe block as used if there are any unsafe operations inside
@@ -373,7 +406,7 @@ impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> {
373406 UnsafetyViolationKind :: GeneralAndConstFn => { }
374407 // these things are forbidden in const fns
375408 UnsafetyViolationKind :: General
376- | UnsafetyViolationKind :: BorrowPacked ( _ ) => {
409+ | UnsafetyViolationKind :: BorrowPacked => {
377410 let mut violation = * violation;
378411 // const fns don't need to be backwards compatible and can
379412 // emit these violations as a hard error instead of a backwards
@@ -383,6 +416,10 @@ impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> {
383416 self . violations . push ( violation)
384417 }
385418 }
419+ UnsafetyViolationKind :: UnsafeFn
420+ | UnsafetyViolationKind :: UnsafeFnBorrowPacked => bug ! (
421+ "`UnsafetyViolationKind::UnsafeFn` in an `ExplicitUnsafe` context"
422+ ) ,
386423 }
387424 }
388425 }
@@ -575,9 +612,12 @@ fn is_enclosed(
575612 kind : hir:: ItemKind :: Fn ( ref sig, _, _) , ..
576613 } ) ) = tcx. hir ( ) . find ( parent_id)
577614 {
578- match sig. header . unsafety {
579- hir:: Unsafety :: Unsafe => Some ( ( "fn" . to_string ( ) , parent_id) ) ,
580- hir:: Unsafety :: Normal => None ,
615+ if sig. header . unsafety == hir:: Unsafety :: Unsafe
616+ && !tcx. features ( ) . unsafe_block_in_unsafe_fn
617+ {
618+ Some ( ( "fn" . to_string ( ) , parent_id) )
619+ } else {
620+ None
581621 }
582622 } else {
583623 is_enclosed ( tcx, used_unsafe, parent_id)
@@ -630,40 +670,90 @@ pub fn check_unsafety(tcx: TyCtxt<'_>, def_id: DefId) {
630670 let UnsafetyCheckResult { violations, unsafe_blocks } =
631671 tcx. unsafety_check_result ( def_id. expect_local ( ) ) ;
632672
633- for & UnsafetyViolation { source_info, description, details, kind } in violations. iter ( ) {
673+ for & UnsafetyViolation { source_info, lint_root, description, details, kind } in
674+ violations. iter ( )
675+ {
634676 // Report an error.
677+ let unsafe_fn_msg =
678+ if unsafe_op_in_unsafe_fn_allowed ( tcx, lint_root) { " function or" } else { "" } ;
679+
635680 match kind {
636681 UnsafetyViolationKind :: GeneralAndConstFn | UnsafetyViolationKind :: General => {
682+ // once
637683 struct_span_err ! (
638684 tcx. sess,
639685 source_info. span,
640686 E0133 ,
641- "{} is unsafe and requires unsafe function or block" ,
642- description
687+ "{} is unsafe and requires unsafe{} block" ,
688+ description,
689+ unsafe_fn_msg,
643690 )
644691 . span_label ( source_info. span , & * description. as_str ( ) )
645692 . note ( & details. as_str ( ) )
646693 . emit ( ) ;
647694 }
648- UnsafetyViolationKind :: BorrowPacked ( lint_hir_id ) => {
695+ UnsafetyViolationKind :: BorrowPacked => {
649696 if let Some ( impl_def_id) = builtin_derive_def_id ( tcx, def_id) {
650697 tcx. ensure ( ) . unsafe_derive_on_repr_packed ( impl_def_id) ;
651698 } else {
652699 tcx. struct_span_lint_hir (
653700 SAFE_PACKED_BORROWS ,
654- lint_hir_id ,
701+ lint_root ,
655702 source_info. span ,
656703 |lint| {
657704 lint. build ( & format ! (
658- "{} is unsafe and requires unsafe function or block (error E0133)" ,
659- description
705+ "{} is unsafe and requires unsafe{} block (error E0133)" ,
706+ description, unsafe_fn_msg ,
660707 ) )
661708 . note ( & details. as_str ( ) )
662709 . emit ( )
663710 } ,
664711 )
665712 }
666713 }
714+ UnsafetyViolationKind :: UnsafeFn => tcx. struct_span_lint_hir (
715+ UNSAFE_OP_IN_UNSAFE_FN ,
716+ lint_root,
717+ source_info. span ,
718+ |lint| {
719+ lint. build ( & format ! (
720+ "{} is unsafe and requires unsafe block (error E0133)" ,
721+ description,
722+ ) )
723+ . span_label ( source_info. span , & * description. as_str ( ) )
724+ . note ( & details. as_str ( ) )
725+ . emit ( ) ;
726+ } ,
727+ ) ,
728+ UnsafetyViolationKind :: UnsafeFnBorrowPacked => {
729+ // When `unsafe_op_in_unsafe_fn` is disallowed, the behavior of safe and unsafe functions
730+ // should be the same in terms of warnings and errors. Therefore, with `#[warn(safe_packed_borrows)]`,
731+ // a safe packed borrow should emit a warning *but not an error* in an unsafe function,
732+ // just like in a safe function, even if `unsafe_op_in_unsafe_fn` is `deny`.
733+ //
734+ // Also, `#[warn(unsafe_op_in_unsafe_fn)]` can't cause any new errors. Therefore, with
735+ // `#[deny(safe_packed_borrows)]` and `#[warn(unsafe_op_in_unsafe_fn)]`, a packed borrow
736+ // should only issue a warning for the sake of backwards compatibility.
737+ //
738+ // The solution those 2 expectations is to always take the minimum of both lints.
739+ // This prevent any new errors (unless both lints are explicitely set to `deny`).
740+ let lint = if tcx. lint_level_at_node ( SAFE_PACKED_BORROWS , lint_root) . 0
741+ <= tcx. lint_level_at_node ( UNSAFE_OP_IN_UNSAFE_FN , lint_root) . 0
742+ {
743+ SAFE_PACKED_BORROWS
744+ } else {
745+ UNSAFE_OP_IN_UNSAFE_FN
746+ } ;
747+ tcx. struct_span_lint_hir ( & lint, lint_root, source_info. span , |lint| {
748+ lint. build ( & format ! (
749+ "{} is unsafe and requires unsafe block (error E0133)" ,
750+ description,
751+ ) )
752+ . span_label ( source_info. span , & * description. as_str ( ) )
753+ . note ( & details. as_str ( ) )
754+ . emit ( ) ;
755+ } )
756+ }
667757 }
668758 }
669759
@@ -683,3 +773,7 @@ pub fn check_unsafety(tcx: TyCtxt<'_>, def_id: DefId) {
683773 report_unused_unsafe ( tcx, & unsafe_used, block_id) ;
684774 }
685775}
776+
777+ fn unsafe_op_in_unsafe_fn_allowed ( tcx : TyCtxt < ' _ > , id : HirId ) -> bool {
778+ tcx. lint_level_at_node ( UNSAFE_OP_IN_UNSAFE_FN , id) . 0 == Level :: Allow
779+ }
0 commit comments