use std::ops::Bound;
-use crate::const_eval::is_min_const_fn;
-
pub struct UnsafetyChecker<'a, 'tcx> {
body: &'a Body<'tcx>,
body_did: LocalDefId,
const_context: bool,
- min_const_fn: bool,
violations: Vec<UnsafetyViolation>,
source_info: SourceInfo,
tcx: TyCtxt<'tcx>,
impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> {
fn new(
const_context: bool,
- min_const_fn: bool,
body: &'a Body<'tcx>,
body_did: LocalDefId,
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
) -> Self {
- // sanity check
- if min_const_fn {
- assert!(const_context);
- }
Self {
body,
body_did,
const_context,
- min_const_fn,
violations: vec![],
source_info: SourceInfo::outermost(body.span),
tcx,
let sig = func_ty.fn_sig(self.tcx);
if let hir::Unsafety::Unsafe = sig.unsafety() {
self.require_unsafe(
- UnsafetyViolationKind::GeneralAndConstFn,
+ UnsafetyViolationKind::General,
UnsafetyViolationDetails::CallToUnsafeFunction,
)
}
match self.tcx.layout_scalar_valid_range(def.did) {
(Bound::Unbounded, Bound::Unbounded) => {}
_ => self.require_unsafe(
- UnsafetyViolationKind::GeneralAndConstFn,
+ UnsafetyViolationKind::General,
UnsafetyViolationDetails::InitializingTypeWith,
),
}
let base_ty = base.ty(self.body, self.tcx).ty;
if base_ty.is_unsafe_ptr() {
self.require_unsafe(
- UnsafetyViolationKind::GeneralAndConstFn,
+ UnsafetyViolationKind::General,
UnsafetyViolationDetails::DerefOfRawPointer,
)
}
}
let base_ty = base.ty(self.body, self.tcx).ty;
- if base_ty.ty_adt_def().map_or(false, |adt| adt.is_union()) {
+ if base_ty.is_union() {
// If we did not hit a `Deref` yet and the overall place use is an assignment, the
// rules are different.
let assign_to_field = !saw_deref
);
if !nodrop {
self.require_unsafe(
- UnsafetyViolationKind::GeneralAndConstFn,
+ UnsafetyViolationKind::General,
UnsafetyViolationDetails::AssignToDroppingUnionField,
);
} else {
}
} else {
self.require_unsafe(
- UnsafetyViolationKind::GeneralAndConstFn,
+ UnsafetyViolationKind::General,
UnsafetyViolationDetails::AccessToUnionField,
)
}
impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> {
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 lint_root = self.body.source_scopes[self.source_info.scope]
.local_data
Safety::Safe => {
for violation in violations {
match violation.kind {
- UnsafetyViolationKind::GeneralAndConstFn
- | UnsafetyViolationKind::General => {}
+ UnsafetyViolationKind::General => {}
UnsafetyViolationKind::UnsafeFn => {
bug!("`UnsafetyViolationKind::UnsafeFn` in an `Safe` context")
}
if !violations.is_empty() {
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
}
};
} else {
continue;
};
- self.require_unsafe(UnsafetyViolationKind::GeneralAndConstFn, details);
+ self.require_unsafe(UnsafetyViolationKind::General, details);
}
}
}
/// Checks whether calling `func_did` needs an `unsafe` context or not, i.e. whether
/// the called function has target features the calling function hasn't.
fn check_target_features(&mut self, func_did: DefId) {
+ // Unsafety isn't required on wasm targets. For more information see
+ // the corresponding check in typeck/src/collect.rs
+ if self.tcx.sess.target.options.is_like_wasm {
+ return;
+ }
+
let callee_features = &self.tcx.codegen_fn_attrs(func_did).target_features;
let self_features = &self.tcx.codegen_fn_attrs(self.body_did).target_features;
// Is `callee_features` a subset of `calling_features`?
if !callee_features.iter().all(|feature| self_features.contains(feature)) {
self.require_unsafe(
- UnsafetyViolationKind::GeneralAndConstFn,
+ UnsafetyViolationKind::General,
UnsafetyViolationDetails::CallToFunctionWith,
)
}
let param_env = tcx.param_env(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) {
- hir::BodyOwnerKind::Closure => (false, false),
- hir::BodyOwnerKind::Fn => {
- (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, false),
+ let const_context = match tcx.hir().body_owner_kind(id) {
+ hir::BodyOwnerKind::Closure => false,
+ hir::BodyOwnerKind::Fn => tcx.is_const_fn_raw(def.did.to_def_id()),
+ hir::BodyOwnerKind::Const | hir::BodyOwnerKind::Static(_) => true,
};
- let mut checker =
- UnsafetyChecker::new(const_context, min_const_fn, body, def.did, tcx, param_env);
+ let mut checker = UnsafetyChecker::new(const_context, body, def.did, tcx, param_env);
checker.visit_body(&body);
check_unused_unsafe(tcx, def.did, &checker.used_unsafe, &mut checker.inherited_blocks);
if unsafe_op_in_unsafe_fn_allowed(tcx, lint_root) { " function or" } else { "" };
match kind {
- UnsafetyViolationKind::GeneralAndConstFn | UnsafetyViolationKind::General => {
+ UnsafetyViolationKind::General => {
// once
struct_span_err!(
tcx.sess,