diff options
Diffstat (limited to 'compiler/rustc_const_eval')
19 files changed, 161 insertions, 219 deletions
diff --git a/compiler/rustc_const_eval/Cargo.toml b/compiler/rustc_const_eval/Cargo.toml index 7717cd2c696..a0cc2c65e6e 100644 --- a/compiler/rustc_const_eval/Cargo.toml +++ b/compiler/rustc_const_eval/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "rustc_const_eval" version = "0.0.0" -edition = "2021" +edition = "2024" [dependencies] # tidy-alphabetical-start diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs index d0ce027ec2b..90002d3f109 100644 --- a/compiler/rustc_const_eval/src/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/check_consts/check.rs @@ -710,7 +710,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { if is_int_bool_float_or_char(lhs_ty) && is_int_bool_float_or_char(rhs_ty) { // Int, bool, float, and char operations are fine. - } else if lhs_ty.is_fn_ptr() || lhs_ty.is_unsafe_ptr() { + } else if lhs_ty.is_fn_ptr() || lhs_ty.is_raw_ptr() { assert_matches!( op, BinOp::Eq diff --git a/compiler/rustc_const_eval/src/check_consts/mod.rs b/compiler/rustc_const_eval/src/check_consts/mod.rs index bfa0a0319c3..52e000858b4 100644 --- a/compiler/rustc_const_eval/src/check_consts/mod.rs +++ b/compiler/rustc_const_eval/src/check_consts/mod.rs @@ -31,7 +31,7 @@ pub struct ConstCx<'mir, 'tcx> { impl<'mir, 'tcx> ConstCx<'mir, 'tcx> { pub fn new(tcx: TyCtxt<'tcx>, body: &'mir mir::Body<'tcx>) -> Self { let typing_env = body.typing_env(tcx); - let const_kind = tcx.hir().body_const_context(body.source.def_id().expect_local()); + let const_kind = tcx.hir_body_const_context(body.source.def_id().expect_local()); ConstCx { body, tcx, typing_env, const_kind } } diff --git a/compiler/rustc_const_eval/src/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/check_consts/qualifs.rs index 9c99782d223..dfcd1969a73 100644 --- a/compiler/rustc_const_eval/src/check_consts/qualifs.rs +++ b/compiler/rustc_const_eval/src/check_consts/qualifs.rs @@ -188,12 +188,14 @@ impl Qualif for NeedsNonConstDrop { ObligationCause::misc(cx.body.span, cx.def_id()), param_env, ty::Binder::dummy(ty::TraitRef::new(cx.tcx, destruct_def_id, [ty])) - .to_host_effect_clause(cx.tcx, match cx.const_kind() { - rustc_hir::ConstContext::ConstFn => ty::BoundConstness::Maybe, - rustc_hir::ConstContext::Static(_) | rustc_hir::ConstContext::Const { .. } => { - ty::BoundConstness::Const - } - }), + .to_host_effect_clause( + cx.tcx, + match cx.const_kind() { + rustc_hir::ConstContext::ConstFn => ty::BoundConstness::Maybe, + rustc_hir::ConstContext::Static(_) + | rustc_hir::ConstContext::Const { .. } => ty::BoundConstness::Const, + }, + ), )); !ocx.select_all_or_error().is_empty() } diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index 82e0a6e6666..4db862afd9f 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -502,12 +502,10 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { RemainderByZero(op) => RemainderByZero(eval_to_int(op)?), ResumedAfterReturn(coroutine_kind) => ResumedAfterReturn(*coroutine_kind), ResumedAfterPanic(coroutine_kind) => ResumedAfterPanic(*coroutine_kind), - MisalignedPointerDereference { ref required, ref found } => { - MisalignedPointerDereference { - required: eval_to_int(required)?, - found: eval_to_int(found)?, - } - } + MisalignedPointerDereference { required, found } => MisalignedPointerDereference { + required: eval_to_int(required)?, + found: eval_to_int(found)?, + }, NullPointerDereference => NullPointerDereference, }; 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 c35910a706b..3776fb55c2e 100644 --- a/compiler/rustc_const_eval/src/const_eval/valtrees.rs +++ b/compiler/rustc_const_eval/src/const_eval/valtrees.rs @@ -2,7 +2,7 @@ use rustc_abi::{BackendRepr, VariantIdx}; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_middle::mir::interpret::{EvalToValTreeResult, GlobalId, ReportedErrorInfo}; use rustc_middle::ty::layout::{LayoutCx, LayoutOf, TyAndLayout}; -use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt}; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::{bug, mir}; use rustc_span::DUMMY_SP; use tracing::{debug, instrument, trace}; @@ -21,7 +21,7 @@ use crate::interpret::{ fn branches<'tcx>( ecx: &CompileTimeInterpCx<'tcx>, place: &MPlaceTy<'tcx>, - n: usize, + field_count: usize, variant: Option<VariantIdx>, num_nodes: &mut usize, ) -> ValTreeCreationResult<'tcx> { @@ -29,30 +29,28 @@ fn branches<'tcx>( Some(variant) => ecx.project_downcast(place, variant).unwrap(), None => place.clone(), }; - let variant = variant.map(|variant| Some(ty::ValTree::Leaf(ScalarInt::from(variant.as_u32())))); - debug!(?place, ?variant); + debug!(?place); - let mut fields = Vec::with_capacity(n); - for i in 0..n { - let field = ecx.project_field(&place, i).unwrap(); - let valtree = const_to_valtree_inner(ecx, &field, num_nodes)?; - fields.push(Some(valtree)); - } + let mut branches = Vec::with_capacity(field_count + variant.is_some() as usize); // For enums, we prepend their variant index before the variant's fields so we can figure out // the variant again when just seeing a valtree. - let branches = variant - .into_iter() - .chain(fields.into_iter()) - .collect::<Option<Vec<_>>>() - .expect("should have already checked for errors in ValTree creation"); + if let Some(variant) = variant { + branches.push(ty::ValTree::from_scalar_int(*ecx.tcx, variant.as_u32().into())); + } + + for i in 0..field_count { + let field = ecx.project_field(&place, i).unwrap(); + let valtree = const_to_valtree_inner(ecx, &field, num_nodes)?; + branches.push(valtree); + } // Have to account for ZSTs here if branches.len() == 0 { *num_nodes += 1; } - Ok(ty::ValTree::Branch(ecx.tcx.arena.alloc_from_iter(branches))) + Ok(ty::ValTree::from_branches(*ecx.tcx, branches)) } #[instrument(skip(ecx), level = "debug")] @@ -70,7 +68,7 @@ fn slice_branches<'tcx>( elems.push(valtree); } - Ok(ty::ValTree::Branch(ecx.tcx.arena.alloc_from_iter(elems))) + Ok(ty::ValTree::from_branches(*ecx.tcx, elems)) } #[instrument(skip(ecx), level = "debug")] @@ -79,6 +77,7 @@ fn const_to_valtree_inner<'tcx>( place: &MPlaceTy<'tcx>, num_nodes: &mut usize, ) -> ValTreeCreationResult<'tcx> { + let tcx = *ecx.tcx; let ty = place.layout.ty; debug!("ty kind: {:?}", ty.kind()); @@ -89,14 +88,14 @@ fn const_to_valtree_inner<'tcx>( match ty.kind() { ty::FnDef(..) => { *num_nodes += 1; - Ok(ty::ValTree::zst()) + Ok(ty::ValTree::zst(tcx)) } ty::Bool | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Char => { let val = ecx.read_immediate(place).unwrap(); let val = val.to_scalar_int().unwrap(); *num_nodes += 1; - Ok(ty::ValTree::Leaf(val)) + Ok(ty::ValTree::from_scalar_int(tcx, val)) } ty::Pat(base, ..) => { @@ -127,7 +126,7 @@ fn const_to_valtree_inner<'tcx>( return Err(ValTreeCreationError::NonSupportedType(ty)); }; // It's just a ScalarInt! - Ok(ty::ValTree::Leaf(val)) + Ok(ty::ValTree::from_scalar_int(tcx, val)) } // Technically we could allow function pointers (represented as `ty::Instance`), but this is not guaranteed to @@ -287,16 +286,11 @@ pub fn valtree_to_const_value<'tcx>( // FIXME: Does this need an example? match *cv.ty.kind() { ty::FnDef(..) => { - assert!(cv.valtree.unwrap_branch().is_empty()); + assert!(cv.valtree.is_zst()); mir::ConstValue::ZeroSized } ty::Bool | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Char | ty::RawPtr(_, _) => { - match cv.valtree { - ty::ValTree::Leaf(scalar_int) => mir::ConstValue::Scalar(Scalar::Int(scalar_int)), - ty::ValTree::Branch(_) => bug!( - "ValTrees for Bool, Int, Uint, Float, Char or RawPtr should have the form ValTree::Leaf" - ), - } + mir::ConstValue::Scalar(Scalar::Int(cv.valtree.unwrap_leaf())) } ty::Pat(ty, _) => { let cv = ty::Value { valtree: cv.valtree, ty }; diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs index a2635885098..c08495c012f 100644 --- a/compiler/rustc_const_eval/src/errors.rs +++ b/compiler/rustc_const_eval/src/errors.rs @@ -16,7 +16,6 @@ use rustc_middle::mir::interpret::{ }; use rustc_middle::ty::{self, Mutability, Ty}; use rustc_span::{Span, Symbol}; -use rustc_target::callconv::AdjustForForeignAbiError; use crate::interpret::InternKind; @@ -578,10 +577,13 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> { } ShiftOverflow { intrinsic, shift_amount } => { diag.arg("intrinsic", intrinsic); - diag.arg("shift_amount", match shift_amount { - Either::Left(v) => v.to_string(), - Either::Right(v) => v.to_string(), - }); + diag.arg( + "shift_amount", + match shift_amount { + Either::Left(v) => v.to_string(), + Either::Right(v) => v.to_string(), + }, + ); } BoundsCheckFailed { len, index } => { diag.arg("len", len); @@ -933,9 +935,6 @@ impl<'tcx> ReportErrorExt for InvalidProgramInfo<'tcx> { InvalidProgramInfo::TooGeneric => const_eval_too_generic, InvalidProgramInfo::AlreadyReported(_) => const_eval_already_reported, InvalidProgramInfo::Layout(e) => e.diagnostic_message(), - InvalidProgramInfo::FnAbiAdjustForForeignAbi(_) => { - rustc_middle::error::middle_adjust_for_foreign_abi_error - } } } fn add_args<G: EmissionGuarantee>(self, diag: &mut Diag<'_, G>) { @@ -950,12 +949,6 @@ impl<'tcx> ReportErrorExt for InvalidProgramInfo<'tcx> { } dummy_diag.cancel(); } - InvalidProgramInfo::FnAbiAdjustForForeignAbi( - AdjustForForeignAbiError::Unsupported { arch, abi }, - ) => { - diag.arg("arch", arch); - diag.arg("abi", abi.name()); - } } } } diff --git a/compiler/rustc_const_eval/src/interpret/call.rs b/compiler/rustc_const_eval/src/interpret/call.rs index e2e6e16d8a7..29f819cca1f 100644 --- a/compiler/rustc_const_eval/src/interpret/call.rs +++ b/compiler/rustc_const_eval/src/interpret/call.rs @@ -241,7 +241,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { interp_ok(caller == callee) } - fn check_argument_compat( + /// Returns a `bool` saying whether the two arguments are ABI-compatible. + pub fn check_argument_compat( &self, caller_abi: &ArgAbi<'tcx, Ty<'tcx>>, callee_abi: &ArgAbi<'tcx, Ty<'tcx>>, @@ -381,10 +382,13 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { "caller ABI: {:#?}, args: {:#?}", caller_fn_abi, args.iter() - .map(|arg| (arg.layout().ty, match arg { - FnArg::Copy(op) => format!("copy({op:?})"), - FnArg::InPlace(mplace) => format!("in-place({mplace:?})"), - })) + .map(|arg| ( + arg.layout().ty, + match arg { + FnArg::Copy(op) => format!("copy({op:?})"), + FnArg::InPlace(mplace) => format!("in-place({mplace:?})"), + } + )) .collect::<Vec<_>>() ); trace!( @@ -874,10 +878,13 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ); // Check `unwinding`. - assert_eq!(unwinding, match self.frame().loc { - Left(loc) => self.body().basic_blocks[loc.block].is_cleanup, - Right(_) => true, - }); + assert_eq!( + unwinding, + match self.frame().loc { + Left(loc) => self.body().basic_blocks[loc.block].is_cleanup, + Right(_) => true, + } + ); if unwinding && self.frame_idx() == 0 { throw_ub_custom!(fluent::const_eval_unwind_past_top); } diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs index e110c155da0..7b706334ed8 100644 --- a/compiler/rustc_const_eval/src/interpret/cast.rs +++ b/compiler/rustc_const_eval/src/interpret/cast.rs @@ -203,7 +203,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { cast_to: TyAndLayout<'tcx>, ) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> { assert!(src.layout.ty.is_any_ptr()); - assert!(cast_to.ty.is_unsafe_ptr()); + assert!(cast_to.ty.is_raw_ptr()); // Handle casting any ptr to raw ptr (might be a wide ptr). if cast_to.size == src.layout.size { // Thin or wide pointer that just has the ptr kind of target type changed. @@ -212,7 +212,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // Casting the metadata away from a wide ptr. assert_eq!(src.layout.size, 2 * self.pointer_size()); assert_eq!(cast_to.size, self.pointer_size()); - assert!(src.layout.ty.is_unsafe_ptr()); + assert!(src.layout.ty.is_raw_ptr()); return match **src { Immediate::ScalarPair(data, _) => interp_ok(ImmTy::from_scalar(data, cast_to)), Immediate::Scalar(..) => span_bug!( @@ -430,10 +430,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { }; let erased_trait_ref = ty::ExistentialTraitRef::erase_self_ty(*self.tcx, upcast_trait_ref); - assert!(data_b.principal().is_some_and(|b| self.eq_in_param_env( - erased_trait_ref, - self.tcx.instantiate_bound_regions_with_erased(b) - ))); + assert_eq!( + data_b.principal().map(|b| { + self.tcx.normalize_erasing_late_bound_regions(self.typing_env, b) + }), + Some(erased_trait_ref), + ); } else { // In this case codegen would keep using the old vtable. We don't want to do // that as it has the wrong trait. The reason codegen can do this is that diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index 95a72d3cbc1..c1948e9f31f 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -4,9 +4,6 @@ use either::{Left, Right}; use rustc_abi::{Align, HasDataLayout, Size, TargetDataLayout}; use rustc_errors::DiagCtxtHandle; use rustc_hir::def_id::DefId; -use rustc_infer::infer::TyCtxtInferExt; -use rustc_infer::infer::at::ToTrace; -use rustc_infer::traits::ObligationCause; use rustc_middle::mir::interpret::{ErrorHandled, InvalidMetaKind, ReportedErrorInfo}; use rustc_middle::query::TyCtxtAt; use rustc_middle::ty::layout::{ @@ -17,8 +14,7 @@ use rustc_middle::{mir, span_bug}; use rustc_session::Limit; use rustc_span::Span; use rustc_target::callconv::FnAbi; -use rustc_trait_selection::traits::ObligationCtxt; -use tracing::{debug, instrument, trace}; +use tracing::{debug, trace}; use super::{ Frame, FrameInfo, GlobalId, InterpErrorInfo, InterpErrorKind, InterpResult, MPlaceTy, Machine, @@ -106,9 +102,6 @@ impl<'tcx, M: Machine<'tcx>> FnAbiOfHelpers<'tcx> for InterpCx<'tcx, M> { ) -> InterpErrorKind<'tcx> { match err { FnAbiError::Layout(err) => err_inval!(Layout(err)), - FnAbiError::AdjustForForeignAbi(err) => { - err_inval!(FnAbiAdjustForForeignAbi(err)) - } } } } @@ -323,40 +316,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } } - /// Check if the two things are equal in the current param_env, using an infcx to get proper - /// equality checks. - #[instrument(level = "trace", skip(self), ret)] - pub(super) fn eq_in_param_env<T>(&self, a: T, b: T) -> bool - where - T: PartialEq + TypeFoldable<TyCtxt<'tcx>> + ToTrace<'tcx>, - { - // Fast path: compare directly. - if a == b { - return true; - } - // Slow path: spin up an inference context to check if these traits are sufficiently equal. - let (infcx, param_env) = self.tcx.infer_ctxt().build_with_typing_env(self.typing_env); - let ocx = ObligationCtxt::new(&infcx); - let cause = ObligationCause::dummy_with_span(self.cur_span()); - // equate the two trait refs after normalization - let a = ocx.normalize(&cause, param_env, a); - let b = ocx.normalize(&cause, param_env, b); - - if let Err(terr) = ocx.eq(&cause, param_env, a, b) { - trace!(?terr); - return false; - } - - let errors = ocx.select_all_or_error(); - if !errors.is_empty() { - trace!(?errors); - return false; - } - - // All good. - true - } - /// Walks up the callstack from the intrinsic's callsite, searching for the first callsite in a /// frame which is not `#[track_caller]`. This matches the `caller_location` intrinsic, /// and is primarily intended for the panic machinery. diff --git a/compiler/rustc_const_eval/src/interpret/intern.rs b/compiler/rustc_const_eval/src/interpret/intern.rs index 0cbb3b157f3..43631330f89 100644 --- a/compiler/rustc_const_eval/src/interpret/intern.rs +++ b/compiler/rustc_const_eval/src/interpret/intern.rs @@ -61,8 +61,8 @@ impl HasStaticRootDefId for const_eval::CompileTimeMachine<'_> { /// already mutable (as a sanity check). /// /// Returns an iterator over all relocations referred to by this allocation. -fn intern_shallow<'rt, 'tcx, T, M: CompileTimeMachine<'tcx, T>>( - ecx: &'rt mut InterpCx<'tcx, M>, +fn intern_shallow<'tcx, T, M: CompileTimeMachine<'tcx, T>>( + ecx: &mut InterpCx<'tcx, M>, alloc_id: AllocId, mutability: Mutability, ) -> Result<impl Iterator<Item = CtfeProvenance> + 'tcx, ()> { @@ -102,11 +102,11 @@ fn intern_as_new_static<'tcx>( alloc_id: AllocId, alloc: ConstAllocation<'tcx>, ) { - let feed = tcx.create_def(static_id, sym::nested, DefKind::Static { - safety: hir::Safety::Safe, - mutability: alloc.0.mutability, - nested: true, - }); + let feed = tcx.create_def( + static_id, + sym::nested, + DefKind::Static { safety: hir::Safety::Safe, mutability: alloc.0.mutability, nested: true }, + ); tcx.set_nested_alloc_id_static(alloc_id, feed.def_id()); if tcx.is_thread_local_static(static_id.into()) { diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs index 5d905cff1f2..36da9037e43 100644 --- a/compiler/rustc_const_eval/src/interpret/operand.rs +++ b/compiler/rustc_const_eval/src/interpret/operand.rs @@ -385,7 +385,7 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> { (Immediate::Uninit, _) => Immediate::Uninit, // If the field is uninhabited, we can forget the data (can happen in ConstProp). // `enum S { A(!), B, C }` is an example of an enum with Scalar layout that - // has an `Uninhabited` variant, which means this case is possible. + // has an uninhabited variant, which means this case is possible. _ if layout.is_uninhabited() => Immediate::Uninit, // the field contains no information, can be left uninit // (Scalar/ScalarPair can contain even aligned ZST, not just 1-ZST) diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index abe73c43d8a..6a17da61c8b 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -241,7 +241,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // Figure out whether this is an addr_of of an already raw place. let place_base_raw = if place.is_indirect_first_projection() { let ty = self.frame().body.local_decls[place.local].ty; - ty.is_unsafe_ptr() + ty.is_raw_ptr() } else { // Not a deref, and thus not raw. false diff --git a/compiler/rustc_const_eval/src/interpret/traits.rs b/compiler/rustc_const_eval/src/interpret/traits.rs index 4cfaacebfcd..a5029eea5a7 100644 --- a/compiler/rustc_const_eval/src/interpret/traits.rs +++ b/compiler/rustc_const_eval/src/interpret/traits.rs @@ -86,21 +86,15 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { throw_ub!(InvalidVTableTrait { vtable_dyn_type, expected_dyn_type }); } + // This checks whether there is a subtyping relation between the predicates in either direction. + // For example: + // - casting between `dyn for<'a> Trait<fn(&'a u8)>` and `dyn Trait<fn(&'static u8)>` is OK + // - casting between `dyn Trait<for<'a> fn(&'a u8)>` and either of the above is UB for (a_pred, b_pred) in std::iter::zip(sorted_vtable, sorted_expected) { - let is_eq = match (a_pred.skip_binder(), b_pred.skip_binder()) { - ( - ty::ExistentialPredicate::Trait(a_data), - ty::ExistentialPredicate::Trait(b_data), - ) => self.eq_in_param_env(a_pred.rebind(a_data), b_pred.rebind(b_data)), + let a_pred = self.tcx.normalize_erasing_late_bound_regions(self.typing_env, a_pred); + let b_pred = self.tcx.normalize_erasing_late_bound_regions(self.typing_env, b_pred); - ( - ty::ExistentialPredicate::Projection(a_data), - ty::ExistentialPredicate::Projection(b_data), - ) => self.eq_in_param_env(a_pred.rebind(a_data), b_pred.rebind(b_data)), - - _ => false, - }; - if !is_eq { + if a_pred != b_pred { throw_ub!(InvalidVTableTrait { vtable_dyn_type, expected_dyn_type }); } } diff --git a/compiler/rustc_const_eval/src/interpret/util.rs b/compiler/rustc_const_eval/src/interpret/util.rs index eb98e3b5380..ba579e25f03 100644 --- a/compiler/rustc_const_eval/src/interpret/util.rs +++ b/compiler/rustc_const_eval/src/interpret/util.rs @@ -1,12 +1,8 @@ -use std::ops::ControlFlow; - use rustc_hir::def_id::LocalDefId; use rustc_middle::mir; use rustc_middle::mir::interpret::{AllocInit, Allocation, InterpResult, Pointer}; use rustc_middle::ty::layout::TyAndLayout; -use rustc_middle::ty::{ - self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, -}; +use rustc_middle::ty::{TyCtxt, TypeVisitable, TypeVisitableExt}; use tracing::debug; use super::{InterpCx, MPlaceTy, MemoryKind, interp_ok, throw_inval}; @@ -20,44 +16,10 @@ where T: TypeVisitable<TyCtxt<'tcx>>, { debug!("ensure_monomorphic_enough: ty={:?}", ty); - if !ty.has_param() { - return interp_ok(()); - } - - struct FoundParam; - struct UsedParamsNeedInstantiationVisitor {} - - impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for UsedParamsNeedInstantiationVisitor { - type Result = ControlFlow<FoundParam>; - - fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result { - if !ty.has_param() { - return ControlFlow::Continue(()); - } - - match *ty.kind() { - ty::Param(_) => ControlFlow::Break(FoundParam), - ty::Closure(..) | ty::CoroutineClosure(..) | ty::Coroutine(..) | ty::FnDef(..) => { - ControlFlow::Continue(()) - } - _ => ty.super_visit_with(self), - } - } - - fn visit_const(&mut self, c: ty::Const<'tcx>) -> Self::Result { - match c.kind() { - ty::ConstKind::Param(..) => ControlFlow::Break(FoundParam), - _ => c.super_visit_with(self), - } - } - } - - let mut vis = UsedParamsNeedInstantiationVisitor {}; - if matches!(ty.visit_with(&mut vis), ControlFlow::Break(FoundParam)) { + if ty.has_param() { throw_inval!(TooGeneric); - } else { - interp_ok(()) } + interp_ok(()) } impl<'tcx> InterpretationResult<'tcx> for mir::interpret::ConstAllocation<'tcx> { diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 8e544798742..40dec0cb39e 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -812,10 +812,10 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { if start == 1 && end == max_value { // Only null is the niche. So make sure the ptr is NOT null. if self.ecx.scalar_may_be_null(scalar)? { - throw_validation_failure!(self.path, NullablePtrOutOfRange { - range: valid_range, - max_value - }) + throw_validation_failure!( + self.path, + NullablePtrOutOfRange { range: valid_range, max_value } + ) } else { return interp_ok(()); } @@ -825,10 +825,10 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { } else { // Conservatively, we reject, because the pointer *could* have a bad // value. - throw_validation_failure!(self.path, PtrOutOfRange { - range: valid_range, - max_value - }) + throw_validation_failure!( + self.path, + PtrOutOfRange { range: valid_range, max_value } + ) } } }; @@ -836,11 +836,10 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { if valid_range.contains(bits) { interp_ok(()) } else { - throw_validation_failure!(self.path, OutOfRange { - value: format!("{bits}"), - range: valid_range, - max_value - }) + throw_validation_failure!( + self.path, + OutOfRange { value: format!("{bits}"), range: valid_range, max_value } + ) } } @@ -1265,21 +1264,20 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, } } - // *After* all of this, check the ABI. We need to check the ABI to handle - // types like `NonNull` where the `Scalar` info is more restrictive than what - // the fields say (`rustc_layout_scalar_valid_range_start`). - // But in most cases, this will just propagate what the fields say, - // and then we want the error to point at the field -- so, first recurse, - // then check ABI. + // *After* all of this, check further information stored in the layout. We need to check + // this to handle types like `NonNull` where the `Scalar` info is more restrictive than what + // the fields say (`rustc_layout_scalar_valid_range_start`). But in most cases, this will + // just propagate what the fields say, and then we want the error to point at the field -- + // so, we first recurse, then we do this check. // // FIXME: We could avoid some redundant checks here. For newtypes wrapping // scalars, we do the same check on every "level" (e.g., first we check // MyNewtype and then the scalar in there). + if val.layout.is_uninhabited() { + let ty = val.layout.ty; + throw_validation_failure!(self.path, UninhabitedVal { ty }); + } match val.layout.backend_repr { - BackendRepr::Uninhabited => { - let ty = val.layout.ty; - throw_validation_failure!(self.path, UninhabitedVal { ty }); - } BackendRepr::Scalar(scalar_layout) => { if !scalar_layout.is_uninit_valid() { // There is something to check here. diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs index b44d2a01d57..ed5489652fb 100644 --- a/compiler/rustc_const_eval/src/lib.rs +++ b/compiler/rustc_const_eval/src/lib.rs @@ -51,6 +51,8 @@ pub fn provide(providers: &mut Providers) { providers.check_validity_requirement = |tcx, (init_kind, param_env_and_ty)| { util::check_validity_requirement(tcx, init_kind, param_env_and_ty) }; + providers.hooks.validate_scalar_in_layout = + |tcx, scalar, layout| util::validate_scalar_in_layout(tcx, scalar, layout); } /// `rustc_driver::main` installs a handler that will set this to `true` if diff --git a/compiler/rustc_const_eval/src/util/check_validity_requirement.rs b/compiler/rustc_const_eval/src/util/check_validity_requirement.rs index a729d9325c8..6426bca2332 100644 --- a/compiler/rustc_const_eval/src/util/check_validity_requirement.rs +++ b/compiler/rustc_const_eval/src/util/check_validity_requirement.rs @@ -1,9 +1,10 @@ use rustc_abi::{BackendRepr, FieldsShape, Scalar, Variants}; -use rustc_middle::bug; use rustc_middle::ty::layout::{ HasTyCtxt, LayoutCx, LayoutError, LayoutOf, TyAndLayout, ValidityRequirement, }; -use rustc_middle::ty::{PseudoCanonicalInput, Ty, TyCtxt}; +use rustc_middle::ty::{PseudoCanonicalInput, ScalarInt, Ty, TyCtxt}; +use rustc_middle::{bug, ty}; +use rustc_span::DUMMY_SP; use crate::const_eval::{CanAccessMutGlobal, CheckAlignment, CompileTimeMachine}; use crate::interpret::{InterpCx, MemoryKind}; @@ -34,7 +35,7 @@ pub fn check_validity_requirement<'tcx>( let layout_cx = LayoutCx::new(tcx, input.typing_env); if kind == ValidityRequirement::Uninit || tcx.sess.opts.unstable_opts.strict_init_checks { - check_validity_requirement_strict(layout, &layout_cx, kind) + Ok(check_validity_requirement_strict(layout, &layout_cx, kind)) } else { check_validity_requirement_lax(layout, &layout_cx, kind) } @@ -46,10 +47,10 @@ fn check_validity_requirement_strict<'tcx>( ty: TyAndLayout<'tcx>, cx: &LayoutCx<'tcx>, kind: ValidityRequirement, -) -> Result<bool, &'tcx LayoutError<'tcx>> { +) -> bool { let machine = CompileTimeMachine::new(CanAccessMutGlobal::No, CheckAlignment::Error); - let mut cx = InterpCx::new(cx.tcx(), rustc_span::DUMMY_SP, cx.typing_env, machine); + let mut cx = InterpCx::new(cx.tcx(), DUMMY_SP, cx.typing_env, machine); let allocated = cx .allocate(ty, MemoryKind::Machine(crate::const_eval::MemoryKind::Heap)) @@ -69,14 +70,13 @@ fn check_validity_requirement_strict<'tcx>( // due to this. // The value we are validating is temporary and discarded at the end of this function, so // there is no point in reseting provenance and padding. - Ok(cx - .validate_operand( - &allocated.into(), - /*recursive*/ false, - /*reset_provenance_and_padding*/ false, - ) - .discard_err() - .is_some()) + cx.validate_operand( + &allocated.into(), + /*recursive*/ false, + /*reset_provenance_and_padding*/ false, + ) + .discard_err() + .is_some() } /// Implements the 'lax' (default) version of the [`check_validity_requirement`] checks; see that @@ -111,13 +111,15 @@ fn check_validity_requirement_lax<'tcx>( }; // Check the ABI. - let valid = match this.backend_repr { - BackendRepr::Uninhabited => false, // definitely UB - BackendRepr::Scalar(s) => scalar_allows_raw_init(s), - BackendRepr::ScalarPair(s1, s2) => scalar_allows_raw_init(s1) && scalar_allows_raw_init(s2), - BackendRepr::Vector { element: s, count } => count == 0 || scalar_allows_raw_init(s), - BackendRepr::Memory { .. } => true, // Fields are checked below. - }; + let valid = !this.is_uninhabited() // definitely UB if uninhabited + && match this.backend_repr { + BackendRepr::Scalar(s) => scalar_allows_raw_init(s), + BackendRepr::ScalarPair(s1, s2) => { + scalar_allows_raw_init(s1) && scalar_allows_raw_init(s2) + } + BackendRepr::Vector { element: s, count } => count == 0 || scalar_allows_raw_init(s), + BackendRepr::Memory { .. } => true, // Fields are checked below. + }; if !valid { // This is definitely not okay. return Ok(false); @@ -168,3 +170,31 @@ fn check_validity_requirement_lax<'tcx>( Ok(true) } + +pub(crate) fn validate_scalar_in_layout<'tcx>( + tcx: TyCtxt<'tcx>, + scalar: ScalarInt, + ty: Ty<'tcx>, +) -> bool { + let machine = CompileTimeMachine::new(CanAccessMutGlobal::No, CheckAlignment::Error); + + let typing_env = ty::TypingEnv::fully_monomorphized(); + let mut cx = InterpCx::new(tcx, DUMMY_SP, typing_env, machine); + + let Ok(layout) = cx.layout_of(ty) else { + bug!("could not compute layout of {scalar:?}:{ty:?}") + }; + let allocated = cx + .allocate(layout, MemoryKind::Machine(crate::const_eval::MemoryKind::Heap)) + .expect("OOM: failed to allocate for uninit check"); + + cx.write_scalar(scalar, &allocated).unwrap(); + + cx.validate_operand( + &allocated.into(), + /*recursive*/ false, + /*reset_provenance_and_padding*/ false, + ) + .discard_err() + .is_some() +} diff --git a/compiler/rustc_const_eval/src/util/mod.rs b/compiler/rustc_const_eval/src/util/mod.rs index 25a9dbb2c11..5be5ee8d1ae 100644 --- a/compiler/rustc_const_eval/src/util/mod.rs +++ b/compiler/rustc_const_eval/src/util/mod.rs @@ -8,6 +8,7 @@ mod type_name; pub use self::alignment::{is_disaligned, is_within_packed}; pub use self::check_validity_requirement::check_validity_requirement; +pub(crate) use self::check_validity_requirement::validate_scalar_in_layout; pub use self::compare_types::{relate_types, sub_types}; pub use self::type_name::type_name; |
