Skip to content

Commit c442e43

Browse files
authored
Rollup merge of rust-lang#71862 - LeSeulArtichaut:unsafe-block-in-unsafe-fn, r=nikomatsakis
Implement RFC 2585: unsafe blocks in unsafe fn Tracking issue: rust-lang#71668 r? @RalfJung cc @nikomatsakis
2 parents 7aef3a0 + 0e3b31c commit c442e43

15 files changed

+527
-29
lines changed

src/librustc_feature/active.rs

+3
Original file line numberDiff line numberDiff line change
@@ -571,6 +571,9 @@ declare_features! (
571571
/// Allows the use of `#[ffi_const]` on foreign functions.
572572
(active, ffi_const, "1.45.0", Some(58328), None),
573573

574+
/// No longer treat an unsafe function as an unsafe block.
575+
(active, unsafe_block_in_unsafe_fn, "1.45.0", Some(71668), None),
576+
574577
// -------------------------------------------------------------------------
575578
// feature-group-end: actual feature gates
576579
// -------------------------------------------------------------------------

src/librustc_lint/levels.rs

+19-2
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@ use rustc_middle::lint::LintDiagnosticBuilder;
1414
use rustc_middle::lint::{struct_lint_level, LintLevelMap, LintLevelSets, LintSet, LintSource};
1515
use rustc_middle::ty::query::Providers;
1616
use rustc_middle::ty::TyCtxt;
17-
use rustc_session::lint::{builtin, Level, Lint};
17+
use rustc_session::lint::{builtin, Level, Lint, LintId};
1818
use rustc_session::parse::feature_err;
1919
use rustc_session::Session;
20-
use rustc_span::source_map::MultiSpan;
2120
use rustc_span::symbol::{sym, Symbol};
21+
use rustc_span::{source_map::MultiSpan, Span, DUMMY_SP};
2222

2323
use std::cmp;
2424

@@ -80,11 +80,13 @@ impl<'s> LintLevelsBuilder<'s> {
8080
let level = cmp::min(level, self.sets.lint_cap);
8181

8282
let lint_flag_val = Symbol::intern(lint_name);
83+
8384
let ids = match store.find_lints(&lint_name) {
8485
Ok(ids) => ids,
8586
Err(_) => continue, // errors handled in check_lint_name_cmdline above
8687
};
8788
for id in ids {
89+
self.check_gated_lint(id, DUMMY_SP);
8890
let src = LintSource::CommandLine(lint_flag_val);
8991
specs.insert(id, (level, src));
9092
}
@@ -213,6 +215,7 @@ impl<'s> LintLevelsBuilder<'s> {
213215
CheckLintNameResult::Ok(ids) => {
214216
let src = LintSource::Node(name, li.span(), reason);
215217
for id in ids {
218+
self.check_gated_lint(*id, attr.span);
216219
specs.insert(*id, (level, src));
217220
}
218221
}
@@ -383,6 +386,20 @@ impl<'s> LintLevelsBuilder<'s> {
383386
BuilderPush { prev, changed: prev != self.cur }
384387
}
385388

389+
fn check_gated_lint(&self, id: LintId, span: Span) {
390+
if id == LintId::of(builtin::UNSAFE_OP_IN_UNSAFE_FN)
391+
&& !self.sess.features_untracked().unsafe_block_in_unsafe_fn
392+
{
393+
feature_err(
394+
&self.sess.parse_sess,
395+
sym::unsafe_block_in_unsafe_fn,
396+
span,
397+
"the `unsafe_op_in_unsafe_fn` lint is unstable",
398+
)
399+
.emit();
400+
}
401+
}
402+
386403
/// Called after `push` when the scope of a set of attributes are exited.
387404
pub fn pop(&mut self, push: BuilderPush) {
388405
self.cur = push.prev;

src/librustc_middle/mir/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -408,7 +408,7 @@ impl<'tcx> Body<'tcx> {
408408
}
409409
}
410410

411-
#[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable, HashStable)]
411+
#[derive(Copy, Clone, PartialEq, Eq, Debug, RustcEncodable, RustcDecodable, HashStable)]
412412
pub enum Safety {
413413
Safe,
414414
/// Unsafe because of a PushUnsafeBlock

src/librustc_middle/mir/query.rs

+13-1
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,27 @@ use super::{Field, SourceInfo};
1515

1616
#[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable, HashStable)]
1717
pub enum UnsafetyViolationKind {
18+
/// Only permitted in regular `fn`s, prohibitted in `const fn`s.
1819
General,
1920
/// Permitted both in `const fn`s and regular `fn`s.
2021
GeneralAndConstFn,
21-
BorrowPacked(hir::HirId),
22+
/// Borrow of packed field.
23+
/// Has to be handled as a lint for backwards compatibility.
24+
BorrowPacked,
25+
/// Unsafe operation in an `unsafe fn` but outside an `unsafe` block.
26+
/// Has to be handled as a lint for backwards compatibility.
27+
/// Should stay gated under `#![feature(unsafe_block_in_unsafe_fn)]`.
28+
UnsafeFn,
29+
/// Borrow of packed field in an `unsafe fn` but outside an `unsafe` block.
30+
/// Has to be handled as a lint for backwards compatibility.
31+
/// Should stay gated under `#![feature(unsafe_block_in_unsafe_fn)]`.
32+
UnsafeFnBorrowPacked,
2233
}
2334

2435
#[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable, HashStable)]
2536
pub struct UnsafetyViolation {
2637
pub source_info: SourceInfo,
38+
pub lint_root: hir::HirId,
2739
pub description: Symbol,
2840
pub details: Symbol,
2941
pub kind: UnsafetyViolationKind,

src/librustc_mir/transform/check_unsafety.rs

+117-23
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,16 @@ use rustc_data_structures::fx::FxHashSet;
22
use rustc_errors::struct_span_err;
33
use rustc_hir as hir;
44
use rustc_hir::def_id::{DefId, LocalDefId};
5+
use rustc_hir::hir_id::HirId;
56
use rustc_hir::intravisit;
67
use rustc_hir::Node;
78
use rustc_middle::mir::visit::{MutatingUseContext, PlaceContext, Visitor};
89
use rustc_middle::mir::*;
910
use rustc_middle::ty::cast::CastTy;
1011
use rustc_middle::ty::query::Providers;
1112
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;
1315
use rustc_span::symbol::{sym, Symbol};
1416

1517
use 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

Comments
 (0)