diff options
Diffstat (limited to 'compiler/rustc_const_eval/src/const_eval/machine.rs')
| -rw-r--r-- | compiler/rustc_const_eval/src/const_eval/machine.rs | 106 |
1 files changed, 18 insertions, 88 deletions
diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index 5f0bc8539ee..b27e3606f38 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -1,21 +1,19 @@ use std::borrow::{Borrow, Cow}; use std::fmt; use std::hash::Hash; -use std::ops::ControlFlow; +use rustc_abi::{Align, ExternAbi, Size}; use rustc_ast::Mutability; use rustc_data_structures::fx::{FxHashMap, FxIndexMap, IndexEntry}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::{self as hir, CRATE_HIR_ID, LangItem}; use rustc_middle::mir::AssertMessage; use rustc_middle::query::TyCtxtAt; -use rustc_middle::ty::layout::{FnAbiOf, TyAndLayout}; +use rustc_middle::ty::layout::{HasTypingEnv, TyAndLayout}; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::{bug, mir}; use rustc_span::Span; use rustc_span::symbol::{Symbol, sym}; -use rustc_target::abi::{Align, Size}; -use rustc_target::spec::abi::Abi as CallAbi; use tracing::debug; use super::error::*; @@ -23,9 +21,9 @@ use crate::errors::{LongRunning, LongRunningWarn}; use crate::fluent_generated as fluent; use crate::interpret::{ self, AllocId, AllocRange, ConstAllocation, CtfeProvenance, FnArg, Frame, GlobalAlloc, ImmTy, - InterpCx, InterpResult, MPlaceTy, OpTy, Pointer, PointerArithmetic, RangeSet, Scalar, - StackPopCleanup, compile_time_machine, interp_ok, throw_exhaust, throw_inval, throw_ub, - throw_ub_custom, throw_unsup, throw_unsup_format, + InterpCx, InterpResult, MPlaceTy, OpTy, Pointer, RangeSet, Scalar, compile_time_machine, + interp_ok, throw_exhaust, throw_inval, throw_ub, throw_ub_custom, throw_unsup, + throw_unsup_format, }; /// When hitting this many interpreted terminators we emit a deny by default lint @@ -227,8 +225,8 @@ impl<'tcx> CompileTimeInterpCx<'tcx> { &mut self, instance: ty::Instance<'tcx>, args: &[FnArg<'tcx>], - dest: &MPlaceTy<'tcx>, - ret: Option<mir::BasicBlock>, + _dest: &MPlaceTy<'tcx>, + _ret: Option<mir::BasicBlock>, ) -> InterpResult<'tcx, Option<ty::Instance<'tcx>>> { let def_id = instance.def_id(); @@ -251,95 +249,27 @@ impl<'tcx> CompileTimeInterpCx<'tcx> { } else if self.tcx.is_lang_item(def_id, LangItem::PanicFmt) { // For panic_fmt, call const_panic_fmt instead. let const_def_id = self.tcx.require_lang_item(LangItem::ConstPanicFmt, None); + // FIXME(@lcnr): why does this use an empty env if we've got a `param_env` right here. let new_instance = ty::Instance::expect_resolve( *self.tcx, - ty::ParamEnv::reveal_all(), + ty::TypingEnv::fully_monomorphized(), const_def_id, instance.args, self.cur_span(), ); return interp_ok(Some(new_instance)); - } else if self.tcx.is_lang_item(def_id, LangItem::AlignOffset) { - let args = self.copy_fn_args(args); - // For align_offset, we replace the function call if the pointer has no address. - match self.align_offset(instance, &args, dest, ret)? { - ControlFlow::Continue(()) => return interp_ok(Some(instance)), - ControlFlow::Break(()) => return interp_ok(None), - } } interp_ok(Some(instance)) } - /// `align_offset(ptr, target_align)` needs special handling in const eval, because the pointer - /// may not have an address. - /// - /// If `ptr` does have a known address, then we return `Continue(())` and the function call should - /// proceed as normal. - /// - /// If `ptr` doesn't have an address, but its underlying allocation's alignment is at most - /// `target_align`, then we call the function again with an dummy address relative to the - /// allocation. - /// - /// If `ptr` doesn't have an address and `target_align` is stricter than the underlying - /// allocation's alignment, then we return `usize::MAX` immediately. - fn align_offset( - &mut self, - instance: ty::Instance<'tcx>, - args: &[OpTy<'tcx>], - dest: &MPlaceTy<'tcx>, - ret: Option<mir::BasicBlock>, - ) -> InterpResult<'tcx, ControlFlow<()>> { - assert_eq!(args.len(), 2); - - let ptr = self.read_pointer(&args[0])?; - let target_align = self.read_scalar(&args[1])?.to_target_usize(self)?; - - if !target_align.is_power_of_two() { - throw_ub_custom!( - fluent::const_eval_align_offset_invalid_align, - target_align = target_align, - ); - } - - match self.ptr_try_get_alloc_id(ptr, 0) { - Ok((alloc_id, offset, _extra)) => { - let (_size, alloc_align, _kind) = self.get_alloc_info(alloc_id); - - if target_align <= alloc_align.bytes() { - // Extract the address relative to the allocation base that is definitely - // sufficiently aligned and call `align_offset` again. - let addr = ImmTy::from_uint(offset.bytes(), args[0].layout).into(); - let align = ImmTy::from_uint(target_align, args[1].layout).into(); - let fn_abi = self.fn_abi_of_instance(instance, ty::List::empty())?; - - // Push the stack frame with our own adjusted arguments. - self.init_stack_frame( - instance, - self.load_mir(instance.def, None)?, - fn_abi, - &[FnArg::Copy(addr), FnArg::Copy(align)], - /* with_caller_location = */ false, - dest, - StackPopCleanup::Goto { ret, unwind: mir::UnwindAction::Unreachable }, - )?; - interp_ok(ControlFlow::Break(())) - } else { - // Not alignable in const, return `usize::MAX`. - let usize_max = Scalar::from_target_usize(self.target_usize_max(), self); - self.write_scalar(usize_max, dest)?; - self.return_to_block(ret)?; - interp_ok(ControlFlow::Break(())) - } - } - Err(_addr) => { - // The pointer has an address, continue with function call. - interp_ok(ControlFlow::Continue(())) - } - } - } - /// See documentation on the `ptr_guaranteed_cmp` intrinsic. + /// Returns `2` if the result is unknown. + /// Returns `1` if the pointers are guaranteed equal. + /// Returns `0` if the pointers are guaranteed inequal. + /// + /// Note that this intrinsic is exposed on stable for comparison with null. In other words, any + /// change to this function that affects comparison with null is insta-stable! fn guaranteed_cmp(&mut self, a: Scalar, b: Scalar) -> InterpResult<'tcx, u8> { interp_ok(match (a, b) { // Comparisons between integers are always known. @@ -411,7 +341,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { fn find_mir_or_eval_fn( ecx: &mut InterpCx<'tcx, Self>, orig_instance: ty::Instance<'tcx>, - _abi: CallAbi, + _abi: ExternAbi, args: &[FnArg<'tcx>], dest: &MPlaceTy<'tcx>, ret: Option<mir::BasicBlock>, @@ -431,7 +361,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { // sensitive check here. But we can at least rule out functions that are not const at // all. That said, we have to allow calling functions inside a trait marked with // #[const_trait]. These *are* const-checked! - // FIXME(effects): why does `is_const_fn` not classify them as const? + // FIXME(const_trait_impl): why does `is_const_fn` not classify them as const? if (!ecx.tcx.is_const_fn(def) && !ecx.tcx.is_const_default_method(def)) || ecx.tcx.has_attr(def, sym::rustc_do_not_const_check) { @@ -737,7 +667,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { .is_some_and(|p| !p.immutable()) { // That next check is expensive, that's why we have all the guards above. - let is_immutable = ty.is_freeze(*ecx.tcx, ecx.param_env); + let is_immutable = ty.is_freeze(*ecx.tcx, ecx.typing_env()); let place = ecx.ref_to_mplace(val)?; let new_place = if is_immutable { place.map_provenance(CtfeProvenance::as_immutable) |
