mirror of
https://github.com/rust-lang/rust.git
synced 2025-01-31 17:12:53 +00:00
Rollup merge of #84547 - RalfJung:max_const_fn, r=oli-obk
Get rid of is_min_const_fn This removes the last trace of the min_const_fn mechanism by making the unsafety checker agnostic about whether something is a min or "non-min" const fn. It seems this distinction was used to disallow some features inside `const fn`, but that is the responsibility of the const checker, not of the unsafety checker. No test seems to even notice this change in the unsafety checker so I guess we are good... r? `@oli-obk` Cc https://github.com/rust-lang/rust/issues/84510
This commit is contained in:
commit
000a630110
@ -19,10 +19,8 @@ use super::{Field, SourceInfo};
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, TyEncodable, TyDecodable, HashStable, Debug)]
|
||||
pub enum UnsafetyViolationKind {
|
||||
/// Only permitted in regular `fn`s, prohibited in `const fn`s.
|
||||
/// Unsafe operation outside `unsafe`.
|
||||
General,
|
||||
/// Permitted both in `const fn`s and regular `fn`s.
|
||||
GeneralAndConstFn,
|
||||
/// Unsafe operation in an `unsafe fn` but outside an `unsafe` block.
|
||||
/// Has to be handled as a lint for backwards compatibility.
|
||||
UnsafeFn,
|
||||
|
@ -1,4 +1,3 @@
|
||||
use rustc_attr as attr;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_middle::hir::map::blocks::FnLikeNode;
|
||||
@ -34,54 +33,6 @@ pub fn is_unstable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Symbol> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if this function must conform to `min_const_fn`
|
||||
pub fn is_min_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
|
||||
// Bail out if the signature doesn't contain `const`
|
||||
if !tcx.is_const_fn_raw(def_id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if tcx.features().staged_api {
|
||||
// In order for a libstd function to be considered min_const_fn
|
||||
// it needs to be stable and have no `rustc_const_unstable` attribute.
|
||||
match tcx.lookup_const_stability(def_id) {
|
||||
// `rustc_const_unstable` functions don't need to conform.
|
||||
Some(&attr::ConstStability { ref level, .. }) if level.is_unstable() => false,
|
||||
None => {
|
||||
if let Some(stab) = tcx.lookup_stability(def_id) {
|
||||
if stab.level.is_stable() {
|
||||
tcx.sess.delay_span_bug(
|
||||
tcx.def_span(def_id),
|
||||
"stable const functions must have either `rustc_const_stable` or \
|
||||
`rustc_const_unstable` attribute",
|
||||
);
|
||||
// While we errored above, because we don't know if we need to conform, we
|
||||
// err on the "safe" side and require min_const_fn.
|
||||
true
|
||||
} else {
|
||||
// Unstable functions need not conform to min_const_fn.
|
||||
false
|
||||
}
|
||||
} else {
|
||||
// Internal functions are forced to conform to min_const_fn.
|
||||
// Annotate the internal function with a const stability attribute if
|
||||
// you need to use unstable features.
|
||||
// Note: this is an arbitrary choice that does not affect stability or const
|
||||
// safety or anything, it just changes whether we need to annotate some
|
||||
// internal functions with `rustc_const_stable` or with `rustc_const_unstable`
|
||||
true
|
||||
}
|
||||
}
|
||||
// Everything else needs to conform, because it would be callable from
|
||||
// other `min_const_fn` functions.
|
||||
_ => true,
|
||||
}
|
||||
} else {
|
||||
// users enabling the `const_fn` feature gate can do what they want
|
||||
!tcx.features().const_fn
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_parent_const_impl_raw(tcx: TyCtxt<'_>, hir_id: hir::HirId) -> bool {
|
||||
let parent_id = tcx.hir().get_parent_did(hir_id);
|
||||
if !parent_id.is_top_level_module() { is_const_impl_raw(tcx, parent_id) } else { false }
|
||||
|
@ -15,13 +15,10 @@ use rustc_session::lint::Level;
|
||||
|
||||
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>,
|
||||
@ -34,21 +31,15 @@ pub struct UnsafetyChecker<'a, '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,
|
||||
@ -84,7 +75,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, '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,
|
||||
)
|
||||
}
|
||||
@ -134,7 +125,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
|
||||
match self.tcx.layout_scalar_valid_range(def.did) {
|
||||
(Bound::Unbounded, Bound::Unbounded) => {}
|
||||
_ => self.require_unsafe(
|
||||
UnsafetyViolationKind::GeneralAndConstFn,
|
||||
UnsafetyViolationKind::General,
|
||||
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;
|
||||
if base_ty.is_unsafe_ptr() {
|
||||
self.require_unsafe(
|
||||
UnsafetyViolationKind::GeneralAndConstFn,
|
||||
UnsafetyViolationKind::General,
|
||||
UnsafetyViolationDetails::DerefOfRawPointer,
|
||||
)
|
||||
}
|
||||
@ -258,7 +249,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
|
||||
);
|
||||
if !nodrop {
|
||||
self.require_unsafe(
|
||||
UnsafetyViolationKind::GeneralAndConstFn,
|
||||
UnsafetyViolationKind::General,
|
||||
UnsafetyViolationDetails::AssignToDroppingUnionField,
|
||||
);
|
||||
} else {
|
||||
@ -266,7 +257,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
|
||||
}
|
||||
} else {
|
||||
self.require_unsafe(
|
||||
UnsafetyViolationKind::GeneralAndConstFn,
|
||||
UnsafetyViolationKind::General,
|
||||
UnsafetyViolationDetails::AccessToUnionField,
|
||||
)
|
||||
}
|
||||
@ -277,6 +268,9 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
|
||||
|
||||
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
|
||||
@ -304,8 +298,7 @@ impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> {
|
||||
Safety::Safe => {
|
||||
for violation in violations {
|
||||
match violation.kind {
|
||||
UnsafetyViolationKind::GeneralAndConstFn
|
||||
| UnsafetyViolationKind::General => {}
|
||||
UnsafetyViolationKind::General => {}
|
||||
UnsafetyViolationKind::UnsafeFn => {
|
||||
bug!("`UnsafetyViolationKind::UnsafeFn` in an `Safe` context")
|
||||
}
|
||||
@ -334,29 +327,6 @@ impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> {
|
||||
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
|
||||
}
|
||||
};
|
||||
@ -394,7 +364,7 @@ impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> {
|
||||
} else {
|
||||
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`?
|
||||
if !callee_features.iter().all(|feature| self_features.contains(feature)) {
|
||||
self.require_unsafe(
|
||||
UnsafetyViolationKind::GeneralAndConstFn,
|
||||
UnsafetyViolationKind::General,
|
||||
UnsafetyViolationDetails::CallToFunctionWith,
|
||||
)
|
||||
}
|
||||
@ -494,15 +464,12 @@ fn unsafety_check_result<'tcx>(
|
||||
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);
|
||||
@ -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 { "" };
|
||||
|
||||
match kind {
|
||||
UnsafetyViolationKind::GeneralAndConstFn | UnsafetyViolationKind::General => {
|
||||
UnsafetyViolationKind::General => {
|
||||
// once
|
||||
struct_span_err!(
|
||||
tcx.sess,
|
||||
|
@ -53,17 +53,11 @@ pub struct RawVec<T, A: Allocator = Global> {
|
||||
}
|
||||
|
||||
impl<T> RawVec<T, Global> {
|
||||
/// HACK(Centril): This exists because `#[unstable]` `const fn`s needn't conform
|
||||
/// to `min_const_fn` and so they cannot be called in `min_const_fn`s either.
|
||||
/// HACK(Centril): This exists because stable `const fn` can only call stable `const fn`, so
|
||||
/// they cannot call `Self::new()`.
|
||||
///
|
||||
/// If you change `RawVec<T>::new` or dependencies, please take care to not
|
||||
/// introduce anything that would truly violate `min_const_fn`.
|
||||
///
|
||||
/// NOTE: We could avoid this hack and check conformance with some
|
||||
/// `#[rustc_force_min_const_fn]` attribute which requires conformance
|
||||
/// with `min_const_fn` but does not necessarily allow calling it in
|
||||
/// `stable(...) const fn` / user code not enabling `foo` when
|
||||
/// `#[rustc_const_unstable(feature = "foo", issue = "01234")]` is present.
|
||||
/// If you change `RawVec<T>::new` or dependencies, please take care to not introduce anything
|
||||
/// that would truly const-call something unstable.
|
||||
pub const NEW: Self = Self::new();
|
||||
|
||||
/// Creates the biggest possible `RawVec` (on the system heap)
|
||||
|
@ -10,7 +10,6 @@ use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::Mutability;
|
||||
use rustc_metadata::creader::LoadedMacro;
|
||||
use rustc_middle::ty::{self, TyCtxt};
|
||||
use rustc_mir::const_eval::is_min_const_fn;
|
||||
use rustc_span::hygiene::MacroKind;
|
||||
use rustc_span::symbol::{kw, sym, Symbol};
|
||||
use rustc_span::Span;
|
||||
@ -210,7 +209,7 @@ fn build_external_function(cx: &mut DocContext<'_>, did: DefId) -> clean::Functi
|
||||
let sig = cx.tcx.fn_sig(did);
|
||||
|
||||
let constness =
|
||||
if is_min_const_fn(cx.tcx, did) { hir::Constness::Const } else { hir::Constness::NotConst };
|
||||
if cx.tcx.is_const_fn_raw(did) { hir::Constness::Const } else { hir::Constness::NotConst };
|
||||
let asyncness = cx.tcx.asyncness(did);
|
||||
let predicates = cx.tcx.predicates_of(did);
|
||||
let (generics, decl) = clean::enter_impl_trait(cx, |cx| {
|
||||
|
@ -22,7 +22,7 @@ use rustc_middle::middle::resolve_lifetime as rl;
|
||||
use rustc_middle::ty::fold::TypeFolder;
|
||||
use rustc_middle::ty::subst::{InternalSubsts, Subst};
|
||||
use rustc_middle::ty::{self, AdtKind, Lift, Ty, TyCtxt};
|
||||
use rustc_mir::const_eval::{is_const_fn, is_min_const_fn, is_unstable_const_fn};
|
||||
use rustc_mir::const_eval::{is_const_fn, is_unstable_const_fn};
|
||||
use rustc_span::hygiene::{AstPass, MacroKind};
|
||||
use rustc_span::symbol::{kw, sym, Ident, Symbol};
|
||||
use rustc_span::{self, ExpnKind};
|
||||
@ -1048,7 +1048,7 @@ impl Clean<Item> for ty::AssocItem {
|
||||
ty::TraitContainer(_) => self.defaultness.has_value(),
|
||||
};
|
||||
if provided {
|
||||
let constness = if is_min_const_fn(tcx, self.def_id) {
|
||||
let constness = if tcx.is_const_fn_raw(self.def_id) {
|
||||
hir::Constness::Const
|
||||
} else {
|
||||
hir::Constness::NotConst
|
||||
|
@ -1,11 +1,25 @@
|
||||
// gate-test-const_raw_ptr_to_usize_cast
|
||||
// revisions: with_feature without_feature
|
||||
|
||||
#![cfg_attr(with_feature, feature(const_raw_ptr_to_usize_cast))]
|
||||
|
||||
fn main() {
|
||||
const X: u32 = unsafe {
|
||||
main as u32 //~ ERROR casting pointers to integers in constants is unstable
|
||||
const X: usize = unsafe {
|
||||
main as usize //[without_feature]~ ERROR casting pointers to integers in constants is unstable
|
||||
};
|
||||
const Y: u32 = 0;
|
||||
const Z: u32 = unsafe {
|
||||
&Y as *const u32 as u32 //~ ERROR is unstable
|
||||
const Z: usize = unsafe {
|
||||
&Y as *const u32 as usize //[without_feature]~ ERROR is unstable
|
||||
};
|
||||
// Cast in `const` without `unsafe` block
|
||||
const SAFE: usize = {
|
||||
&Y as *const u32 as usize //[without_feature]~ ERROR is unstable
|
||||
//[with_feature]~^ ERROR cast of pointer to int is unsafe and requires unsafe
|
||||
};
|
||||
}
|
||||
|
||||
// Cast in `const fn` without `unsafe` block
|
||||
const fn test() -> usize {
|
||||
&0 as *const i32 as usize //[without_feature]~ ERROR is unstable
|
||||
//[with_feature]~^ ERROR cast of pointer to int is unsafe and requires unsafe
|
||||
}
|
||||
|
@ -1,21 +0,0 @@
|
||||
error[E0658]: casting pointers to integers in constants is unstable
|
||||
--> $DIR/cast-ptr-to-int-const.rs:5:9
|
||||
|
|
||||
LL | main as u32
|
||||
| ^^^^^^^^^^^
|
||||
|
|
||||
= note: see issue #51910 <https://github.com/rust-lang/rust/issues/51910> for more information
|
||||
= help: add `#![feature(const_raw_ptr_to_usize_cast)]` to the crate attributes to enable
|
||||
|
||||
error[E0658]: casting pointers to integers in constants is unstable
|
||||
--> $DIR/cast-ptr-to-int-const.rs:9:9
|
||||
|
|
||||
LL | &Y as *const u32 as u32
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: see issue #51910 <https://github.com/rust-lang/rust/issues/51910> for more information
|
||||
= help: add `#![feature(const_raw_ptr_to_usize_cast)]` to the crate attributes to enable
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0658`.
|
19
src/test/ui/cast/cast-ptr-to-int-const.with_feature.stderr
Normal file
19
src/test/ui/cast/cast-ptr-to-int-const.with_feature.stderr
Normal file
@ -0,0 +1,19 @@
|
||||
error[E0133]: cast of pointer to int is unsafe and requires unsafe function or block
|
||||
--> $DIR/cast-ptr-to-int-const.rs:16:9
|
||||
|
|
||||
LL | &Y as *const u32 as usize
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^ cast of pointer to int
|
||||
|
|
||||
= note: casting pointers to integers in constants
|
||||
|
||||
error[E0133]: cast of pointer to int is unsafe and requires unsafe function or block
|
||||
--> $DIR/cast-ptr-to-int-const.rs:23:5
|
||||
|
|
||||
LL | &0 as *const i32 as usize
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^ cast of pointer to int
|
||||
|
|
||||
= note: casting pointers to integers in constants
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0133`.
|
@ -0,0 +1,39 @@
|
||||
error[E0658]: casting pointers to integers in constants is unstable
|
||||
--> $DIR/cast-ptr-to-int-const.rs:8:9
|
||||
|
|
||||
LL | main as usize
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
||||
= note: see issue #51910 <https://github.com/rust-lang/rust/issues/51910> for more information
|
||||
= help: add `#![feature(const_raw_ptr_to_usize_cast)]` to the crate attributes to enable
|
||||
|
||||
error[E0658]: casting pointers to integers in constants is unstable
|
||||
--> $DIR/cast-ptr-to-int-const.rs:12:9
|
||||
|
|
||||
LL | &Y as *const u32 as usize
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: see issue #51910 <https://github.com/rust-lang/rust/issues/51910> for more information
|
||||
= help: add `#![feature(const_raw_ptr_to_usize_cast)]` to the crate attributes to enable
|
||||
|
||||
error[E0658]: casting pointers to integers in constants is unstable
|
||||
--> $DIR/cast-ptr-to-int-const.rs:16:9
|
||||
|
|
||||
LL | &Y as *const u32 as usize
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: see issue #51910 <https://github.com/rust-lang/rust/issues/51910> for more information
|
||||
= help: add `#![feature(const_raw_ptr_to_usize_cast)]` to the crate attributes to enable
|
||||
|
||||
error[E0658]: casting pointers to integers in constant functions is unstable
|
||||
--> $DIR/cast-ptr-to-int-const.rs:23:5
|
||||
|
|
||||
LL | &0 as *const i32 as usize
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: see issue #51910 <https://github.com/rust-lang/rust/issues/51910> for more information
|
||||
= help: add `#![feature(const_raw_ptr_to_usize_cast)]` to the crate attributes to enable
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0658`.
|
@ -139,7 +139,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn {
|
||||
let mir = cx.tcx.optimized_mir(def_id);
|
||||
|
||||
if let Err((span, err)) = is_min_const_fn(cx.tcx, mir, self.msrv.as_ref()) {
|
||||
if rustc_mir::const_eval::is_min_const_fn(cx.tcx, def_id.to_def_id()) {
|
||||
if cx.tcx.is_const_fn_raw(def_id.to_def_id()) {
|
||||
cx.tcx.sess.span_err(span, &err);
|
||||
}
|
||||
} else {
|
||||
|
Loading…
Reference in New Issue
Block a user