diff options
Diffstat (limited to 'compiler/rustc_const_eval')
23 files changed, 363 insertions, 265 deletions
diff --git a/compiler/rustc_const_eval/locales/en-US.ftl b/compiler/rustc_const_eval/messages.ftl index 33bb116d6..f6751df44 100644 --- a/compiler/rustc_const_eval/locales/en-US.ftl +++ b/compiler/rustc_const_eval/messages.ftl @@ -39,17 +39,25 @@ const_eval_unstable_const_fn = `{$def_path}` is not yet stable as a const fn const_eval_unallowed_mutable_refs = mutable references are not allowed in the final value of {$kind}s .teach_note = - References in statics and constants may only refer to immutable values.\n\n + References in statics and constants may only refer to immutable values. + + Statics are shared everywhere, and if they refer to mutable data one might violate memory - safety since holding multiple mutable references to shared data is not allowed.\n\n + safety since holding multiple mutable references to shared data is not allowed. + + If you really want global mutable state, try using static mut or a global UnsafeCell. const_eval_unallowed_mutable_refs_raw = raw mutable references are not allowed in the final value of {$kind}s .teach_note = - References in statics and constants may only refer to immutable values.\n\n + References in statics and constants may only refer to immutable values. + + Statics are shared everywhere, and if they refer to mutable data one might violate memory - safety since holding multiple mutable references to shared data is not allowed.\n\n + safety since holding multiple mutable references to shared data is not allowed. + + If you really want global mutable state, try using static mut or a global UnsafeCell. const_eval_non_const_fmt_macro_call = 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 => { |