summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_const_eval/src
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_const_eval/src')
-rw-r--r--compiler/rustc_const_eval/src/const_eval/eval_queries.rs2
-rw-r--r--compiler/rustc_const_eval/src/const_eval/fn_queries.rs18
-rw-r--r--compiler/rustc_const_eval/src/const_eval/machine.rs22
-rw-r--r--compiler/rustc_const_eval/src/const_eval/valtrees.rs5
-rw-r--r--compiler/rustc_const_eval/src/interpret/cast.rs51
-rw-r--r--compiler/rustc_const_eval/src/interpret/eval_context.rs70
-rw-r--r--compiler/rustc_const_eval/src/interpret/intrinsics.rs4
-rw-r--r--compiler/rustc_const_eval/src/interpret/intrinsics/caller_location.rs8
-rw-r--r--compiler/rustc_const_eval/src/interpret/machine.rs23
-rw-r--r--compiler/rustc_const_eval/src/interpret/mod.rs4
-rw-r--r--compiler/rustc_const_eval/src/interpret/place.rs15
-rw-r--r--compiler/rustc_const_eval/src/interpret/step.rs2
-rw-r--r--compiler/rustc_const_eval/src/interpret/terminator.rs60
-rw-r--r--compiler/rustc_const_eval/src/interpret/validity.rs10
-rw-r--r--compiler/rustc_const_eval/src/lib.rs3
-rw-r--r--compiler/rustc_const_eval/src/transform/check_consts/check.rs85
-rw-r--r--compiler/rustc_const_eval/src/transform/check_consts/ops.rs6
-rw-r--r--compiler/rustc_const_eval/src/transform/check_consts/post_drop_elaboration.rs7
-rw-r--r--compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs7
-rw-r--r--compiler/rustc_const_eval/src/transform/check_consts/resolver.rs15
-rw-r--r--compiler/rustc_const_eval/src/transform/promote_consts.rs17
-rw-r--r--compiler/rustc_const_eval/src/transform/validate.rs178
22 files changed, 351 insertions, 261 deletions
diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs
index 7564ba17b..4bd6fe199 100644
--- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs
+++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs
@@ -205,7 +205,7 @@ pub(crate) fn turn_into_const_value<'tcx>(
let cid = key.value;
let def_id = cid.instance.def.def_id();
let is_static = tcx.is_static(def_id);
- // This is just accessing an already computed constant, so no need to check alginment here.
+ // This is just accessing an already computed constant, so no need to check alignment here.
let ecx = mk_eval_cx(
tcx,
tcx.def_span(key.value.instance.def_id()),
diff --git a/compiler/rustc_const_eval/src/const_eval/fn_queries.rs b/compiler/rustc_const_eval/src/const_eval/fn_queries.rs
index 9eaab1f47..088a824fd 100644
--- a/compiler/rustc_const_eval/src/const_eval/fn_queries.rs
+++ b/compiler/rustc_const_eval/src/const_eval/fn_queries.rs
@@ -1,15 +1,22 @@
+use rustc_attr as attr;
use rustc_hir as hir;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_middle::ty::query::Providers;
-use rustc_middle::ty::{DefIdTree, TyCtxt};
+use rustc_middle::ty::TyCtxt;
use rustc_span::symbol::Symbol;
-/// Whether the `def_id` is an unstable const fn and what feature gate is necessary to enable it
-pub fn is_unstable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Symbol> {
+/// Whether the `def_id` is an unstable const fn and what feature gate(s) are necessary to enable
+/// it.
+pub fn is_unstable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> Option<(Symbol, Option<Symbol>)> {
if tcx.is_const_fn_raw(def_id) {
let const_stab = tcx.lookup_const_stability(def_id)?;
- if const_stab.is_const_unstable() { Some(const_stab.feature) } else { None }
+ match const_stab.level {
+ attr::StabilityLevel::Unstable { implied_by, .. } => {
+ Some((const_stab.feature, implied_by))
+ }
+ attr::StabilityLevel::Stable { .. } => None,
+ }
} else {
None
}
@@ -25,8 +32,7 @@ pub fn is_parent_const_impl_raw(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
/// it is a trait impl/function, return if it has a `const` modifier. If it is an intrinsic,
/// report whether said intrinsic has a `rustc_const_{un,}stable` attribute. Otherwise, return
/// `Constness::NotConst`.
-fn constness(tcx: TyCtxt<'_>, def_id: DefId) -> hir::Constness {
- let def_id = def_id.expect_local();
+fn constness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> hir::Constness {
let node = tcx.hir().get_by_def_id(def_id);
match node {
diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs
index a44f70ed0..a5dfd1072 100644
--- a/compiler/rustc_const_eval/src/const_eval/machine.rs
+++ b/compiler/rustc_const_eval/src/const_eval/machine.rs
@@ -2,7 +2,7 @@ use rustc_hir::def::DefKind;
use rustc_hir::{LangItem, CRATE_HIR_ID};
use rustc_middle::mir;
use rustc_middle::mir::interpret::PointerArithmetic;
-use rustc_middle::ty::layout::FnAbiOf;
+use rustc_middle::ty::layout::{FnAbiOf, TyAndLayout};
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_session::lint::builtin::INVALID_ALIGNMENT;
use std::borrow::Borrow;
@@ -23,7 +23,7 @@ use rustc_target::spec::abi::Abi as CallAbi;
use crate::interpret::{
self, compile_time_machine, AllocId, ConstAllocation, FnVal, Frame, ImmTy, InterpCx,
- InterpResult, OpTy, PlaceTy, Pointer, Scalar, StackPopUnwind,
+ InterpResult, OpTy, PlaceTy, Pointer, Scalar,
};
use super::error::*;
@@ -271,7 +271,7 @@ impl<'mir, 'tcx: 'mir> CompileTimeEvalContext<'mir, 'tcx> {
/* with_caller_location = */ false,
dest,
ret,
- StackPopUnwind::NotAllowed,
+ mir::UnwindAction::Unreachable,
)?;
Ok(ControlFlow::Break(()))
} else {
@@ -335,8 +335,8 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
}
#[inline(always)]
- fn enforce_validity(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool {
- ecx.tcx.sess.opts.unstable_opts.extra_const_ub_checks
+ fn enforce_validity(ecx: &InterpCx<'mir, 'tcx, Self>, layout: TyAndLayout<'tcx>) -> bool {
+ ecx.tcx.sess.opts.unstable_opts.extra_const_ub_checks || layout.abi.is_uninhabited()
}
fn alignment_check_failed(
@@ -401,7 +401,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
args: &[OpTy<'tcx>],
dest: &PlaceTy<'tcx>,
ret: Option<mir::BasicBlock>,
- _unwind: StackPopUnwind, // unwinding is not supported in consts
+ _unwind: mir::UnwindAction, // unwinding is not supported in consts
) -> InterpResult<'tcx, Option<(&'mir mir::Body<'tcx>, ty::Instance<'tcx>)>> {
debug!("find_mir_or_eval_fn: {:?}", instance);
@@ -450,7 +450,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
args: &[OpTy<'tcx>],
dest: &PlaceTy<'tcx, Self::Provenance>,
target: Option<mir::BasicBlock>,
- _unwind: StackPopUnwind,
+ _unwind: mir::UnwindAction,
) -> InterpResult<'tcx> {
// Shared intrinsics.
if ecx.emulate_intrinsic(instance, args, dest, target)? {
@@ -526,7 +526,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
fn assert_panic(
ecx: &mut InterpCx<'mir, 'tcx, Self>,
msg: &AssertMessage<'tcx>,
- _unwind: Option<mir::BasicBlock>,
+ _unwind: mir::UnwindAction,
) -> InterpResult<'tcx> {
use rustc_middle::mir::AssertKind::*;
// Convert `AssertKind<Operand>` to `AssertKind<Scalar>`.
@@ -544,6 +544,12 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
RemainderByZero(op) => RemainderByZero(eval_to_int(op)?),
ResumedAfterReturn(generator_kind) => ResumedAfterReturn(*generator_kind),
ResumedAfterPanic(generator_kind) => ResumedAfterPanic(*generator_kind),
+ MisalignedPointerDereference { ref required, ref found } => {
+ MisalignedPointerDereference {
+ required: eval_to_int(required)?,
+ found: eval_to_int(found)?,
+ }
+ }
};
Err(ConstEvalErrKind::AssertFailure(err).into())
}
diff --git a/compiler/rustc_const_eval/src/const_eval/valtrees.rs b/compiler/rustc_const_eval/src/const_eval/valtrees.rs
index a73f778d4..4d54c0183 100644
--- a/compiler/rustc_const_eval/src/const_eval/valtrees.rs
+++ b/compiler/rustc_const_eval/src/const_eval/valtrees.rs
@@ -8,7 +8,7 @@ use crate::interpret::{
use crate::interpret::{MPlaceTy, Value};
use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt};
use rustc_span::source_map::DUMMY_SP;
-use rustc_target::abi::{Align, VariantIdx};
+use rustc_target::abi::{Align, FieldIdx, VariantIdx, FIRST_VARIANT};
#[instrument(skip(ecx), level = "debug")]
fn branches<'tcx>(
@@ -412,7 +412,8 @@ fn valtree_into_mplace<'tcx>(
let inner_ty = match ty.kind() {
ty::Adt(def, substs) => {
- def.variant(VariantIdx::from_u32(0)).fields[i].ty(tcx, substs)
+ let i = FieldIdx::from_usize(i);
+ def.variant(FIRST_VARIANT).fields[i].ty(tcx, substs)
}
ty::Tuple(inner_tys) => inner_tys[i],
_ => bug!("unexpected unsized type {:?}", ty),
diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs
index 2be5ed896..163e3f869 100644
--- a/compiler/rustc_const_eval/src/interpret/cast.rs
+++ b/compiler/rustc_const_eval/src/interpret/cast.rs
@@ -67,12 +67,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
}
Pointer(PointerCast::ReifyFnPointer) => {
+ // All reifications must be monomorphic, bail out otherwise.
+ ensure_monomorphic_enough(*self.tcx, src.layout.ty)?;
+
// The src operand does not matter, just its type
match *src.layout.ty.kind() {
ty::FnDef(def_id, substs) => {
- // All reifications must be monomorphic, bail out otherwise.
- ensure_monomorphic_enough(*self.tcx, src.layout.ty)?;
-
let instance = ty::Instance::resolve_for_fn_ptr(
*self.tcx,
self.param_env,
@@ -100,12 +100,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
}
Pointer(PointerCast::ClosureFnPointer(_)) => {
+ // All reifications must be monomorphic, bail out otherwise.
+ ensure_monomorphic_enough(*self.tcx, src.layout.ty)?;
+
// The src operand does not matter, just its type
match *src.layout.ty.kind() {
ty::Closure(def_id, substs) => {
- // All reifications must be monomorphic, bail out otherwise.
- ensure_monomorphic_enough(*self.tcx, src.layout.ty)?;
-
let instance = ty::Instance::resolve_closure(
*self.tcx,
def_id,
@@ -133,6 +133,22 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
bug!()
}
}
+
+ Transmute => {
+ assert!(src.layout.is_sized());
+ assert!(dest.layout.is_sized());
+ if src.layout.size != dest.layout.size {
+ throw_ub_format!(
+ "transmuting from {}-byte type to {}-byte type: `{}` -> `{}`",
+ src.layout.size.bytes(),
+ dest.layout.size.bytes(),
+ src.layout.ty,
+ dest.layout.ty,
+ );
+ }
+
+ self.copy_op(src, dest, /*allow_transmute*/ true)?;
+ }
}
Ok(())
}
@@ -359,8 +375,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
let val = Immediate::new_dyn_trait(ptr, vtable, &*self.tcx);
self.write_immediate(val, dest)
}
-
_ => {
+ // Do not ICE if we are not monomorphic enough.
+ ensure_monomorphic_enough(*self.tcx, src.layout.ty)?;
+ ensure_monomorphic_enough(*self.tcx, cast_ty)?;
+
span_bug!(
self.cur_span(),
"invalid pointer unsizing {:?} -> {:?}",
@@ -404,12 +423,18 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
}
Ok(())
}
- _ => span_bug!(
- self.cur_span(),
- "unsize_into: invalid conversion: {:?} -> {:?}",
- src.layout,
- dest.layout
- ),
+ _ => {
+ // Do not ICE if we are not monomorphic enough.
+ ensure_monomorphic_enough(*self.tcx, src.layout.ty)?;
+ ensure_monomorphic_enough(*self.tcx, cast_ty.ty)?;
+
+ span_bug!(
+ self.cur_span(),
+ "unsize_into: invalid conversion: {:?} -> {:?}",
+ src.layout,
+ dest.layout
+ )
+ }
}
}
}
diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs
index 3db102e48..3e58a58ae 100644
--- a/compiler/rustc_const_eval/src/interpret/eval_context.rs
+++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs
@@ -7,7 +7,7 @@ use either::{Either, Left, Right};
use rustc_hir::{self as hir, def_id::DefId, definitions::DefPathData};
use rustc_index::vec::IndexVec;
use rustc_middle::mir;
-use rustc_middle::mir::interpret::{ErrorHandled, InterpError, InvalidProgramInfo};
+use rustc_middle::mir::interpret::{ErrorHandled, InterpError};
use rustc_middle::ty::layout::{
self, FnAbiError, FnAbiOfHelpers, FnAbiRequest, LayoutError, LayoutOf, LayoutOfHelpers,
TyAndLayout,
@@ -139,17 +139,6 @@ pub struct FrameInfo<'tcx> {
pub lint_root: Option<hir::HirId>,
}
-/// Unwind information.
-#[derive(Clone, Copy, Eq, PartialEq, Debug)]
-pub enum StackPopUnwind {
- /// The cleanup block.
- Cleanup(mir::BasicBlock),
- /// No cleanup needs to be done.
- Skip,
- /// Unwinding is not allowed (UB).
- NotAllowed,
-}
-
#[derive(Clone, Copy, Eq, PartialEq, Debug)] // Miri debug-prints these
pub enum StackPopCleanup {
/// Jump to the next block in the caller, or cause UB if None (that's a function
@@ -157,7 +146,7 @@ pub enum StackPopCleanup {
/// we can validate it at that layout.
/// `ret` stores the block we jump to on a normal return, while `unwind`
/// stores the block used for cleanup during unwinding.
- Goto { ret: Option<mir::BasicBlock>, unwind: StackPopUnwind },
+ Goto { ret: Option<mir::BasicBlock>, unwind: mir::UnwindAction },
/// The root frame of the stack: nowhere else to jump to.
/// `cleanup` says whether locals are deallocated. Static computation
/// wants them leaked to intern what they need (and just throw away
@@ -508,14 +497,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
frame
.instance
.try_subst_mir_and_normalize_erasing_regions(*self.tcx, self.param_env, value)
- .map_err(|e| {
- self.tcx.sess.delay_span_bug(
- self.cur_span(),
- format!("failed to normalize {}", e.get_type_for_failure()).as_str(),
- );
-
- InterpError::InvalidProgram(InvalidProgramInfo::TooGeneric)
- })
+ .map_err(|_| err_inval!(TooGeneric))
}
/// The `substs` are assumed to already be in our interpreter "universe" (param_env).
@@ -543,24 +525,20 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
local: mir::Local,
layout: Option<TyAndLayout<'tcx>>,
) -> InterpResult<'tcx, TyAndLayout<'tcx>> {
- // `const_prop` runs into this with an invalid (empty) frame, so we
- // have to support that case (mostly by skipping all caching).
- match frame.locals.get(local).and_then(|state| state.layout.get()) {
- None => {
- let layout = from_known_layout(self.tcx, self.param_env, layout, || {
- let local_ty = frame.body.local_decls[local].ty;
- let local_ty =
- self.subst_from_frame_and_normalize_erasing_regions(frame, local_ty)?;
- self.layout_of(local_ty)
- })?;
- if let Some(state) = frame.locals.get(local) {
- // Layouts of locals are requested a lot, so we cache them.
- state.layout.set(Some(layout));
- }
- Ok(layout)
- }
- Some(layout) => Ok(layout),
+ let state = &frame.locals[local];
+ if let Some(layout) = state.layout.get() {
+ return Ok(layout);
}
+
+ let layout = from_known_layout(self.tcx, self.param_env, layout, || {
+ let local_ty = frame.body.local_decls[local].ty;
+ let local_ty = self.subst_from_frame_and_normalize_erasing_regions(frame, local_ty)?;
+ self.layout_of(local_ty)
+ })?;
+
+ // Layouts of locals are requested a lot, so we cache them.
+ state.layout.set(Some(layout));
+ Ok(layout)
}
/// Returns the actual dynamic size and alignment of the place at the given type.
@@ -746,18 +724,22 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
/// *Unwind* to the given `target` basic block.
/// Do *not* use for returning! Use `return_to_block` instead.
///
- /// If `target` is `StackPopUnwind::Skip`, that indicates the function does not need cleanup
+ /// If `target` is `UnwindAction::Continue`, that indicates the function does not need cleanup
/// during unwinding, and we will just keep propagating that upwards.
///
- /// If `target` is `StackPopUnwind::NotAllowed`, that indicates the function does not allow
+ /// If `target` is `UnwindAction::Unreachable`, that indicates the function does not allow
/// unwinding, and doing so is UB.
- pub fn unwind_to_block(&mut self, target: StackPopUnwind) -> InterpResult<'tcx> {
+ pub fn unwind_to_block(&mut self, target: mir::UnwindAction) -> InterpResult<'tcx> {
self.frame_mut().loc = match target {
- StackPopUnwind::Cleanup(block) => Left(mir::Location { block, statement_index: 0 }),
- StackPopUnwind::Skip => Right(self.frame_mut().body.span),
- StackPopUnwind::NotAllowed => {
+ mir::UnwindAction::Cleanup(block) => Left(mir::Location { block, statement_index: 0 }),
+ mir::UnwindAction::Continue => Right(self.frame_mut().body.span),
+ mir::UnwindAction::Unreachable => {
throw_ub_format!("unwinding past a stack frame that does not allow unwinding")
}
+ mir::UnwindAction::Terminate => {
+ self.frame_mut().loc = Right(self.frame_mut().body.span);
+ M::abort(self, "panic in a function that cannot unwind".to_owned())?;
+ }
};
Ok(())
}
diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
index a29cdade0..26fb041b4 100644
--- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs
+++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
@@ -127,7 +127,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
// First handle intrinsics without return place.
let ret = match ret {
None => match intrinsic_name {
- sym::transmute => throw_ub_format!("transmuting to uninhabited type"),
sym::abort => M::abort(self, "the program aborted execution".to_owned())?,
// Unsupported diverging intrinsic.
_ => return Ok(false),
@@ -411,9 +410,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
self.exact_div(&val, &size, dest)?;
}
- sym::transmute => {
- self.copy_op(&args[0], dest, /*allow_transmute*/ true)?;
- }
sym::assert_inhabited
| sym::assert_zero_valid
| sym::assert_mem_uninitialized_valid => {
diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics/caller_location.rs b/compiler/rustc_const_eval/src/interpret/intrinsics/caller_location.rs
index cf52299b7..3701eb93e 100644
--- a/compiler/rustc_const_eval/src/interpret/intrinsics/caller_location.rs
+++ b/compiler/rustc_const_eval/src/interpret/intrinsics/caller_location.rs
@@ -77,7 +77,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
line: u32,
col: u32,
) -> MPlaceTy<'tcx, M::Provenance> {
- let loc_details = &self.tcx.sess.opts.unstable_opts.location_detail;
+ let loc_details = self.tcx.sess.opts.unstable_opts.location_detail;
// This can fail if rustc runs out of memory right here. Trying to emit an error would be
// pointless, since that would require allocating more memory than these short strings.
let file = if loc_details.file {
@@ -111,7 +111,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
location
}
- pub(crate) fn location_triple_for_span(&self, span: Span) -> (Symbol, u32, u32) {
+ pub(crate) fn location_triple_for_span(&self, mut span: Span) -> (Symbol, u32, u32) {
+ // Remove `Inlined` marks as they pollute `expansion_cause`.
+ while span.is_inlined() {
+ span.remove_mark();
+ }
let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span);
let caller = self.tcx.sess.source_map().lookup_char_pos(topmost.lo());
(
diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs
index 92fa59aec..0291cca73 100644
--- a/compiler/rustc_const_eval/src/interpret/machine.rs
+++ b/compiler/rustc_const_eval/src/interpret/machine.rs
@@ -8,6 +8,7 @@ use std::hash::Hash;
use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece};
use rustc_middle::mir;
+use rustc_middle::ty::layout::TyAndLayout;
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_span::def_id::DefId;
use rustc_target::abi::{Align, Size};
@@ -17,7 +18,7 @@ use crate::const_eval::CheckAlignment;
use super::{
AllocBytes, AllocId, AllocRange, Allocation, ConstAllocation, Frame, ImmTy, InterpCx,
- InterpResult, MemoryKind, OpTy, Operand, PlaceTy, Pointer, Provenance, Scalar, StackPopUnwind,
+ InterpResult, MemoryKind, OpTy, Operand, PlaceTy, Pointer, Provenance, Scalar,
};
/// Data returned by Machine::stack_pop,
@@ -145,8 +146,8 @@ pub trait Machine<'mir, 'tcx>: Sized {
check: CheckAlignment,
) -> InterpResult<'tcx, ()>;
- /// Whether to enforce the validity invariant
- fn enforce_validity(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool;
+ /// Whether to enforce the validity invariant for a specific layout.
+ fn enforce_validity(ecx: &InterpCx<'mir, 'tcx, Self>, layout: TyAndLayout<'tcx>) -> bool;
/// Whether function calls should be [ABI](CallAbi)-checked.
fn enforce_abi(_ecx: &InterpCx<'mir, 'tcx, Self>) -> bool {
@@ -155,7 +156,7 @@ pub trait Machine<'mir, 'tcx>: Sized {
/// Whether Assert(OverflowNeg) and Assert(Overflow) MIR terminators should actually
/// check for overflow.
- fn ignore_checkable_overflow_assertions(_ecx: &InterpCx<'mir, 'tcx, Self>) -> bool;
+ fn ignore_optional_overflow_checks(_ecx: &InterpCx<'mir, 'tcx, Self>) -> bool;
/// Entry point for obtaining the MIR of anything that should get evaluated.
/// So not just functions and shims, but also const/static initializers, anonymous
@@ -184,7 +185,7 @@ pub trait Machine<'mir, 'tcx>: Sized {
args: &[OpTy<'tcx, Self::Provenance>],
destination: &PlaceTy<'tcx, Self::Provenance>,
target: Option<mir::BasicBlock>,
- unwind: StackPopUnwind,
+ unwind: mir::UnwindAction,
) -> InterpResult<'tcx, Option<(&'mir mir::Body<'tcx>, ty::Instance<'tcx>)>>;
/// Execute `fn_val`. It is the hook's responsibility to advance the instruction
@@ -196,7 +197,7 @@ pub trait Machine<'mir, 'tcx>: Sized {
args: &[OpTy<'tcx, Self::Provenance>],
destination: &PlaceTy<'tcx, Self::Provenance>,
target: Option<mir::BasicBlock>,
- unwind: StackPopUnwind,
+ unwind: mir::UnwindAction,
) -> InterpResult<'tcx>;
/// Directly process an intrinsic without pushing a stack frame. It is the hook's
@@ -207,17 +208,17 @@ pub trait Machine<'mir, 'tcx>: Sized {
args: &[OpTy<'tcx, Self::Provenance>],
destination: &PlaceTy<'tcx, Self::Provenance>,
target: Option<mir::BasicBlock>,
- unwind: StackPopUnwind,
+ unwind: mir::UnwindAction,
) -> InterpResult<'tcx>;
/// Called to evaluate `Assert` MIR terminators that trigger a panic.
fn assert_panic(
ecx: &mut InterpCx<'mir, 'tcx, Self>,
msg: &mir::AssertMessage<'tcx>,
- unwind: Option<mir::BasicBlock>,
+ unwind: mir::UnwindAction,
) -> InterpResult<'tcx>;
- /// Called to evaluate `Abort` MIR terminator.
+ /// Called to abort evaluation.
fn abort(_ecx: &mut InterpCx<'mir, 'tcx, Self>, _msg: String) -> InterpResult<'tcx, !> {
throw_unsup_format!("aborting execution is not supported")
}
@@ -474,7 +475,7 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) {
}
#[inline(always)]
- fn ignore_checkable_overflow_assertions(_ecx: &InterpCx<$mir, $tcx, Self>) -> bool {
+ fn ignore_optional_overflow_checks(_ecx: &InterpCx<$mir, $tcx, Self>) -> bool {
false
}
@@ -486,7 +487,7 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) {
_args: &[OpTy<$tcx>],
_destination: &PlaceTy<$tcx, Self::Provenance>,
_target: Option<mir::BasicBlock>,
- _unwind: StackPopUnwind,
+ _unwind: mir::UnwindAction,
) -> InterpResult<$tcx> {
match fn_val {}
}
diff --git a/compiler/rustc_const_eval/src/interpret/mod.rs b/compiler/rustc_const_eval/src/interpret/mod.rs
index 86de4e4e3..898d62361 100644
--- a/compiler/rustc_const_eval/src/interpret/mod.rs
+++ b/compiler/rustc_const_eval/src/interpret/mod.rs
@@ -20,9 +20,7 @@ mod visitor;
pub use rustc_middle::mir::interpret::*; // have all the `interpret` symbols in one place: here
-pub use self::eval_context::{
- Frame, FrameInfo, InterpCx, LocalState, LocalValue, StackPopCleanup, StackPopUnwind,
-};
+pub use self::eval_context::{Frame, FrameInfo, InterpCx, LocalState, LocalValue, StackPopCleanup};
pub use self::intern::{intern_const_alloc_recursive, InternKind};
pub use self::machine::{compile_time_machine, AllocMap, Machine, MayLeak, StackPopJump};
pub use self::memory::{AllocKind, AllocRef, AllocRefMut, FnVal, Memory, MemoryKind};
diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs
index 3c463500a..03b09cf83 100644
--- a/compiler/rustc_const_eval/src/interpret/place.rs
+++ b/compiler/rustc_const_eval/src/interpret/place.rs
@@ -5,10 +5,11 @@
use either::{Either, Left, Right};
use rustc_ast::Mutability;
+use rustc_index::vec::IndexSlice;
use rustc_middle::mir;
use rustc_middle::ty;
use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
-use rustc_target::abi::{self, Abi, Align, HasDataLayout, Size, VariantIdx};
+use rustc_target::abi::{self, Abi, Align, FieldIdx, HasDataLayout, Size, FIRST_VARIANT};
use super::{
alloc_range, mir_assign_valid_types, AllocId, AllocRef, AllocRefMut, CheckInAllocMsg,
@@ -461,7 +462,7 @@ where
) -> InterpResult<'tcx> {
self.write_immediate_no_validate(src, dest)?;
- if M::enforce_validity(self) {
+ if M::enforce_validity(self, dest.layout) {
// Data got changed, better make sure it matches the type!
self.validate_operand(&self.place_to_op(dest)?)?;
}
@@ -616,7 +617,7 @@ where
) -> InterpResult<'tcx> {
self.copy_op_no_validate(src, dest, allow_transmute)?;
- if M::enforce_validity(self) {
+ if M::enforce_validity(self, dest.layout) {
// Data got changed, better make sure it matches the type!
self.validate_operand(&self.place_to_op(dest)?)?;
}
@@ -787,7 +788,7 @@ where
pub fn write_aggregate(
&mut self,
kind: &mir::AggregateKind<'tcx>,
- operands: &[mir::Operand<'tcx>],
+ operands: &IndexSlice<FieldIdx, mir::Operand<'tcx>>,
dest: &PlaceTy<'tcx, M::Provenance>,
) -> InterpResult<'tcx> {
self.write_uninit(&dest)?;
@@ -796,14 +797,14 @@ where
let variant_dest = self.place_downcast(&dest, variant_index)?;
(variant_index, variant_dest, active_field_index)
}
- _ => (VariantIdx::from_u32(0), dest.clone(), None),
+ _ => (FIRST_VARIANT, dest.clone(), None),
};
if active_field_index.is_some() {
assert_eq!(operands.len(), 1);
}
- for (field_index, operand) in operands.iter().enumerate() {
+ for (field_index, operand) in operands.iter_enumerated() {
let field_index = active_field_index.unwrap_or(field_index);
- let field_dest = self.place_field(&variant_dest, field_index)?;
+ let field_dest = self.place_field(&variant_dest, field_index.as_usize())?;
let op = self.eval_operand(operand, Some(field_dest.layout))?;
self.copy_op(&op, &field_dest, /*allow_transmute*/ false)?;
}
diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs
index 6863435e5..9a366364e 100644
--- a/compiler/rustc_const_eval/src/interpret/step.rs
+++ b/compiler/rustc_const_eval/src/interpret/step.rs
@@ -114,7 +114,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
Intrinsic(box intrinsic) => self.emulate_nondiverging_intrinsic(intrinsic)?,
// Statements we do not track.
- AscribeUserType(..) => {}
+ PlaceMention(..) | AscribeUserType(..) => {}
// Currently, Miri discards Coverage statements. Coverage statements are only injected
// via an optional compile time MIR pass and have no side effects. Since Coverage
diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/terminator.rs
index 2aea7c79b..a07702f7d 100644
--- a/compiler/rustc_const_eval/src/interpret/terminator.rs
+++ b/compiler/rustc_const_eval/src/interpret/terminator.rs
@@ -13,7 +13,7 @@ use rustc_target::spec::abi::Abi;
use super::{
FnVal, ImmTy, Immediate, InterpCx, InterpResult, MPlaceTy, Machine, MemoryKind, OpTy, Operand,
- PlaceTy, Scalar, StackPopCleanup, StackPopUnwind,
+ PlaceTy, Scalar, StackPopCleanup,
};
impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
@@ -60,7 +60,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
ref args,
destination,
target,
- ref cleanup,
+ unwind,
from_hir_call: _,
fn_span: _,
} => {
@@ -106,11 +106,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
with_caller_location,
&destination,
target,
- match (cleanup, fn_abi.can_unwind) {
- (Some(cleanup), true) => StackPopUnwind::Cleanup(*cleanup),
- (None, true) => StackPopUnwind::Skip,
- (_, false) => StackPopUnwind::NotAllowed,
- },
+ if fn_abi.can_unwind { unwind } else { mir::UnwindAction::Unreachable },
)?;
// Sanity-check that `eval_fn_call` either pushed a new frame or
// did a jump to another block.
@@ -137,23 +133,20 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
self.drop_in_place(&place, instance, target, unwind)?;
}
- Assert { ref cond, expected, ref msg, target, cleanup } => {
- let ignored = M::ignore_checkable_overflow_assertions(self)
- && match msg {
- mir::AssertKind::OverflowNeg(..) => true,
- mir::AssertKind::Overflow(op, ..) => op.is_checkable(),
- _ => false,
- };
+ Assert { ref cond, expected, ref msg, target, unwind } => {
+ let ignored =
+ M::ignore_optional_overflow_checks(self) && msg.is_optional_overflow_check();
let cond_val = self.read_scalar(&self.eval_operand(cond, None)?)?.to_bool()?;
if ignored || expected == cond_val {
self.go_to_block(target);
} else {
- M::assert_panic(self, msg, cleanup)?;
+ M::assert_panic(self, msg, unwind)?;
}
}
- Abort => {
- M::abort(self, "the program aborted execution".to_owned())?;
+ Terminate => {
+ // FIXME: maybe should call `panic_no_unwind` lang item instead.
+ M::abort(self, "panic in a function that cannot unwind".to_owned())?;
}
// When we encounter Resume, we've finished unwinding
@@ -171,11 +164,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
Unreachable => throw_ub!(Unreachable),
// These should never occur for MIR we actually run.
- DropAndReplace { .. }
- | FalseEdge { .. }
- | FalseUnwind { .. }
- | Yield { .. }
- | GeneratorDrop => span_bug!(
+ FalseEdge { .. } | FalseUnwind { .. } | Yield { .. } | GeneratorDrop => span_bug!(
terminator.source_info.span,
"{:#?} should have been eliminated by MIR pass",
terminator.kind
@@ -359,7 +348,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
with_caller_location: bool,
destination: &PlaceTy<'tcx, M::Provenance>,
target: Option<mir::BasicBlock>,
- mut unwind: StackPopUnwind,
+ mut unwind: mir::UnwindAction,
) -> InterpResult<'tcx> {
trace!("eval_fn_call: {:#?}", fn_val);
@@ -390,6 +379,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
| ty::InstanceDef::FnPtrShim(..)
| ty::InstanceDef::DropGlue(..)
| ty::InstanceDef::CloneShim(..)
+ | ty::InstanceDef::FnPtrAddrShim(..)
+ | ty::InstanceDef::ThreadLocalShim(..)
| ty::InstanceDef::Item(_) => {
// We need MIR for this fn
let Some((body, instance)) =
@@ -416,9 +407,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
}
}
- if !matches!(unwind, StackPopUnwind::NotAllowed) && !callee_fn_abi.can_unwind {
- // The callee cannot unwind.
- unwind = StackPopUnwind::NotAllowed;
+ if !callee_fn_abi.can_unwind {
+ // The callee cannot unwind, so force the `Unreachable` unwind handling.
+ unwind = mir::UnwindAction::Unreachable;
}
self.push_stack_frame(
@@ -547,7 +538,15 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
let mut receiver = args[0].clone();
let receiver_place = loop {
match receiver.layout.ty.kind() {
- ty::Ref(..) | ty::RawPtr(..) => break self.deref_operand(&receiver)?,
+ ty::Ref(..) | ty::RawPtr(..) => {
+ // We do *not* use `deref_operand` here: we don't want to conceptually
+ // create a place that must be dereferenceable, since the receiver might
+ // be a raw pointer and (for `*const dyn Trait`) we don't need to
+ // actually access memory to resolve this method.
+ // Also see <https://github.com/rust-lang/miri/issues/2786>.
+ let val = self.read_immediate(&receiver)?;
+ break self.ref_to_mplace(&val)?;
+ }
ty::Dynamic(.., ty::Dyn) => break receiver.assert_mem_place(), // no immediate unsized values
ty::Dynamic(.., ty::DynStar) => {
// Not clear how to handle this, so far we assume the receiver is always a pointer.
@@ -674,7 +673,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
place: &PlaceTy<'tcx, M::Provenance>,
instance: ty::Instance<'tcx>,
target: mir::BasicBlock,
- unwind: Option<mir::BasicBlock>,
+ unwind: mir::UnwindAction,
) -> InterpResult<'tcx> {
trace!("drop_in_place: {:?},\n {:?}, {:?}", *place, place.layout.ty, instance);
// We take the address of the object. This may well be unaligned, which is fine
@@ -715,10 +714,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
false,
&ret.into(),
Some(target),
- match unwind {
- Some(cleanup) => StackPopUnwind::Cleanup(cleanup),
- None => StackPopUnwind::Skip,
- },
+ unwind,
)
}
}
diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs
index f7881c509..93b5273e1 100644
--- a/compiler/rustc_const_eval/src/interpret/validity.rs
+++ b/compiler/rustc_const_eval/src/interpret/validity.rs
@@ -16,7 +16,9 @@ use rustc_middle::mir::interpret::InterpError;
use rustc_middle::ty;
use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
use rustc_span::symbol::{sym, Symbol};
-use rustc_target::abi::{Abi, Scalar as ScalarAbi, Size, VariantIdx, Variants, WrappingRange};
+use rustc_target::abi::{
+ Abi, FieldIdx, Scalar as ScalarAbi, Size, VariantIdx, Variants, WrappingRange,
+};
use std::hash::Hash;
@@ -269,14 +271,16 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
match layout.variants {
Variants::Single { index } => {
// Inside a variant
- PathElem::Field(def.variant(index).fields[field].name)
+ PathElem::Field(def.variant(index).fields[FieldIdx::from_usize(field)].name)
}
Variants::Multiple { .. } => bug!("we handled variants above"),
}
}
// other ADTs
- ty::Adt(def, _) => PathElem::Field(def.non_enum_variant().fields[field].name),
+ ty::Adt(def, _) => {
+ PathElem::Field(def.non_enum_variant().fields[FieldIdx::from_usize(field)].name)
+ }
// arrays/slices
ty::Array(..) | ty::Slice(..) => PathElem::ArrayElem(field),
diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs
index ed9efe568..5ab389d04 100644
--- a/compiler/rustc_const_eval/src/lib.rs
+++ b/compiler/rustc_const_eval/src/lib.rs
@@ -20,7 +20,6 @@ Rust MIR: a lowered representation of Rust.
#![feature(try_blocks)]
#![feature(yeet_expr)]
#![feature(if_let_guard)]
-#![feature(is_some_and)]
#![recursion_limit = "256"]
#[macro_use]
@@ -39,7 +38,7 @@ use rustc_macros::fluent_messages;
use rustc_middle::ty;
use rustc_middle::ty::query::Providers;
-fluent_messages! { "../locales/en-US.ftl" }
+fluent_messages! { "../messages.ftl" }
pub fn provide(providers: &mut Providers) {
const_eval::provide(providers);
diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs
index aa24d9053..55080d94f 100644
--- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs
+++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs
@@ -246,7 +246,7 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> {
self.check_local_or_return_ty(return_ty.skip_binder(), RETURN_PLACE);
}
- if !tcx.has_attr(def_id.to_def_id(), sym::rustc_do_not_const_check) {
+ if !tcx.has_attr(def_id, sym::rustc_do_not_const_check) {
self.visit_body(&body);
}
@@ -412,9 +412,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
BorrowKind::Shallow => {
PlaceContext::NonMutatingUse(NonMutatingUseContext::ShallowBorrow)
}
- BorrowKind::Unique => {
- PlaceContext::NonMutatingUse(NonMutatingUseContext::UniqueBorrow)
- }
+ BorrowKind::Unique => PlaceContext::MutatingUse(MutatingUseContext::Borrow),
BorrowKind::Mut { .. } => {
PlaceContext::MutatingUse(MutatingUseContext::Borrow)
}
@@ -553,7 +551,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
}
Rvalue::Cast(CastKind::DynStar, _, _) => {
- unimplemented!()
+ // `dyn*` coercion is implemented for CTFE.
}
Rvalue::Cast(_, _, _) => {}
@@ -643,7 +641,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
if base_ty.is_unsafe_ptr() {
if proj_base.is_empty() {
let decl = &self.body.local_decls[place_local];
- if let Some(box LocalInfo::StaticRef { def_id, .. }) = decl.local_info {
+ if let LocalInfo::StaticRef { def_id, .. } = *decl.local_info() {
let span = decl.source_info.span;
self.check_static(def_id, span);
return;
@@ -690,6 +688,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
| StatementKind::StorageLive(_)
| StatementKind::StorageDead(_)
| StatementKind::Retag { .. }
+ | StatementKind::PlaceMention(..)
| StatementKind::AscribeUserType(..)
| StatementKind::Coverage(..)
| StatementKind::Intrinsic(..)
@@ -721,6 +720,32 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
}
};
+ // Check that all trait bounds that are marked as `~const` can be satisfied.
+ //
+ // Typeck only does a "non-const" check since it operates on HIR and cannot distinguish
+ // which path expressions are getting called on and which path expressions are only used
+ // as function pointers. This is required for correctness.
+ let infcx = tcx.infer_ctxt().build();
+ let ocx = ObligationCtxt::new(&infcx);
+
+ let predicates = tcx.predicates_of(callee).instantiate(tcx, substs);
+ let cause = ObligationCause::new(
+ terminator.source_info.span,
+ self.body.source.def_id().expect_local(),
+ ObligationCauseCode::ItemObligation(callee),
+ );
+ let normalized_predicates = ocx.normalize(&cause, param_env, predicates);
+ ocx.register_obligations(traits::predicates_for_generics(
+ |_, _| cause.clone(),
+ self.param_env,
+ normalized_predicates,
+ ));
+
+ let errors = ocx.select_all_or_error();
+ if !errors.is_empty() {
+ infcx.err_ctxt().report_fulfillment_errors(&errors);
+ }
+
// Attempting to call a trait method?
if let Some(trait_id) = tcx.trait_of_item(callee) {
trace!("attempting to call a trait method");
@@ -748,31 +773,6 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
selcx.select(&obligation)
};
- // do a well-formedness check on the trait method being called. This is because typeck only does a
- // "non-const" check. This is required for correctness here.
- {
- let infcx = tcx.infer_ctxt().build();
- let ocx = ObligationCtxt::new(&infcx);
-
- let predicates = tcx.predicates_of(callee).instantiate(tcx, substs);
- let cause = ObligationCause::new(
- terminator.source_info.span,
- self.body.source.def_id().expect_local(),
- ObligationCauseCode::ItemObligation(callee),
- );
- let normalized_predicates = ocx.normalize(&cause, param_env, predicates);
- ocx.register_obligations(traits::predicates_for_generics(
- |_, _| cause.clone(),
- self.param_env,
- normalized_predicates,
- ));
-
- let errors = ocx.select_all_or_error();
- if !errors.is_empty() {
- infcx.err_ctxt().report_fulfillment_errors(&errors, None);
- }
- }
-
match implsrc {
Ok(Some(ImplSource::Param(_, ty::BoundConstness::ConstIfConst))) => {
debug!(
@@ -926,15 +926,24 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
// If the `const fn` we are trying to call is not const-stable, ensure that we have
// the proper feature gate enabled.
- if let Some(gate) = is_unstable_const_fn(tcx, callee) {
+ if let Some((gate, implied_by)) = is_unstable_const_fn(tcx, callee) {
trace!(?gate, "calling unstable const fn");
if self.span.allows_unstable(gate) {
return;
}
+ if let Some(implied_by_gate) = implied_by && self.span.allows_unstable(implied_by_gate) {
+ return;
+ }
// Calling an unstable function *always* requires that the corresponding gate
- // be enabled, even if the function has `#[rustc_allow_const_fn_unstable(the_gate)]`.
- if !tcx.features().declared_lib_features.iter().any(|&(sym, _)| sym == gate) {
+ // (or implied gate) be enabled, even if the function has
+ // `#[rustc_allow_const_fn_unstable(the_gate)]`.
+ let gate_declared = |gate| {
+ tcx.features().declared_lib_features.iter().any(|&(sym, _)| sym == gate)
+ };
+ let feature_gate_declared = gate_declared(gate);
+ let implied_gate_declared = implied_by.map(gate_declared).unwrap_or(false);
+ if !feature_gate_declared && !implied_gate_declared {
self.check_op(ops::FnCallUnstable(callee, Some(gate)));
return;
}
@@ -947,7 +956,6 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
}
// Otherwise, we are something const-stable calling a const-unstable fn.
-
if super::rustc_allow_const_fn_unstable(tcx, caller, gate) {
trace!("rustc_allow_const_fn_unstable gate active");
return;
@@ -977,8 +985,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
// Forbid all `Drop` terminators unless the place being dropped is a local with no
// projections that cannot be `NeedsNonConstDrop`.
- TerminatorKind::Drop { place: dropped_place, .. }
- | TerminatorKind::DropAndReplace { place: dropped_place, .. } => {
+ TerminatorKind::Drop { place: dropped_place, .. } => {
// If we are checking live drops after drop-elaboration, don't emit duplicate
// errors here.
if super::post_drop_elaboration::checking_enabled(self.ccx) {
@@ -1022,9 +1029,9 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
self.check_op(ops::Generator(hir::GeneratorKind::Gen))
}
- TerminatorKind::Abort => {
+ TerminatorKind::Terminate => {
// Cleanup blocks are skipped for const checking (see `visit_basic_block_data`).
- span_bug!(self.span, "`Abort` terminator outside of cleanup block")
+ span_bug!(self.span, "`Terminate` terminator outside of cleanup block")
}
TerminatorKind::Assert { .. }
diff --git a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs
index 3e416b89c..c0f5b3725 100644
--- a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs
+++ b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs
@@ -12,9 +12,7 @@ use rustc_infer::traits::{ImplSource, Obligation, ObligationCause};
use rustc_middle::mir;
use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::subst::{GenericArgKind, SubstsRef};
-use rustc_middle::ty::{
- suggest_constraining_type_param, Adt, Closure, DefIdTree, FnDef, FnPtr, Param, Ty,
-};
+use rustc_middle::ty::{suggest_constraining_type_param, Adt, Closure, FnDef, FnPtr, Param, Ty};
use rustc_middle::ty::{Binder, TraitRef};
use rustc_session::parse::feature_err;
use rustc_span::symbol::sym;
@@ -706,7 +704,7 @@ pub mod ty {
fn importance(&self) -> DiagnosticImportance {
match self.0 {
- mir::LocalKind::Var | mir::LocalKind::Temp => DiagnosticImportance::Secondary,
+ mir::LocalKind::Temp => DiagnosticImportance::Secondary,
mir::LocalKind::ReturnPointer | mir::LocalKind::Arg => {
DiagnosticImportance::Primary
}
diff --git a/compiler/rustc_const_eval/src/transform/check_consts/post_drop_elaboration.rs b/compiler/rustc_const_eval/src/transform/check_consts/post_drop_elaboration.rs
index cf4e875c9..1f1640fd8 100644
--- a/compiler/rustc_const_eval/src/transform/check_consts/post_drop_elaboration.rs
+++ b/compiler/rustc_const_eval/src/transform/check_consts/post_drop_elaboration.rs
@@ -30,7 +30,7 @@ pub fn check_live_drops<'tcx>(tcx: TyCtxt<'tcx>, body: &mir::Body<'tcx>) {
return;
}
- if tcx.has_attr(def_id.to_def_id(), sym::rustc_do_not_const_check) {
+ if tcx.has_attr(def_id, sym::rustc_do_not_const_check) {
return;
}
@@ -80,8 +80,7 @@ impl<'tcx> Visitor<'tcx> for CheckLiveDrops<'_, 'tcx> {
trace!("visit_terminator: terminator={:?} location={:?}", terminator, location);
match &terminator.kind {
- mir::TerminatorKind::Drop { place: dropped_place, .. }
- | mir::TerminatorKind::DropAndReplace { place: dropped_place, .. } => {
+ mir::TerminatorKind::Drop { place: dropped_place, .. } => {
let dropped_ty = dropped_place.ty(self.body, self.tcx).ty;
if !NeedsNonConstDrop::in_any_value_of_ty(self.ccx, dropped_ty) {
// Instead of throwing a bug, we just return here. This is because we have to
@@ -105,7 +104,7 @@ impl<'tcx> Visitor<'tcx> for CheckLiveDrops<'_, 'tcx> {
}
}
- mir::TerminatorKind::Abort
+ mir::TerminatorKind::Terminate
| mir::TerminatorKind::Call { .. }
| mir::TerminatorKind::Assert { .. }
| mir::TerminatorKind::FalseEdge { .. }
diff --git a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs
index bb4b7ad50..6758cba2e 100644
--- a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs
+++ b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs
@@ -9,7 +9,7 @@ use rustc_middle::mir;
use rustc_middle::mir::*;
use rustc_middle::ty::{self, subst::SubstsRef, AdtDef, Ty};
use rustc_trait_selection::traits::{
- self, ImplSource, Obligation, ObligationCause, SelectionContext,
+ self, ImplSource, Obligation, ObligationCause, ObligationCtxt, SelectionContext,
};
use super::ConstCx;
@@ -184,7 +184,10 @@ impl Qualif for NeedsNonConstDrop {
}
// If we had any errors, then it's bad
- !traits::fully_solve_obligations(&infcx, impl_src.nested_obligations()).is_empty()
+ let ocx = ObligationCtxt::new(&infcx);
+ ocx.register_obligations(impl_src.nested_obligations());
+ let errors = ocx.select_all_or_error();
+ !errors.is_empty()
}
fn in_adt_inherently<'tcx>(
diff --git a/compiler/rustc_const_eval/src/transform/check_consts/resolver.rs b/compiler/rustc_const_eval/src/transform/check_consts/resolver.rs
index 805e6096b..78c74e189 100644
--- a/compiler/rustc_const_eval/src/transform/check_consts/resolver.rs
+++ b/compiler/rustc_const_eval/src/transform/check_consts/resolver.rs
@@ -222,23 +222,8 @@ where
// The effect of assignment to the return place in `TerminatorKind::Call` is not applied
// here; that occurs in `apply_call_return_effect`.
- if let mir::TerminatorKind::DropAndReplace { value, place, .. } = &terminator.kind {
- let qualif = qualifs::in_operand::<Q, _>(
- self.ccx,
- &mut |l| self.state.qualif.contains(l),
- value,
- );
-
- if !place.is_indirect() {
- self.assign_qualif_direct(place, qualif);
- }
- }
-
// We ignore borrow on drop because custom drop impls are not allowed in consts.
// FIXME: Reconsider if accounting for borrows in drops is necessary for const drop.
-
- // We need to assign qualifs to the dropped location before visiting the operand that
- // replaces it since qualifs can be cleared on move.
self.super_terminator(terminator, location);
}
}
diff --git a/compiler/rustc_const_eval/src/transform/promote_consts.rs b/compiler/rustc_const_eval/src/transform/promote_consts.rs
index 3f3b66b06..7919aed09 100644
--- a/compiler/rustc_const_eval/src/transform/promote_consts.rs
+++ b/compiler/rustc_const_eval/src/transform/promote_consts.rs
@@ -21,7 +21,7 @@ use rustc_middle::ty::subst::InternalSubsts;
use rustc_middle::ty::{self, List, TyCtxt, TypeVisitableExt};
use rustc_span::Span;
-use rustc_index::vec::{Idx, IndexVec};
+use rustc_index::vec::{Idx, IndexSlice, IndexVec};
use std::cell::Cell;
use std::{cmp, iter, mem};
@@ -106,8 +106,9 @@ impl<'tcx> Visitor<'tcx> for Collector<'_, 'tcx> {
debug!("visit_local: index={:?} context={:?} location={:?}", index, context, location);
// We're only interested in temporaries and the return place
match self.ccx.body.local_kind(index) {
- LocalKind::Temp | LocalKind::ReturnPointer => {}
- LocalKind::Arg | LocalKind::Var => return,
+ LocalKind::Arg => return,
+ LocalKind::Temp if self.ccx.body.local_decls[index].is_user_variable() => return,
+ LocalKind::ReturnPointer | LocalKind::Temp => {}
}
// Ignore drops, if the temp gets promoted,
@@ -183,7 +184,7 @@ pub fn collect_temps_and_candidates<'tcx>(
/// This wraps an `Item`, and has access to all fields of that `Item` via `Deref` coercion.
struct Validator<'a, 'tcx> {
ccx: &'a ConstCx<'a, 'tcx>,
- temps: &'a mut IndexVec<Local, TempState>,
+ temps: &'a mut IndexSlice<Local, TempState>,
}
impl<'a, 'tcx> std::ops::Deref for Validator<'a, 'tcx> {
@@ -668,7 +669,7 @@ impl<'tcx> Validator<'_, 'tcx> {
// FIXME(eddyb) remove the differences for promotability in `static`, `const`, `const fn`.
pub fn validate_candidates(
ccx: &ConstCx<'_, '_>,
- temps: &mut IndexVec<Local, TempState>,
+ temps: &mut IndexSlice<Local, TempState>,
candidates: &[Candidate],
) -> Vec<Candidate> {
let mut validator = Validator { ccx, temps };
@@ -706,7 +707,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
}
fn assign(&mut self, dest: Local, rvalue: Rvalue<'tcx>, span: Span) {
- let last = self.promoted.basic_blocks.last().unwrap();
+ let last = self.promoted.basic_blocks.last_index().unwrap();
let data = &mut self.promoted[last];
data.statements.push(Statement {
source_info: SourceInfo::outermost(span),
@@ -799,14 +800,14 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
self.visit_operand(arg, loc);
}
- let last = self.promoted.basic_blocks.last().unwrap();
+ let last = self.promoted.basic_blocks.last_index().unwrap();
let new_target = self.new_block();
*self.promoted[last].terminator_mut() = Terminator {
kind: TerminatorKind::Call {
func,
args,
- cleanup: None,
+ unwind: UnwindAction::Continue,
destination: Place::from(new_temp),
target: Some(new_target),
from_hir_call,
diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs
index fb37eb79a..d4bed9738 100644
--- a/compiler/rustc_const_eval/src/transform/validate.rs
+++ b/compiler/rustc_const_eval/src/transform/validate.rs
@@ -5,19 +5,18 @@ use rustc_index::bit_set::BitSet;
use rustc_index::vec::IndexVec;
use rustc_infer::traits::Reveal;
use rustc_middle::mir::interpret::Scalar;
-use rustc_middle::mir::visit::NonUseContext::VarDebugInfo;
-use rustc_middle::mir::visit::{PlaceContext, Visitor};
+use rustc_middle::mir::visit::{NonUseContext, PlaceContext, Visitor};
use rustc_middle::mir::{
traversal, BasicBlock, BinOp, Body, BorrowKind, CastKind, CopyNonOverlapping, Local, Location,
MirPass, MirPhase, NonDivergingIntrinsic, Operand, Place, PlaceElem, PlaceRef, ProjectionElem,
RetagKind, RuntimePhase, Rvalue, SourceScope, Statement, StatementKind, Terminator,
- TerminatorKind, UnOp, START_BLOCK,
+ TerminatorKind, UnOp, UnwindAction, VarDebugInfo, VarDebugInfoContents, START_BLOCK,
};
use rustc_middle::ty::{self, InstanceDef, ParamEnv, Ty, TyCtxt, TypeVisitableExt};
use rustc_mir_dataflow::impls::MaybeStorageLive;
use rustc_mir_dataflow::storage::always_storage_live_locals;
use rustc_mir_dataflow::{Analysis, ResultsCursor};
-use rustc_target::abi::{Size, VariantIdx};
+use rustc_target::abi::{Size, FIRST_VARIANT};
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
enum EdgeKind {
@@ -72,6 +71,17 @@ impl<'tcx> MirPass<'tcx> for Validator {
};
checker.visit_body(body);
checker.check_cleanup_control_flow();
+
+ if let MirPhase::Runtime(_) = body.phase {
+ if let ty::InstanceDef::Item(_) = body.source.instance {
+ if body.has_free_regions() {
+ checker.fail(
+ Location::START,
+ format!("Free regions in optimized {} MIR", body.phase.name()),
+ );
+ }
+ }
+ }
}
}
@@ -222,6 +232,24 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
}
}
+ fn check_unwind_edge(&mut self, location: Location, unwind: UnwindAction) {
+ let is_cleanup = self.body.basic_blocks[location.block].is_cleanup;
+ match unwind {
+ UnwindAction::Cleanup(unwind) => {
+ if is_cleanup {
+ self.fail(location, "unwind on cleanup block");
+ }
+ self.check_edge(location, unwind, EdgeKind::Unwind);
+ }
+ UnwindAction::Continue => {
+ if is_cleanup {
+ self.fail(location, "unwind on cleanup block");
+ }
+ }
+ UnwindAction::Unreachable | UnwindAction::Terminate => (),
+ }
+ }
+
/// Check if src can be assigned into dest.
/// This is not precise, it will accept some incorrect assignments.
fn mir_assign_valid_types(&self, src: Ty<'tcx>, dest: Ty<'tcx>) -> bool {
@@ -348,8 +376,8 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
check_equal(self, location, *f_ty);
}
ty::Adt(adt_def, substs) => {
- let var = parent_ty.variant_index.unwrap_or(VariantIdx::from_u32(0));
- let Some(field) = adt_def.variant(var).fields.get(f.as_usize()) else {
+ let var = parent_ty.variant_index.unwrap_or(FIRST_VARIANT);
+ let Some(field) = adt_def.variant(var).fields.get(f) else {
fail_out_of_bounds(self, location);
return;
};
@@ -408,13 +436,49 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
self.super_projection_elem(local, proj_base, elem, context, location);
}
+ fn visit_var_debug_info(&mut self, debuginfo: &VarDebugInfo<'tcx>) {
+ let check_place = |place: Place<'_>| {
+ if place.projection.iter().any(|p| !p.can_use_in_debuginfo()) {
+ self.fail(
+ START_BLOCK.start_location(),
+ format!("illegal place {:?} in debuginfo for {:?}", place, debuginfo.name),
+ );
+ }
+ };
+ match debuginfo.value {
+ VarDebugInfoContents::Const(_) => {}
+ VarDebugInfoContents::Place(place) => check_place(place),
+ VarDebugInfoContents::Composite { ty, ref fragments } => {
+ for f in fragments {
+ check_place(f.contents);
+ if ty.is_union() || ty.is_enum() {
+ self.fail(
+ START_BLOCK.start_location(),
+ format!("invalid type {:?} for composite debuginfo", ty),
+ );
+ }
+ if f.projection.iter().any(|p| !matches!(p, PlaceElem::Field(..))) {
+ self.fail(
+ START_BLOCK.start_location(),
+ format!(
+ "illegal projection {:?} in debuginfo for {:?}",
+ f.projection, debuginfo.name
+ ),
+ );
+ }
+ }
+ }
+ }
+ self.super_var_debug_info(debuginfo);
+ }
+
fn visit_place(&mut self, place: &Place<'tcx>, cntxt: PlaceContext, location: Location) {
// Set off any `bug!`s in the type computation code
let _ = place.ty(&self.body.local_decls, self.tcx);
if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial)
&& place.projection.len() > 1
- && cntxt != PlaceContext::NonUse(VarDebugInfo)
+ && cntxt != PlaceContext::NonUse(NonUseContext::VarDebugInfo)
&& place.projection[1..].contains(&ProjectionElem::Deref)
{
self.fail(location, format!("{:?}, has deref at the wrong place", place));
@@ -553,15 +617,6 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
);
}
}
- Shl | Shr => {
- for x in [a, b] {
- check_kinds!(
- x,
- "Cannot perform checked shift on non-integer type {:?}",
- ty::Uint(..) | ty::Int(..)
- )
- }
- }
_ => self.fail(location, format!("There is no checked version of {:?}", op)),
}
}
@@ -619,6 +674,41 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
);
}
}
+ CastKind::Transmute => {
+ if let MirPhase::Runtime(..) = self.mir_phase {
+ // Unlike `mem::transmute`, a MIR `Transmute` is well-formed
+ // for any two `Sized` types, just potentially UB to run.
+
+ if !self
+ .tcx
+ .normalize_erasing_regions(self.param_env, op_ty)
+ .is_sized(self.tcx, self.param_env)
+ {
+ self.fail(
+ location,
+ format!("Cannot transmute from non-`Sized` type {op_ty:?}"),
+ );
+ }
+ if !self
+ .tcx
+ .normalize_erasing_regions(self.param_env, *target_type)
+ .is_sized(self.tcx, self.param_env)
+ {
+ self.fail(
+ location,
+ format!("Cannot transmute to non-`Sized` type {target_type:?}"),
+ );
+ }
+ } else {
+ self.fail(
+ location,
+ format!(
+ "Transmute is not supported in non-runtime phase {:?}.",
+ self.mir_phase
+ ),
+ );
+ }
+ }
}
}
Rvalue::Repeat(_, _)
@@ -648,8 +738,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
);
}
if let Rvalue::CopyForDeref(place) = rvalue {
- if !place.ty(&self.body.local_decls, self.tcx).ty.builtin_deref(true).is_some()
- {
+ if place.ty(&self.body.local_decls, self.tcx).ty.builtin_deref(true).is_none() {
self.fail(
location,
"`CopyForDeref` should only be used for dereferenceable types",
@@ -668,6 +757,14 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
}
}
}
+ StatementKind::PlaceMention(..) => {
+ if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
+ self.fail(
+ location,
+ "`PlaceMention` should have been removed after drop lowering phase",
+ );
+ }
+ }
StatementKind::AscribeUserType(..) => {
if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
self.fail(
@@ -831,23 +928,9 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
}
TerminatorKind::Drop { target, unwind, .. } => {
self.check_edge(location, *target, EdgeKind::Normal);
- if let Some(unwind) = unwind {
- self.check_edge(location, *unwind, EdgeKind::Unwind);
- }
- }
- TerminatorKind::DropAndReplace { target, unwind, .. } => {
- if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
- self.fail(
- location,
- "`DropAndReplace` should have been removed during drop elaboration",
- );
- }
- self.check_edge(location, *target, EdgeKind::Normal);
- if let Some(unwind) = unwind {
- self.check_edge(location, *unwind, EdgeKind::Unwind);
- }
+ self.check_unwind_edge(location, *unwind);
}
- TerminatorKind::Call { func, args, destination, target, cleanup, .. } => {
+ TerminatorKind::Call { func, args, destination, target, unwind, .. } => {
let func_ty = func.ty(&self.body.local_decls, self.tcx);
match func_ty.kind() {
ty::FnPtr(..) | ty::FnDef(..) => {}
@@ -859,9 +942,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
if let Some(target) = target {
self.check_edge(location, *target, EdgeKind::Normal);
}
- if let Some(cleanup) = cleanup {
- self.check_edge(location, *cleanup, EdgeKind::Unwind);
- }
+ self.check_unwind_edge(location, *unwind);
// The call destination place and Operand::Move place used as an argument might be
// passed by a reference to the callee. Consequently they must be non-overlapping.
@@ -887,7 +968,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
);
}
}
- TerminatorKind::Assert { cond, target, cleanup, .. } => {
+ TerminatorKind::Assert { cond, target, unwind, .. } => {
let cond_ty = cond.ty(&self.body.local_decls, self.tcx);
if cond_ty != self.tcx.types.bool {
self.fail(
@@ -899,9 +980,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
);
}
self.check_edge(location, *target, EdgeKind::Normal);
- if let Some(cleanup) = cleanup {
- self.check_edge(location, *cleanup, EdgeKind::Unwind);
- }
+ self.check_unwind_edge(location, *unwind);
}
TerminatorKind::Yield { resume, drop, .. } => {
if self.body.generator.is_none() {
@@ -933,17 +1012,13 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
);
}
self.check_edge(location, *real_target, EdgeKind::Normal);
- if let Some(unwind) = unwind {
- self.check_edge(location, *unwind, EdgeKind::Unwind);
- }
+ self.check_unwind_edge(location, *unwind);
}
- TerminatorKind::InlineAsm { destination, cleanup, .. } => {
+ TerminatorKind::InlineAsm { destination, unwind, .. } => {
if let Some(destination) = destination {
self.check_edge(location, *destination, EdgeKind::Normal);
}
- if let Some(cleanup) = cleanup {
- self.check_edge(location, *cleanup, EdgeKind::Unwind);
- }
+ self.check_unwind_edge(location, *unwind);
}
TerminatorKind::GeneratorDrop => {
if self.body.generator.is_none() {
@@ -956,10 +1031,13 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
);
}
}
- TerminatorKind::Resume | TerminatorKind::Abort => {
+ TerminatorKind::Resume | TerminatorKind::Terminate => {
let bb = location.block;
if !self.body.basic_blocks[bb].is_cleanup {
- self.fail(location, "Cannot `Resume` or `Abort` from non-cleanup basic block")
+ self.fail(
+ location,
+ "Cannot `Resume` or `Terminate` from non-cleanup basic block",
+ )
}
}
TerminatorKind::Return => {