diff options
Diffstat (limited to '')
-rw-r--r-- | compiler/rustc_const_eval/src/const_eval/error.rs | 86 | ||||
-rw-r--r-- | compiler/rustc_const_eval/src/const_eval/eval_queries.rs | 92 | ||||
-rw-r--r-- | compiler/rustc_const_eval/src/const_eval/machine.rs | 133 | ||||
-rw-r--r-- | compiler/rustc_const_eval/src/const_eval/mod.rs | 29 | ||||
-rw-r--r-- | compiler/rustc_const_eval/src/const_eval/valtrees.rs | 39 |
5 files changed, 147 insertions, 232 deletions
diff --git a/compiler/rustc_const_eval/src/const_eval/error.rs b/compiler/rustc_const_eval/src/const_eval/error.rs index 322bfd5ce..4977a5d6b 100644 --- a/compiler/rustc_const_eval/src/const_eval/error.rs +++ b/compiler/rustc_const_eval/src/const_eval/error.rs @@ -2,20 +2,19 @@ use std::error::Error; use std::fmt; use rustc_errors::Diagnostic; -use rustc_hir as hir; use rustc_middle::mir::AssertKind; use rustc_middle::ty::{layout::LayoutError, query::TyCtxtAt, ConstInt}; use rustc_span::{Span, Symbol}; use super::InterpCx; use crate::interpret::{ - struct_error, ErrorHandled, FrameInfo, InterpError, InterpErrorInfo, Machine, MachineStopType, UnsupportedOpInfo, + struct_error, ErrorHandled, FrameInfo, InterpError, InterpErrorInfo, Machine, MachineStopType, + UnsupportedOpInfo, }; /// The CTFE machine has some custom error kinds. #[derive(Clone, Debug)] pub enum ConstEvalErrKind { - NeedsRfc(String), ConstAccessesStatic, ModifiedGlobal, AssertFailure(AssertKind<ConstInt>), @@ -23,11 +22,7 @@ pub enum ConstEvalErrKind { Abort(String), } -impl MachineStopType for ConstEvalErrKind { - fn is_hard_err(&self) -> bool { - matches!(self, Self::Panic { .. }) - } -} +impl MachineStopType for ConstEvalErrKind {} // The errors become `MachineStop` with plain strings when being raised. // `ConstEvalErr` (in `librustc_middle/mir/interpret/error.rs`) knows to @@ -42,9 +37,6 @@ impl fmt::Display for ConstEvalErrKind { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { use self::ConstEvalErrKind::*; match *self { - NeedsRfc(ref msg) => { - write!(f, "\"{}\" needs an rfc before being allowed inside constants", msg) - } ConstAccessesStatic => write!(f, "constant accesses static"), ModifiedGlobal => { write!(f, "modifying a static's initial value from another static's initializer") @@ -72,7 +64,7 @@ pub struct ConstEvalErr<'tcx> { impl<'tcx> ConstEvalErr<'tcx> { /// Turn an interpreter error into something to report to the user. /// As a side-effect, if RUSTC_CTFE_BACKTRACE is set, this prints the backtrace. - /// Should be called only if the error is actually going to to be reported! + /// Should be called only if the error is actually going to be reported! pub fn new<'mir, M: Machine<'mir, 'tcx>>( ecx: &InterpCx<'mir, 'tcx, M>, error: InterpErrorInfo<'tcx>, @@ -90,48 +82,10 @@ impl<'tcx> ConstEvalErr<'tcx> { ConstEvalErr { error: error.into_kind(), stacktrace, span } } - pub fn struct_error( - &self, - tcx: TyCtxtAt<'tcx>, - message: &str, - decorate: impl FnOnce(&mut Diagnostic), - ) -> ErrorHandled { - self.struct_generic(tcx, message, decorate, None) - } - pub fn report_as_error(&self, tcx: TyCtxtAt<'tcx>, message: &str) -> ErrorHandled { self.struct_error(tcx, message, |_| {}) } - pub fn report_as_lint( - &self, - tcx: TyCtxtAt<'tcx>, - message: &str, - lint_root: hir::HirId, - span: Option<Span>, - ) -> ErrorHandled { - self.struct_generic( - tcx, - message, - |lint: &mut Diagnostic| { - // Apply the span. - if let Some(span) = span { - let primary_spans = lint.span.primary_spans().to_vec(); - // point at the actual error as the primary span - lint.replace_span_with(span); - // point to the `const` statement as a secondary span - // they don't have any label - for sp in primary_spans { - if sp != span { - lint.span_label(sp, ""); - } - } - } - }, - Some(lint_root), - ) - } - /// Create a diagnostic for this const eval error. /// /// Sets the message passed in via `message` and adds span labels with detailed error @@ -140,13 +94,12 @@ impl<'tcx> ConstEvalErr<'tcx> { /// /// If `lint_root.is_some()` report it as a lint, else report it as a hard error. /// (Except that for some errors, we ignore all that -- see `must_error` below.) - #[instrument(skip(self, tcx, decorate, lint_root), level = "debug")] - fn struct_generic( + #[instrument(skip(self, tcx, decorate), level = "debug")] + pub fn struct_error( &self, tcx: TyCtxtAt<'tcx>, message: &str, decorate: impl FnOnce(&mut Diagnostic), - lint_root: Option<hir::HirId>, ) -> ErrorHandled { let finish = |err: &mut Diagnostic, span_msg: Option<String>| { trace!("reporting const eval failure at {:?}", self.span); @@ -158,6 +111,7 @@ impl<'tcx> ConstEvalErr<'tcx> { InterpError::Unsupported( UnsupportedOpInfo::ReadPointerAsBytes | UnsupportedOpInfo::PartialPointerOverwrite(_) + | UnsupportedOpInfo::PartialPointerCopy(_), ) => { err.help("this code performed an operation that depends on the underlying bytes representing a pointer"); err.help("the absolute address of a pointer is not known at compile-time, so such operations are not supported"); @@ -226,27 +180,9 @@ impl<'tcx> ConstEvalErr<'tcx> { let err_msg = self.error.to_string(); - // Regular case - emit a lint. - if let Some(lint_root) = lint_root { - // Report as lint. - let hir_id = - self.stacktrace.iter().rev().find_map(|frame| frame.lint_root).unwrap_or(lint_root); - tcx.struct_span_lint_hir( - rustc_session::lint::builtin::CONST_ERR, - hir_id, - tcx.span, - |lint| { - let mut lint = lint.build(message); - finish(&mut lint, Some(err_msg)); - lint.emit(); - }, - ); - ErrorHandled::Linted - } else { - // Report as hard error. - let mut err = struct_error(tcx, message); - finish(&mut err, Some(err_msg)); - ErrorHandled::Reported(err.emit()) - } + // Report as hard error. + let mut err = struct_error(tcx, message); + finish(&mut err, Some(err_msg)); + ErrorHandled::Reported(err.emit()) } } 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 975fb4b22..1b1052fdf 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -2,8 +2,8 @@ use super::{CompileTimeEvalContext, CompileTimeInterpreter, ConstEvalErr}; use crate::interpret::eval_nullary_intrinsic; use crate::interpret::{ intern_const_alloc_recursive, Allocation, ConstAlloc, ConstValue, CtfeValidationMode, GlobalId, - Immediate, InternKind, InterpCx, InterpResult, MPlaceTy, MemoryKind, OpTy, RefTracking, - ScalarMaybeUninit, StackPopCleanup, InterpError, + Immediate, InternKind, InterpCx, InterpError, InterpResult, MPlaceTy, MemoryKind, OpTy, + RefTracking, StackPopCleanup, }; use rustc_hir::def::DefKind; @@ -13,7 +13,7 @@ use rustc_middle::mir::pretty::display_allocation; use rustc_middle::traits::Reveal; use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::print::with_no_trimmed_paths; -use rustc_middle::ty::{self, subst::Subst, TyCtxt}; +use rustc_middle::ty::{self, TyCtxt}; use rustc_span::source_map::Span; use rustc_target::abi::{self, Abi}; use std::borrow::Cow; @@ -74,14 +74,16 @@ fn eval_body_using_ecx<'mir, 'tcx>( None => InternKind::Constant, } }; + ecx.machine.check_alignment = false; // interning doesn't need to respect alignment intern_const_alloc_recursive(ecx, intern_kind, &ret)?; + // we leave alignment checks off, since this `ecx` will not be used for further evaluation anyway debug!("eval_body_using_ecx done: {:?}", *ret); Ok(ret) } /// The `InterpCx` is only meant to be used to do field and index projections into constants for -/// `simd_shuffle` and const patterns in match arms. +/// `simd_shuffle` and const patterns in match arms. It never performs alignment checks. /// /// The function containing the `match` that is currently being analyzed may have generic bounds /// that inform us about the generic bounds of the constant. E.g., using an associated constant @@ -98,7 +100,11 @@ pub(super) fn mk_eval_cx<'mir, 'tcx>( tcx, root_span, param_env, - CompileTimeInterpreter::new(tcx.const_eval_limit(), can_access_statics), + CompileTimeInterpreter::new( + tcx.const_eval_limit(), + can_access_statics, + /*check_alignment:*/ false, + ), ) } @@ -166,10 +172,7 @@ pub(super) fn op_to_const<'tcx>( // see comment on `let try_as_immediate` above Err(imm) => match *imm { _ if imm.layout.is_zst() => ConstValue::ZeroSized, - Immediate::Scalar(x) => match x { - ScalarMaybeUninit::Scalar(s) => ConstValue::Scalar(s), - ScalarMaybeUninit::Uninit => to_const_value(&op.assert_mem_place()), - }, + Immediate::Scalar(x) => ConstValue::Scalar(x), Immediate::ScalarPair(a, b) => { debug!("ScalarPair(a: {:?}, b: {:?})", a, b); // We know `offset` is relative to the allocation, so we can use `into_parts`. @@ -194,7 +197,7 @@ pub(super) fn op_to_const<'tcx>( } } -#[instrument(skip(tcx), level = "debug")] +#[instrument(skip(tcx), level = "debug", ret)] pub(crate) fn turn_into_const_value<'tcx>( tcx: TyCtxt<'tcx>, constant: ConstAlloc<'tcx>, @@ -203,7 +206,13 @@ 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); - let ecx = mk_eval_cx(tcx, tcx.def_span(key.value.instance.def_id()), key.param_env, is_static); + // This is just accessing an already computed constant, so no need to check alginment here. + let ecx = mk_eval_cx( + tcx, + tcx.def_span(key.value.instance.def_id()), + key.param_env, + /*can_access_statics:*/ is_static, + ); let mplace = ecx.raw_const_to_mplace(constant).expect( "can only fail if layout computation failed, \ @@ -215,10 +224,7 @@ pub(crate) fn turn_into_const_value<'tcx>( ); // Turn this into a proper constant. - let const_val = op_to_const(&ecx, &mplace.into()); - debug!(?const_val); - - const_val + op_to_const(&ecx, &mplace.into()) } #[instrument(skip(tcx), level = "debug")] @@ -300,52 +306,34 @@ pub fn eval_to_allocation_raw_provider<'tcx>( key.param_env, // Statics (and promoteds inside statics) may access other statics, because unlike consts // they do not have to behave "as if" they were evaluated at runtime. - CompileTimeInterpreter::new(tcx.const_eval_limit(), /*can_access_statics:*/ is_static), + CompileTimeInterpreter::new( + tcx.const_eval_limit(), + /*can_access_statics:*/ is_static, + /*check_alignment:*/ tcx.sess.opts.unstable_opts.extra_const_ub_checks, + ), ); let res = ecx.load_mir(cid.instance.def, cid.promoted); match res.and_then(|body| eval_body_using_ecx(&mut ecx, cid, &body)) { Err(error) => { let err = ConstEvalErr::new(&ecx, error, None); - // Some CTFE errors raise just a lint, not a hard error; see - // <https://github.com/rust-lang/rust/issues/71800>. - let is_hard_err = if let Some(def) = def.as_local() { - // (Associated) consts only emit a lint, since they might be unused. - !matches!(tcx.def_kind(def.did.to_def_id()), DefKind::Const | DefKind::AssocConst) - // check if the inner InterpError is hard - || err.error.is_hard_err() + let msg = if is_static { + Cow::from("could not evaluate static initializer") } else { - // use of broken constant from other crate: always an error - true - }; - - if is_hard_err { - let msg = if is_static { - Cow::from("could not evaluate static initializer") + // If the current item has generics, we'd like to enrich the message with the + // instance and its substs: to show the actual compile-time values, in addition to + // the expression, leading to the const eval error. + let instance = &key.value.instance; + if !instance.substs.is_empty() { + let instance = with_no_trimmed_paths!(instance.to_string()); + let msg = format!("evaluation of `{}` failed", instance); + Cow::from(msg) } else { - // If the current item has generics, we'd like to enrich the message with the - // instance and its substs: to show the actual compile-time values, in addition to - // the expression, leading to the const eval error. - let instance = &key.value.instance; - if !instance.substs.is_empty() { - let instance = with_no_trimmed_paths!(instance.to_string()); - let msg = format!("evaluation of `{}` failed", instance); - Cow::from(msg) - } else { - Cow::from("evaluation of constant value failed") - } - }; + Cow::from("evaluation of constant value failed") + } + }; - Err(err.report_as_error(ecx.tcx.at(err.span), &msg)) - } else { - let hir_id = tcx.hir().local_def_id_to_hir_id(def.as_local().unwrap().did); - Err(err.report_as_lint( - tcx.at(tcx.def_span(def.did)), - "any use of this value will cause an error", - hir_id, - Some(err.span), - )) - } + Err(err.report_as_error(ecx.tcx.at(err.span), &msg)) } Ok(mplace) => { // Since evaluation had no errors, validate the resulting constant. diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index fc2e6652a..35d58d2f6 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -2,10 +2,10 @@ use rustc_hir::def::DefKind; use rustc_middle::mir; use rustc_middle::ty::{self, Ty, TyCtxt}; use std::borrow::Borrow; -use std::collections::hash_map::Entry; use std::hash::Hash; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::FxIndexMap; +use rustc_data_structures::fx::IndexEntry; use std::fmt; use rustc_ast::Mutability; @@ -35,21 +35,7 @@ impl<'mir, 'tcx> InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>> { // All `#[rustc_do_not_const_check]` functions should be hooked here. let def_id = instance.def_id(); - if Some(def_id) == self.tcx.lang_items().const_eval_select() { - // redirect to const_eval_select_ct - if let Some(const_eval_select) = self.tcx.lang_items().const_eval_select_ct() { - return Ok(Some( - ty::Instance::resolve( - *self.tcx, - ty::ParamEnv::reveal_all(), - const_eval_select, - instance.substs, - ) - .unwrap() - .unwrap(), - )); - } - } else if Some(def_id) == self.tcx.lang_items().panic_display() + if Some(def_id) == self.tcx.lang_items().panic_display() || Some(def_id) == self.tcx.lang_items().begin_panic_fn() { // &str or &&str @@ -89,10 +75,10 @@ pub struct CompileTimeInterpreter<'mir, 'tcx> { /// exhaustion error. /// /// Setting this to `0` disables the limit and allows the interpreter to run forever. - pub steps_remaining: usize, + pub(super) steps_remaining: usize, /// The virtual call stack. - pub(crate) stack: Vec<Frame<'mir, 'tcx, AllocId, ()>>, + pub(super) stack: Vec<Frame<'mir, 'tcx, AllocId, ()>>, /// We need to make sure consts never point to anything mutable, even recursively. That is /// relied on for pattern matching on consts with references. @@ -101,30 +87,38 @@ pub struct CompileTimeInterpreter<'mir, 'tcx> { /// * Pointers to allocations inside of statics can never leak outside, to a non-static global. /// This boolean here controls the second part. pub(super) can_access_statics: bool, + + /// Whether to check alignment during evaluation. + pub(super) check_alignment: bool, } impl<'mir, 'tcx> CompileTimeInterpreter<'mir, 'tcx> { - pub(crate) fn new(const_eval_limit: Limit, can_access_statics: bool) -> Self { + pub(crate) fn new( + const_eval_limit: Limit, + can_access_statics: bool, + check_alignment: bool, + ) -> Self { CompileTimeInterpreter { steps_remaining: const_eval_limit.0, stack: Vec::new(), can_access_statics, + check_alignment, } } } -impl<K: Hash + Eq, V> interpret::AllocMap<K, V> for FxHashMap<K, V> { +impl<K: Hash + Eq, V> interpret::AllocMap<K, V> for FxIndexMap<K, V> { #[inline(always)] fn contains_key<Q: ?Sized + Hash + Eq>(&mut self, k: &Q) -> bool where K: Borrow<Q>, { - FxHashMap::contains_key(self, k) + FxIndexMap::contains_key(self, k) } #[inline(always)] fn insert(&mut self, k: K, v: V) -> Option<V> { - FxHashMap::insert(self, k, v) + FxIndexMap::insert(self, k, v) } #[inline(always)] @@ -132,7 +126,7 @@ impl<K: Hash + Eq, V> interpret::AllocMap<K, V> for FxHashMap<K, V> { where K: Borrow<Q>, { - FxHashMap::remove(self, k) + FxIndexMap::remove(self, k) } #[inline(always)] @@ -154,8 +148,8 @@ impl<K: Hash + Eq, V> interpret::AllocMap<K, V> for FxHashMap<K, V> { #[inline(always)] fn get_mut_or<E>(&mut self, k: K, vacant: impl FnOnce() -> Result<V, E>) -> Result<&mut V, E> { match self.entry(k) { - Entry::Occupied(e) => Ok(e.into_mut()), - Entry::Vacant(e) => { + IndexEntry::Occupied(e) => Ok(e.into_mut()), + IndexEntry::Vacant(e) => { let v = vacant()?; Ok(e.insert(v)) } @@ -197,34 +191,35 @@ impl interpret::MayLeak for ! { } impl<'mir, 'tcx: 'mir> CompileTimeEvalContext<'mir, 'tcx> { - fn guaranteed_eq(&mut self, a: Scalar, b: Scalar) -> InterpResult<'tcx, bool> { + /// See documentation on the `ptr_guaranteed_cmp` intrinsic. + fn guaranteed_cmp(&mut self, a: Scalar, b: Scalar) -> InterpResult<'tcx, u8> { Ok(match (a, b) { // Comparisons between integers are always known. - (Scalar::Int { .. }, Scalar::Int { .. }) => a == b, - // Equality with integers can never be known for sure. - (Scalar::Int { .. }, Scalar::Ptr(..)) | (Scalar::Ptr(..), Scalar::Int { .. }) => false, - // FIXME: return `true` for when both sides are the same pointer, *except* that - // some things (like functions and vtables) do not have stable addresses - // so we need to be careful around them (see e.g. #73722). - (Scalar::Ptr(..), Scalar::Ptr(..)) => false, - }) - } - - fn guaranteed_ne(&mut self, a: Scalar, b: Scalar) -> InterpResult<'tcx, bool> { - Ok(match (a, b) { - // Comparisons between integers are always known. - (Scalar::Int(_), Scalar::Int(_)) => a != b, + (Scalar::Int { .. }, Scalar::Int { .. }) => { + if a == b { + 1 + } else { + 0 + } + } // Comparisons of abstract pointers with null pointers are known if the pointer // is in bounds, because if they are in bounds, the pointer can't be null. // Inequality with integers other than null can never be known for sure. (Scalar::Int(int), ptr @ Scalar::Ptr(..)) - | (ptr @ Scalar::Ptr(..), Scalar::Int(int)) => { - int.is_null() && !self.scalar_may_be_null(ptr)? + | (ptr @ Scalar::Ptr(..), Scalar::Int(int)) + if int.is_null() && !self.scalar_may_be_null(ptr)? => + { + 0 } - // FIXME: return `true` for at least some comparisons where we can reliably + // Equality with integers can never be known for sure. + (Scalar::Int { .. }, Scalar::Ptr(..)) | (Scalar::Ptr(..), Scalar::Int { .. }) => 2, + // FIXME: return a `1` for when both sides are the same pointer, *except* that + // some things (like functions and vtables) do not have stable addresses + // so we need to be careful around them (see e.g. #73722). + // FIXME: return `0` for at least some comparisons where we can reliably // determine the result of runtime inequality tests at compile-time. // Examples include comparison of addresses in different static items. - (Scalar::Ptr(..), Scalar::Ptr(..)) => false, + (Scalar::Ptr(..), Scalar::Ptr(..)) => 2, }) } } @@ -236,6 +231,16 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, const PANIC_ON_ALLOC_FAIL: bool = false; // will be raised as a proper error + #[inline(always)] + fn enforce_alignment(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool { + ecx.machine.check_alignment + } + + #[inline(always)] + fn enforce_validity(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool { + ecx.tcx.sess.opts.unstable_opts.extra_const_ub_checks + } + fn load_mir( ecx: &InterpCx<'mir, 'tcx, Self>, instance: ty::InstanceDef<'tcx>, @@ -251,9 +256,10 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, ); throw_inval!(AlreadyReported(guar)); } else { + // `find_mir_or_eval_fn` checks that this is a const fn before even calling us, + // so this should be unreachable. let path = ecx.tcx.def_path_str(def.did); - Err(ConstEvalErrKind::NeedsRfc(format!("calling extern function `{}`", path)) - .into()) + bug!("trying to call extern function `{path}` at compile-time"); } } _ => Ok(ecx.tcx.instance_mir(instance)), @@ -321,22 +327,14 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, // CTFE-specific intrinsics. let Some(ret) = target else { - return Err(ConstEvalErrKind::NeedsRfc(format!( - "calling intrinsic `{}`", - intrinsic_name - )) - .into()); + throw_unsup_format!("intrinsic `{intrinsic_name}` is not supported at compile-time"); }; match intrinsic_name { - sym::ptr_guaranteed_eq | sym::ptr_guaranteed_ne => { - let a = ecx.read_immediate(&args[0])?.to_scalar()?; - let b = ecx.read_immediate(&args[1])?.to_scalar()?; - let cmp = if intrinsic_name == sym::ptr_guaranteed_eq { - ecx.guaranteed_eq(a, b)? - } else { - ecx.guaranteed_ne(a, b)? - }; - ecx.write_scalar(Scalar::from_bool(cmp), dest)?; + sym::ptr_guaranteed_cmp => { + let a = ecx.read_scalar(&args[0])?; + let b = ecx.read_scalar(&args[1])?; + let cmp = ecx.guaranteed_cmp(a, b)?; + ecx.write_scalar(Scalar::from_u8(cmp), dest)?; } sym::const_allocate => { let size = ecx.read_scalar(&args[0])?.to_machine_usize(ecx)?; @@ -382,11 +380,9 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, } } _ => { - return Err(ConstEvalErrKind::NeedsRfc(format!( - "calling intrinsic `{}`", - intrinsic_name - )) - .into()); + throw_unsup_format!( + "intrinsic `{intrinsic_name}` is not supported at compile-time" + ); } } @@ -429,7 +425,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, _left: &ImmTy<'tcx>, _right: &ImmTy<'tcx>, ) -> InterpResult<'tcx, (Scalar, bool, Ty<'tcx>)> { - Err(ConstEvalErrKind::NeedsRfc("pointer arithmetic or comparison".to_string()).into()) + throw_unsup_format!("pointer arithmetic or comparison is not supported at compile-time"); } fn before_terminator(ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> { @@ -451,7 +447,8 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, _ecx: &mut InterpCx<'mir, 'tcx, Self>, _ptr: Pointer<AllocId>, ) -> InterpResult<'tcx> { - Err(ConstEvalErrKind::NeedsRfc("exposing pointers".to_string()).into()) + // This is only reachable with -Zunleash-the-miri-inside-of-you. + throw_unsup_format!("exposing pointers is not possible at compile-time") } #[inline(always)] diff --git a/compiler/rustc_const_eval/src/const_eval/mod.rs b/compiler/rustc_const_eval/src/const_eval/mod.rs index 948c33494..1c33e7845 100644 --- a/compiler/rustc_const_eval/src/const_eval/mod.rs +++ b/compiler/rustc_const_eval/src/const_eval/mod.rs @@ -1,16 +1,16 @@ // Not in interpret to make sure we do not use private implementation details +use crate::errors::MaxNumNodesInConstErr; +use crate::interpret::{ + intern_const_alloc_recursive, ConstValue, InternKind, InterpCx, InterpResult, MemPlaceMeta, + Scalar, +}; use rustc_hir::Mutability; use rustc_middle::mir; use rustc_middle::mir::interpret::{EvalToValTreeResult, GlobalId}; use rustc_middle::ty::{self, TyCtxt}; use rustc_span::{source_map::DUMMY_SP, symbol::Symbol}; -use crate::interpret::{ - intern_const_alloc_recursive, ConstValue, InternKind, InterpCx, InterpResult, MemPlaceMeta, - Scalar, -}; - mod error; mod eval_queries; mod fn_queries; @@ -72,12 +72,17 @@ pub(crate) fn eval_to_valtree<'tcx>( Ok(valtree) => Ok(Some(valtree)), Err(err) => { let did = cid.instance.def_id(); - let s = cid.display(tcx); + let global_const_id = cid.display(tcx); match err { ValTreeCreationError::NodesOverflow => { - let msg = format!("maximum number of nodes exceeded in constant {}", &s); + let msg = format!( + "maximum number of nodes exceeded in constant {}", + &global_const_id + ); let mut diag = match tcx.hir().span_if_local(did) { - Some(span) => tcx.sess.struct_span_err(span, &msg), + Some(span) => { + tcx.sess.create_err(MaxNumNodesInConstErr { span, global_const_id }) + } None => tcx.sess.struct_err(&msg), }; diag.emit(); @@ -95,10 +100,10 @@ pub(crate) fn try_destructure_mir_constant<'tcx>( tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, val: mir::ConstantKind<'tcx>, -) -> InterpResult<'tcx, mir::DestructuredMirConstant<'tcx>> { +) -> InterpResult<'tcx, mir::DestructuredConstant<'tcx>> { trace!("destructure_mir_constant: {:?}", val); let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false); - let op = ecx.mir_const_to_op(&val, None)?; + let op = ecx.const_to_op(&val, None)?; // We go to `usize` as we cannot allocate anything bigger anyway. let (field_count, variant, down) = match val.ty().kind() { @@ -124,7 +129,7 @@ pub(crate) fn try_destructure_mir_constant<'tcx>( .collect::<InterpResult<'tcx, Vec<_>>>()?; let fields = tcx.arena.alloc_from_iter(fields_iter); - Ok(mir::DestructuredMirConstant { variant, fields }) + Ok(mir::DestructuredConstant { variant, fields }) } #[instrument(skip(tcx), level = "debug")] @@ -134,7 +139,7 @@ pub(crate) fn deref_mir_constant<'tcx>( val: mir::ConstantKind<'tcx>, ) -> mir::ConstantKind<'tcx> { let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false); - let op = ecx.mir_const_to_op(&val, None).unwrap(); + let op = ecx.const_to_op(&val, None).unwrap(); let mplace = ecx.deref_operand(&op).unwrap(); if let Some(alloc_id) = mplace.ptr.provenance { assert_eq!( diff --git a/compiler/rustc_const_eval/src/const_eval/valtrees.rs b/compiler/rustc_const_eval/src/const_eval/valtrees.rs index 8fff4571d..f4da11883 100644 --- a/compiler/rustc_const_eval/src/const_eval/valtrees.rs +++ b/compiler/rustc_const_eval/src/const_eval/valtrees.rs @@ -3,7 +3,7 @@ use super::machine::CompileTimeEvalContext; use super::{ValTreeCreationError, ValTreeCreationResult, VALTREE_MAX_NODES}; use crate::interpret::{ intern_const_alloc_recursive, ConstValue, ImmTy, Immediate, InternKind, MemPlaceMeta, - MemoryKind, PlaceTy, Scalar, ScalarMaybeUninit, + MemoryKind, PlaceTy, Scalar, }; use crate::interpret::{MPlaceTy, Value}; use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt}; @@ -90,14 +90,14 @@ pub(crate) fn const_to_valtree_inner<'tcx>( let Ok(val) = ecx.read_immediate(&place.into()) else { return Err(ValTreeCreationError::Other); }; - let val = val.to_scalar().unwrap(); + let val = val.to_scalar(); *num_nodes += 1; Ok(ty::ValTree::Leaf(val.assert_int())) } // Raw pointers are not allowed in type level constants, as we cannot properly test them for - // equality at compile-time (see `ptr_guaranteed_eq`/`_ne`). + // equality at compile-time (see `ptr_guaranteed_cmp`). // Technically we could allow function pointers (represented as `ty::Instance`), but this is not guaranteed to // agree with runtime equality tests. ty::FnPtr(_) | ty::RawPtr(_) => Err(ValTreeCreationError::NonSupportedType), @@ -204,7 +204,7 @@ fn get_info_on_unsized_field<'tcx>( (unsized_inner_ty, num_elems) } -#[instrument(skip(ecx), level = "debug")] +#[instrument(skip(ecx), level = "debug", ret)] fn create_pointee_place<'tcx>( ecx: &mut CompileTimeEvalContext<'tcx, 'tcx>, ty: Ty<'tcx>, @@ -212,7 +212,7 @@ fn create_pointee_place<'tcx>( ) -> MPlaceTy<'tcx> { let tcx = ecx.tcx.tcx; - if !ty.is_sized(ecx.tcx, ty::ParamEnv::empty()) { + if !ty.is_sized(*ecx.tcx, ty::ParamEnv::empty()) { // We need to create `Allocation`s for custom DSTs let (unsized_inner_ty, num_elems) = get_info_on_unsized_field(ty, valtree, tcx); @@ -237,14 +237,11 @@ fn create_pointee_place<'tcx>( let ptr = ecx.allocate_ptr(size, align, MemoryKind::Stack).unwrap(); debug!(?ptr); - let place = MPlaceTy::from_aligned_ptr_with_meta( + MPlaceTy::from_aligned_ptr_with_meta( ptr.into(), layout, MemPlaceMeta::Meta(Scalar::from_machine_usize(num_elems as u64, &tcx)), - ); - debug!(?place); - - place + ) } else { create_mplace_from_layout(ecx, ty) } @@ -253,7 +250,7 @@ fn create_pointee_place<'tcx>( /// Converts a `ValTree` to a `ConstValue`, which is needed after mir /// construction has finished. // FIXME Merge `valtree_to_const_value` and `valtree_into_mplace` into one function -#[instrument(skip(tcx), level = "debug")] +#[instrument(skip(tcx), level = "debug", ret)] pub fn valtree_to_const_value<'tcx>( tcx: TyCtxt<'tcx>, param_env_ty: ty::ParamEnvAnd<'tcx, Ty<'tcx>>, @@ -294,7 +291,7 @@ pub fn valtree_to_const_value<'tcx>( dump_place(&ecx, place.into()); intern_const_alloc_recursive(&mut ecx, InternKind::Constant, &place).unwrap(); - let const_val = match ty.kind() { + match ty.kind() { ty::Ref(_, _, _) => { let ref_place = place.to_ref(&tcx); let imm = @@ -303,10 +300,7 @@ pub fn valtree_to_const_value<'tcx>( op_to_const(&ecx, &imm.into()) } _ => op_to_const(&ecx, &place.into()), - }; - debug!(?const_val); - - const_val + } } ty::Never | ty::Error(_) @@ -349,11 +343,7 @@ fn valtree_into_mplace<'tcx>( ty::Bool | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Char => { let scalar_int = valtree.unwrap_leaf(); debug!("writing trivial valtree {:?} to place {:?}", scalar_int, place); - ecx.write_immediate( - Immediate::Scalar(ScalarMaybeUninit::Scalar(scalar_int.into())), - &place.into(), - ) - .unwrap(); + ecx.write_immediate(Immediate::Scalar(scalar_int.into()), &place.into()).unwrap(); } ty::Ref(_, inner_ty, _) => { let mut pointee_place = create_pointee_place(ecx, *inner_ty, valtree); @@ -366,11 +356,10 @@ fn valtree_into_mplace<'tcx>( let imm = match inner_ty.kind() { ty::Slice(_) | ty::Str => { let len = valtree.unwrap_branch().len(); - let len_scalar = - ScalarMaybeUninit::Scalar(Scalar::from_machine_usize(len as u64, &tcx)); + let len_scalar = Scalar::from_machine_usize(len as u64, &tcx); Immediate::ScalarPair( - ScalarMaybeUninit::from_maybe_pointer((*pointee_place).ptr, &tcx), + Scalar::from_maybe_pointer((*pointee_place).ptr, &tcx), len_scalar, ) } @@ -409,7 +398,7 @@ fn valtree_into_mplace<'tcx>( let mut place_inner = match ty.kind() { ty::Str | ty::Slice(_) => ecx.mplace_index(&place, i as u64).unwrap(), - _ if !ty.is_sized(ecx.tcx, ty::ParamEnv::empty()) + _ if !ty.is_sized(*ecx.tcx, ty::ParamEnv::empty()) && i == branches.len() - 1 => { // Note: For custom DSTs we need to manually process the last unsized field. |