@@ -2,14 +2,16 @@ use rustc_data_structures::fx::FxHashSet;
2
2
use rustc_errors:: struct_span_err;
3
3
use rustc_hir as hir;
4
4
use rustc_hir:: def_id:: { DefId , LocalDefId } ;
5
+ use rustc_hir:: hir_id:: HirId ;
5
6
use rustc_hir:: intravisit;
6
7
use rustc_hir:: Node ;
7
8
use rustc_middle:: mir:: visit:: { MutatingUseContext , PlaceContext , Visitor } ;
8
9
use rustc_middle:: mir:: * ;
9
10
use rustc_middle:: ty:: cast:: CastTy ;
10
11
use rustc_middle:: ty:: query:: Providers ;
11
12
use 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 ;
13
15
use rustc_span:: symbol:: { sym, Symbol } ;
14
16
15
17
use std:: ops:: Bound ;
@@ -202,25 +204,30 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
202
204
203
205
if context. is_borrow ( ) {
204
206
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 ;
211
207
self . require_unsafe (
212
208
"borrow of packed field" ,
213
209
"fields of packed structs might be misaligned: dereferencing a \
214
210
misaligned pointer or even just creating a misaligned reference \
215
211
is undefined behavior",
216
- UnsafetyViolationKind :: BorrowPacked ( lint_root ) ,
212
+ UnsafetyViolationKind :: BorrowPacked ,
217
213
) ;
218
214
}
219
215
}
220
216
221
217
for ( i, elem) in place. projection . iter ( ) . enumerate ( ) {
222
218
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 ;
224
231
if let [ ] = proj_base {
225
232
let decl = & self . body . local_decls [ place. local ] ;
226
233
if decl. internal {
@@ -301,7 +308,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
301
308
}
302
309
_ => { }
303
310
}
304
- self . source_info = old_source_info ;
311
+ self . source_info = source_info ;
305
312
}
306
313
}
307
314
}
@@ -314,9 +321,15 @@ impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> {
314
321
kind : UnsafetyViolationKind ,
315
322
) {
316
323
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 ;
317
329
self . register_violations (
318
330
& [ UnsafetyViolation {
319
331
source_info,
332
+ lint_root,
320
333
description : Symbol :: intern ( description) ,
321
334
details : Symbol :: intern ( details) ,
322
335
kind,
@@ -343,22 +356,42 @@ impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> {
343
356
match violation. kind {
344
357
UnsafetyViolationKind :: GeneralAndConstFn
345
358
| UnsafetyViolationKind :: General => { }
346
- UnsafetyViolationKind :: BorrowPacked ( _ ) => {
359
+ UnsafetyViolationKind :: BorrowPacked => {
347
360
if self . min_const_fn {
348
361
// const fns don't need to be backwards compatible and can
349
362
// emit these violations as a hard error instead of a backwards
350
363
// compat lint
351
364
violation. kind = UnsafetyViolationKind :: General ;
352
365
}
353
366
}
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 ;
354
387
}
355
388
if !self . violations . contains ( & violation) {
356
389
self . violations . push ( violation)
357
390
}
358
391
}
359
392
false
360
393
}
361
- // `unsafe` function bodies allow unsafe without additional unsafe blocks
394
+ // `unsafe` function bodies allow unsafe without additional unsafe blocks (before RFC 2585)
362
395
Safety :: BuiltinUnsafe | Safety :: FnUnsafe => true ,
363
396
Safety :: ExplicitUnsafe ( hir_id) => {
364
397
// mark unsafe block as used if there are any unsafe operations inside
@@ -373,7 +406,7 @@ impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> {
373
406
UnsafetyViolationKind :: GeneralAndConstFn => { }
374
407
// these things are forbidden in const fns
375
408
UnsafetyViolationKind :: General
376
- | UnsafetyViolationKind :: BorrowPacked ( _ ) => {
409
+ | UnsafetyViolationKind :: BorrowPacked => {
377
410
let mut violation = * violation;
378
411
// const fns don't need to be backwards compatible and can
379
412
// emit these violations as a hard error instead of a backwards
@@ -383,6 +416,10 @@ impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> {
383
416
self . violations . push ( violation)
384
417
}
385
418
}
419
+ UnsafetyViolationKind :: UnsafeFn
420
+ | UnsafetyViolationKind :: UnsafeFnBorrowPacked => bug ! (
421
+ "`UnsafetyViolationKind::UnsafeFn` in an `ExplicitUnsafe` context"
422
+ ) ,
386
423
}
387
424
}
388
425
}
@@ -575,9 +612,12 @@ fn is_enclosed(
575
612
kind : hir:: ItemKind :: Fn ( ref sig, _, _) , ..
576
613
} ) ) = tcx. hir ( ) . find ( parent_id)
577
614
{
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
581
621
}
582
622
} else {
583
623
is_enclosed ( tcx, used_unsafe, parent_id)
@@ -630,40 +670,90 @@ pub fn check_unsafety(tcx: TyCtxt<'_>, def_id: DefId) {
630
670
let UnsafetyCheckResult { violations, unsafe_blocks } =
631
671
tcx. unsafety_check_result ( def_id. expect_local ( ) ) ;
632
672
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
+ {
634
676
// Report an error.
677
+ let unsafe_fn_msg =
678
+ if unsafe_op_in_unsafe_fn_allowed ( tcx, lint_root) { " function or" } else { "" } ;
679
+
635
680
match kind {
636
681
UnsafetyViolationKind :: GeneralAndConstFn | UnsafetyViolationKind :: General => {
682
+ // once
637
683
struct_span_err ! (
638
684
tcx. sess,
639
685
source_info. span,
640
686
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,
643
690
)
644
691
. span_label ( source_info. span , & * description. as_str ( ) )
645
692
. note ( & details. as_str ( ) )
646
693
. emit ( ) ;
647
694
}
648
- UnsafetyViolationKind :: BorrowPacked ( lint_hir_id ) => {
695
+ UnsafetyViolationKind :: BorrowPacked => {
649
696
if let Some ( impl_def_id) = builtin_derive_def_id ( tcx, def_id) {
650
697
tcx. ensure ( ) . unsafe_derive_on_repr_packed ( impl_def_id) ;
651
698
} else {
652
699
tcx. struct_span_lint_hir (
653
700
SAFE_PACKED_BORROWS ,
654
- lint_hir_id ,
701
+ lint_root ,
655
702
source_info. span ,
656
703
|lint| {
657
704
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 ,
660
707
) )
661
708
. note ( & details. as_str ( ) )
662
709
. emit ( )
663
710
} ,
664
711
)
665
712
}
666
713
}
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
+ }
667
757
}
668
758
}
669
759
@@ -683,3 +773,7 @@ pub fn check_unsafety(tcx: TyCtxt<'_>, def_id: DefId) {
683
773
report_unused_unsafe ( tcx, & unsafe_used, block_id) ;
684
774
}
685
775
}
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