unsafety checking: no longer care about is_min_const_fn

Rejecting the forbidden unsafe ops is done by const checking, not by unsafety checking
This commit is contained in:
Ralf Jung 2021-04-25 12:52:42 +02:00
parent 5da10c0121
commit 9082078a26
2 changed files with 18 additions and 53 deletions

View File

@ -19,10 +19,8 @@ use super::{Field, SourceInfo};
#[derive(Copy, Clone, PartialEq, TyEncodable, TyDecodable, HashStable, Debug)] #[derive(Copy, Clone, PartialEq, TyEncodable, TyDecodable, HashStable, Debug)]
pub enum UnsafetyViolationKind { pub enum UnsafetyViolationKind {
/// Only permitted in regular `fn`s, prohibited in `const fn`s. /// Unsafe operation outside `unsafe`
General, General,
/// Permitted both in `const fn`s and regular `fn`s.
GeneralAndConstFn,
/// Unsafe operation in an `unsafe fn` but outside an `unsafe` block. /// Unsafe operation in an `unsafe fn` but outside an `unsafe` block.
/// Has to be handled as a lint for backwards compatibility. /// Has to be handled as a lint for backwards compatibility.
UnsafeFn, UnsafeFn,

View File

@ -15,13 +15,10 @@ use rustc_session::lint::Level;
use std::ops::Bound; use std::ops::Bound;
use crate::const_eval::is_min_const_fn;
pub struct UnsafetyChecker<'a, 'tcx> { pub struct UnsafetyChecker<'a, 'tcx> {
body: &'a Body<'tcx>, body: &'a Body<'tcx>,
body_did: LocalDefId, body_did: LocalDefId,
const_context: bool, const_context: bool,
min_const_fn: bool,
violations: Vec<UnsafetyViolation>, violations: Vec<UnsafetyViolation>,
source_info: SourceInfo, source_info: SourceInfo,
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
@ -34,21 +31,15 @@ pub struct UnsafetyChecker<'a, 'tcx> {
impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> { impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> {
fn new( fn new(
const_context: bool, const_context: bool,
min_const_fn: bool,
body: &'a Body<'tcx>, body: &'a Body<'tcx>,
body_did: LocalDefId, body_did: LocalDefId,
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>, param_env: ty::ParamEnv<'tcx>,
) -> Self { ) -> Self {
// sanity check
if min_const_fn {
assert!(const_context);
}
Self { Self {
body, body,
body_did, body_did,
const_context, const_context,
min_const_fn,
violations: vec![], violations: vec![],
source_info: SourceInfo::outermost(body.span), source_info: SourceInfo::outermost(body.span),
tcx, tcx,
@ -84,7 +75,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
let sig = func_ty.fn_sig(self.tcx); let sig = func_ty.fn_sig(self.tcx);
if let hir::Unsafety::Unsafe = sig.unsafety() { if let hir::Unsafety::Unsafe = sig.unsafety() {
self.require_unsafe( self.require_unsafe(
UnsafetyViolationKind::GeneralAndConstFn, UnsafetyViolationKind::General,
UnsafetyViolationDetails::CallToUnsafeFunction, UnsafetyViolationDetails::CallToUnsafeFunction,
) )
} }
@ -134,7 +125,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
match self.tcx.layout_scalar_valid_range(def.did) { match self.tcx.layout_scalar_valid_range(def.did) {
(Bound::Unbounded, Bound::Unbounded) => {} (Bound::Unbounded, Bound::Unbounded) => {}
_ => self.require_unsafe( _ => self.require_unsafe(
UnsafetyViolationKind::GeneralAndConstFn, UnsafetyViolationKind::General,
UnsafetyViolationDetails::InitializingTypeWith, UnsafetyViolationDetails::InitializingTypeWith,
), ),
} }
@ -213,7 +204,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
let base_ty = base.ty(self.body, self.tcx).ty; let base_ty = base.ty(self.body, self.tcx).ty;
if base_ty.is_unsafe_ptr() { if base_ty.is_unsafe_ptr() {
self.require_unsafe( self.require_unsafe(
UnsafetyViolationKind::GeneralAndConstFn, UnsafetyViolationKind::General,
UnsafetyViolationDetails::DerefOfRawPointer, UnsafetyViolationDetails::DerefOfRawPointer,
) )
} }
@ -258,7 +249,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
); );
if !nodrop { if !nodrop {
self.require_unsafe( self.require_unsafe(
UnsafetyViolationKind::GeneralAndConstFn, UnsafetyViolationKind::General,
UnsafetyViolationDetails::AssignToDroppingUnionField, UnsafetyViolationDetails::AssignToDroppingUnionField,
); );
} else { } else {
@ -266,7 +257,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
} }
} else { } else {
self.require_unsafe( self.require_unsafe(
UnsafetyViolationKind::GeneralAndConstFn, UnsafetyViolationKind::General,
UnsafetyViolationDetails::AccessToUnionField, UnsafetyViolationDetails::AccessToUnionField,
) )
} }
@ -277,6 +268,9 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> { impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> {
fn require_unsafe(&mut self, kind: UnsafetyViolationKind, details: UnsafetyViolationDetails) { fn require_unsafe(&mut self, kind: UnsafetyViolationKind, details: UnsafetyViolationDetails) {
// Violations can turn out to be `UnsafeFn` during analysis, but they should not start out as such.
assert_ne!(kind, UnsafetyViolationKind::UnsafeFn);
let source_info = self.source_info; let source_info = self.source_info;
let lint_root = self.body.source_scopes[self.source_info.scope] let lint_root = self.body.source_scopes[self.source_info.scope]
.local_data .local_data
@ -304,8 +298,7 @@ impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> {
Safety::Safe => { Safety::Safe => {
for violation in violations { for violation in violations {
match violation.kind { match violation.kind {
UnsafetyViolationKind::GeneralAndConstFn UnsafetyViolationKind::General => {}
| UnsafetyViolationKind::General => {}
UnsafetyViolationKind::UnsafeFn => { UnsafetyViolationKind::UnsafeFn => {
bug!("`UnsafetyViolationKind::UnsafeFn` in an `Safe` context") bug!("`UnsafetyViolationKind::UnsafeFn` in an `Safe` context")
} }
@ -334,29 +327,6 @@ impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> {
if !violations.is_empty() { if !violations.is_empty() {
self.used_unsafe.insert(hir_id); self.used_unsafe.insert(hir_id);
} }
// only some unsafety is allowed in const fn
if self.min_const_fn {
for violation in violations {
match violation.kind {
// these unsafe things are stable in const fn
UnsafetyViolationKind::GeneralAndConstFn => {}
// these things are forbidden in const fns
UnsafetyViolationKind::General => {
let mut violation = *violation;
// const fns don't need to be backwards compatible and can
// emit these violations as a hard error instead of a backwards
// compat lint
violation.kind = UnsafetyViolationKind::General;
if !self.violations.contains(&violation) {
self.violations.push(violation)
}
}
UnsafetyViolationKind::UnsafeFn => bug!(
"`UnsafetyViolationKind::UnsafeFn` in an `ExplicitUnsafe` context"
),
}
}
}
true true
} }
}; };
@ -394,7 +364,7 @@ impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> {
} else { } else {
continue; continue;
}; };
self.require_unsafe(UnsafetyViolationKind::GeneralAndConstFn, details); self.require_unsafe(UnsafetyViolationKind::General, details);
} }
} }
} }
@ -412,7 +382,7 @@ impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> {
// Is `callee_features` a subset of `calling_features`? // Is `callee_features` a subset of `calling_features`?
if !callee_features.iter().all(|feature| self_features.contains(feature)) { if !callee_features.iter().all(|feature| self_features.contains(feature)) {
self.require_unsafe( self.require_unsafe(
UnsafetyViolationKind::GeneralAndConstFn, UnsafetyViolationKind::General,
UnsafetyViolationDetails::CallToFunctionWith, UnsafetyViolationDetails::CallToFunctionWith,
) )
} }
@ -494,15 +464,12 @@ fn unsafety_check_result<'tcx>(
let param_env = tcx.param_env(def.did); let param_env = tcx.param_env(def.did);
let id = tcx.hir().local_def_id_to_hir_id(def.did); let id = tcx.hir().local_def_id_to_hir_id(def.did);
let (const_context, min_const_fn) = match tcx.hir().body_owner_kind(id) { let const_context = match tcx.hir().body_owner_kind(id) {
hir::BodyOwnerKind::Closure => (false, false), hir::BodyOwnerKind::Closure => false,
hir::BodyOwnerKind::Fn => { hir::BodyOwnerKind::Fn => tcx.is_const_fn_raw(def.did.to_def_id()),
(tcx.is_const_fn_raw(def.did.to_def_id()), is_min_const_fn(tcx, def.did.to_def_id())) hir::BodyOwnerKind::Const | hir::BodyOwnerKind::Static(_) => true,
}
hir::BodyOwnerKind::Const | hir::BodyOwnerKind::Static(_) => (true, false),
}; };
let mut checker = let mut checker = UnsafetyChecker::new(const_context, body, def.did, tcx, param_env);
UnsafetyChecker::new(const_context, min_const_fn, body, def.did, tcx, param_env);
checker.visit_body(&body); checker.visit_body(&body);
check_unused_unsafe(tcx, def.did, &checker.used_unsafe, &mut checker.inherited_blocks); check_unused_unsafe(tcx, def.did, &checker.used_unsafe, &mut checker.inherited_blocks);
@ -577,7 +544,7 @@ pub fn check_unsafety(tcx: TyCtxt<'_>, def_id: LocalDefId) {
if unsafe_op_in_unsafe_fn_allowed(tcx, lint_root) { " function or" } else { "" }; if unsafe_op_in_unsafe_fn_allowed(tcx, lint_root) { " function or" } else { "" };
match kind { match kind {
UnsafetyViolationKind::GeneralAndConstFn | UnsafetyViolationKind::General => { UnsafetyViolationKind::General => {
// once // once
struct_span_err!( struct_span_err!(
tcx.sess, tcx.sess,