Rollup merge of #105147 - nbdd0121:inline_const_unsafe, r=oli-obk

Allow unsafe through inline const

Handle similar to closures.

Address https://github.com/rust-lang/rust/pull/104087#issuecomment-1324173328

Note that this PR does not fix the issue for `unsafe { [0; function_requiring_unsafe()] }`. This is fundamentally unfixable for MIR unsafeck IMO.

This PR also does not fix unsafety checking for inline const in pattern position. It actually breaks it, allowing unsafe functions to be used in inline const in pattern position without unsafe blocks. Inline const in pattern position is not visible in MIR so ignored by MIR unsafety checking (currently it is also not checked by borrow checker, which is the reason why it's considered an incomplete feature).

`@rustbot` label: +T-lang +F-inline_const
This commit is contained in:
Matthias Krüger 2022-12-13 19:57:09 +01:00 committed by GitHub
commit 7357cfbf3d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 177 additions and 19 deletions

View File

@ -20,6 +20,7 @@
use rustc_ast::Attribute; use rustc_ast::Attribute;
use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::fx::FxHashMap;
use rustc_hir::def_id::DefId; use rustc_hir::def_id::DefId;
use rustc_hir::HirId;
use rustc_index::vec::IndexVec; use rustc_index::vec::IndexVec;
use rustc_middle::{ use rustc_middle::{
mir::*, mir::*,
@ -33,6 +34,7 @@ mod parse;
pub(super) fn build_custom_mir<'tcx>( pub(super) fn build_custom_mir<'tcx>(
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
did: DefId, did: DefId,
hir_id: HirId,
thir: &Thir<'tcx>, thir: &Thir<'tcx>,
expr: ExprId, expr: ExprId,
params: &IndexVec<ParamId, Param<'tcx>>, params: &IndexVec<ParamId, Param<'tcx>>,
@ -67,7 +69,10 @@ pub(super) fn build_custom_mir<'tcx>(
parent_scope: None, parent_scope: None,
inlined: None, inlined: None,
inlined_parent_scope: None, inlined_parent_scope: None,
local_data: ClearCrossCrate::Clear, local_data: ClearCrossCrate::Set(SourceScopeLocalData {
lint_root: hir_id,
safety: Safety::Safe,
}),
}); });
body.injection_phase = Some(parse_attribute(attr)); body.injection_phase = Some(parse_attribute(attr));

View File

@ -487,6 +487,7 @@ fn construct_fn<'tcx>(
return custom::build_custom_mir( return custom::build_custom_mir(
tcx, tcx,
fn_def.did.to_def_id(), fn_def.did.to_def_id(),
fn_id,
thir, thir,
expr, expr,
arguments, arguments,

View File

@ -132,6 +132,18 @@ impl<'tcx> UnsafetyVisitor<'_, 'tcx> {
fn unsafe_op_in_unsafe_fn_allowed(&self) -> bool { fn unsafe_op_in_unsafe_fn_allowed(&self) -> bool {
self.tcx.lint_level_at_node(UNSAFE_OP_IN_UNSAFE_FN, self.hir_context).0 == Level::Allow self.tcx.lint_level_at_node(UNSAFE_OP_IN_UNSAFE_FN, self.hir_context).0 == Level::Allow
} }
/// Handle closures/generators/inline-consts, which is unsafecked with their parent body.
fn visit_inner_body(&mut self, def: ty::WithOptConstParam<LocalDefId>) {
if let Ok((inner_thir, expr)) = self.tcx.thir_body(def) {
let inner_thir = &inner_thir.borrow();
let hir_context = self.tcx.hir().local_def_id_to_hir_id(def.did);
let mut inner_visitor = UnsafetyVisitor { thir: inner_thir, hir_context, ..*self };
inner_visitor.visit_expr(&inner_thir[expr]);
// Unsafe blocks can be used in the inner body, make sure to take it into account
self.safety_context = inner_visitor.safety_context;
}
}
} }
// Searches for accesses to layout constrained fields. // Searches for accesses to layout constrained fields.
@ -408,16 +420,11 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
} else { } else {
ty::WithOptConstParam::unknown(closure_id) ty::WithOptConstParam::unknown(closure_id)
}; };
let (closure_thir, expr) = self.tcx.thir_body(closure_def).unwrap_or_else(|_| { self.visit_inner_body(closure_def);
(self.tcx.alloc_steal_thir(Thir::new()), ExprId::from_u32(0)) }
}); ExprKind::ConstBlock { did, substs: _ } => {
let closure_thir = &closure_thir.borrow(); let def_id = did.expect_local();
let hir_context = self.tcx.hir().local_def_id_to_hir_id(closure_id); self.visit_inner_body(ty::WithOptConstParam::unknown(def_id));
let mut closure_visitor =
UnsafetyVisitor { thir: closure_thir, hir_context, ..*self };
closure_visitor.visit_expr(&closure_thir[expr]);
// Unsafe blocks can be used in closures, make sure to take it into account
self.safety_context = closure_visitor.safety_context;
} }
ExprKind::Field { lhs, .. } => { ExprKind::Field { lhs, .. } => {
let lhs = &self.thir[lhs]; let lhs = &self.thir[lhs];
@ -612,11 +619,8 @@ pub fn check_unsafety<'tcx>(tcx: TyCtxt<'tcx>, def: ty::WithOptConstParam<LocalD
return; return;
} }
// Closures are handled by their owner, if it has a body // Closures and inline consts are handled by their owner, if it has a body
if tcx.is_closure(def.did.to_def_id()) { if tcx.is_typeck_child(def.did.to_def_id()) {
let hir = tcx.hir();
let owner = hir.enclosing_body_owner(hir.local_def_id_to_hir_id(def.did));
tcx.ensure().thir_check_unsafety(owner);
return; return;
} }

View File

@ -1,6 +1,7 @@
use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::fx::FxHashSet;
use rustc_errors::struct_span_err; use rustc_errors::struct_span_err;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::hir_id::HirId; use rustc_hir::hir_id::HirId;
use rustc_hir::intravisit; use rustc_hir::intravisit;
@ -134,6 +135,28 @@ impl<'tcx> Visitor<'tcx> for UnsafetyChecker<'_, 'tcx> {
self.super_rvalue(rvalue, location); self.super_rvalue(rvalue, location);
} }
fn visit_operand(&mut self, op: &Operand<'tcx>, location: Location) {
if let Operand::Constant(constant) = op {
let maybe_uneval = match constant.literal {
ConstantKind::Val(..) | ConstantKind::Ty(_) => None,
ConstantKind::Unevaluated(uv, _) => Some(uv),
};
if let Some(uv) = maybe_uneval {
if uv.promoted.is_none() {
let def_id = uv.def.def_id_for_type_of();
if self.tcx.def_kind(def_id) == DefKind::InlineConst {
let local_def_id = def_id.expect_local();
let UnsafetyCheckResult { violations, used_unsafe_blocks, .. } =
self.tcx.unsafety_check_result(local_def_id);
self.register_violations(violations, used_unsafe_blocks.iter().copied());
}
}
}
}
self.super_operand(op, location);
}
fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, _location: Location) { fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, _location: Location) {
// On types with `scalar_valid_range`, prevent // On types with `scalar_valid_range`, prevent
// * `&mut x.field` // * `&mut x.field`
@ -410,6 +433,12 @@ impl<'tcx> intravisit::Visitor<'tcx> for UnusedUnsafeVisitor<'_, 'tcx> {
intravisit::walk_block(self, block); intravisit::walk_block(self, block);
} }
fn visit_anon_const(&mut self, c: &'tcx hir::AnonConst) {
if matches!(self.tcx.def_kind(c.def_id), DefKind::InlineConst) {
self.visit_body(self.tcx.hir().body(c.body))
}
}
fn visit_fn( fn visit_fn(
&mut self, &mut self,
fk: intravisit::FnKind<'tcx>, fk: intravisit::FnKind<'tcx>,
@ -484,7 +513,7 @@ fn unsafety_check_result<'tcx>(
let mut checker = UnsafetyChecker::new(body, def.did, tcx, param_env); let mut checker = UnsafetyChecker::new(body, def.did, tcx, param_env);
checker.visit_body(&body); checker.visit_body(&body);
let unused_unsafes = (!tcx.is_closure(def.did.to_def_id())) let unused_unsafes = (!tcx.is_typeck_child(def.did.to_def_id()))
.then(|| check_unused_unsafe(tcx, def.did, &checker.used_unsafe_blocks)); .then(|| check_unused_unsafe(tcx, def.did, &checker.used_unsafe_blocks));
tcx.arena.alloc(UnsafetyCheckResult { tcx.arena.alloc(UnsafetyCheckResult {
@ -516,8 +545,8 @@ fn report_unused_unsafe(tcx: TyCtxt<'_>, kind: UnusedUnsafe, id: HirId) {
pub fn check_unsafety(tcx: TyCtxt<'_>, def_id: LocalDefId) { pub fn check_unsafety(tcx: TyCtxt<'_>, def_id: LocalDefId) {
debug!("check_unsafety({:?})", def_id); debug!("check_unsafety({:?})", def_id);
// closures are handled by their parent fn. // closures and inline consts are handled by their parent fn.
if tcx.is_closure(def_id.to_def_id()) { if tcx.is_typeck_child(def_id.to_def_id()) {
return; return;
} }

View File

@ -0,0 +1,11 @@
error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
--> $DIR/expr-unsafe-err.rs:8:9
|
LL | require_unsafe();
| ^^^^^^^^^^^^^^^^ call to unsafe function
|
= note: consult the function's documentation for information on how to avoid undefined behavior
error: aborting due to previous error
For more information about this error, try `rustc --explain E0133`.

View File

@ -0,0 +1,11 @@
// revisions: mir thir
// [thir]compile-flags: -Z thir-unsafeck
#![feature(inline_const)]
const unsafe fn require_unsafe() -> usize { 1 }
fn main() {
const {
require_unsafe();
//~^ ERROR [E0133]
}
}

View File

@ -0,0 +1,11 @@
error[E0133]: call to unsafe function `require_unsafe` is unsafe and requires unsafe function or block
--> $DIR/expr-unsafe-err.rs:8:9
|
LL | require_unsafe();
| ^^^^^^^^^^^^^^^^ call to unsafe function
|
= note: consult the function's documentation for information on how to avoid undefined behavior
error: aborting due to previous error
For more information about this error, try `rustc --explain E0133`.

View File

@ -0,0 +1,14 @@
warning: unnecessary `unsafe` block
--> $DIR/expr-unsafe.rs:12:13
|
LL | unsafe {}
| ^^^^^^ unnecessary `unsafe` block
|
note: the lint level is defined here
--> $DIR/expr-unsafe.rs:4:9
|
LL | #![warn(unused_unsafe)]
| ^^^^^^^^^^^^^
warning: 1 warning emitted

View File

@ -0,0 +1,16 @@
// check-pass
// revisions: mir thir
// [thir]compile-flags: -Z thir-unsafeck
#![warn(unused_unsafe)]
#![feature(inline_const)]
const unsafe fn require_unsafe() -> usize { 1 }
fn main() {
unsafe {
const {
require_unsafe();
unsafe {}
//~^ WARNING unnecessary `unsafe` block
}
}
}

View File

@ -0,0 +1,17 @@
warning: unnecessary `unsafe` block
--> $DIR/expr-unsafe.rs:12:13
|
LL | unsafe {
| ------ because it's nested under this `unsafe` block
...
LL | unsafe {}
| ^^^^^^ unnecessary `unsafe` block
|
note: the lint level is defined here
--> $DIR/expr-unsafe.rs:4:9
|
LL | #![warn(unused_unsafe)]
| ^^^^^^^^^^^^^
warning: 1 warning emitted

View File

@ -0,0 +1,17 @@
// ignore-test This is currently broken
// revisions: mir thir
// [thir]compile-flags: -Z thir-unsafeck
#![allow(incomplete_features)]
#![feature(inline_const_pat)]
const unsafe fn require_unsafe() -> usize { 1 }
fn main() {
match () {
const {
require_unsafe();
//~^ ERROR [E0133]
} => (),
}
}

View File

@ -0,0 +1,22 @@
// ignore-test This is currently broken
// check-pass
// revisions: mir thir
// [thir]compile-flags: -Z thir-unsafeck
#![allow(incomplete_features)]
#![warn(unused_unsafe)]
#![feature(inline_const_pat)]
const unsafe fn require_unsafe() -> usize { 1 }
fn main() {
unsafe {
match () {
const {
require_unsafe();
unsafe {}
//~^ WARNING unnecessary `unsafe` block
} => (),
}
}
}