diff options
Diffstat (limited to 'compiler/rustc_const_eval/src/const_eval')
| -rw-r--r-- | compiler/rustc_const_eval/src/const_eval/eval_queries.rs | 4 | ||||
| -rw-r--r-- | compiler/rustc_const_eval/src/const_eval/mod.rs | 194 | ||||
| -rw-r--r-- | compiler/rustc_const_eval/src/const_eval/valtrees.rs | 51 |
3 files changed, 107 insertions, 142 deletions
diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index 0c20324e452..b7e5e7aea49 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -196,7 +196,7 @@ pub(super) fn op_to_const<'tcx>( } #[instrument(skip(tcx), level = "debug")] -fn turn_into_const_value<'tcx>( +pub(crate) fn turn_into_const_value<'tcx>( tcx: TyCtxt<'tcx>, constant: ConstAlloc<'tcx>, key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>, @@ -222,6 +222,7 @@ fn turn_into_const_value<'tcx>( const_val } +#[instrument(skip(tcx), level = "debug")] pub fn eval_to_const_value_raw_provider<'tcx>( tcx: TyCtxt<'tcx>, key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>, @@ -256,6 +257,7 @@ pub fn eval_to_const_value_raw_provider<'tcx>( tcx.eval_to_allocation_raw(key).map(|val| turn_into_const_value(tcx, val, key)) } +#[instrument(skip(tcx), level = "debug")] pub fn eval_to_allocation_raw_provider<'tcx>( tcx: TyCtxt<'tcx>, key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>, diff --git a/compiler/rustc_const_eval/src/const_eval/mod.rs b/compiler/rustc_const_eval/src/const_eval/mod.rs index 6ee77db4017..51edf64de80 100644 --- a/compiler/rustc_const_eval/src/const_eval/mod.rs +++ b/compiler/rustc_const_eval/src/const_eval/mod.rs @@ -1,12 +1,11 @@ // Not in interpret to make sure we do not use private implementation details -use std::convert::TryFrom; - 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 rustc_target::abi::VariantIdx; use crate::interpret::{ intern_const_alloc_recursive, ConstValue, InternKind, InterpCx, InterpResult, MemPlaceMeta, @@ -25,6 +24,12 @@ pub use fn_queries::*; pub use machine::*; pub(crate) use valtrees::{const_to_valtree_inner, valtree_to_const_value}; +pub(crate) enum ValTreeCreationError { + NonSupportedType, + Other, +} +pub(crate) type ValTreeCreationResult<'tcx> = Result<ty::ValTree<'tcx>, ValTreeCreationError>; + pub(crate) fn const_caller_location( tcx: TyCtxt<'_>, (file, line, col): (Symbol, u32, u32), @@ -39,16 +44,6 @@ pub(crate) fn const_caller_location( ConstValue::Scalar(Scalar::from_maybe_pointer(loc_place.ptr, &tcx)) } -// We forbid type-level constants that contain more than `VALTREE_MAX_NODES` nodes. -const VALTREE_MAX_NODES: usize = 1000; - -pub(crate) enum ValTreeCreationError { - NodesOverflow, - NonSupportedType, - Other, -} -pub(crate) type ValTreeCreationResult<'tcx> = Result<ty::ValTree<'tcx>, ValTreeCreationError>; - /// Evaluates a constant and turns it into a type-level constant value. pub(crate) fn eval_to_valtree<'tcx>( tcx: TyCtxt<'tcx>, @@ -56,6 +51,8 @@ pub(crate) fn eval_to_valtree<'tcx>( cid: GlobalId<'tcx>, ) -> EvalToValTreeResult<'tcx> { let const_alloc = tcx.eval_to_allocation_raw(param_env.and(cid))?; + + // FIXME Need to provide a span to `eval_to_valtree` let ecx = mk_eval_cx( tcx, DUMMY_SP, param_env, // It is absolutely crucial for soundness that @@ -65,65 +62,89 @@ pub(crate) fn eval_to_valtree<'tcx>( let place = ecx.raw_const_to_mplace(const_alloc).unwrap(); debug!(?place); - let mut num_nodes = 0; - let valtree_result = const_to_valtree_inner(&ecx, &place, &mut num_nodes); + let valtree_result = const_to_valtree_inner(&ecx, &place); match valtree_result { Ok(valtree) => Ok(Some(valtree)), - Err(err) => { - let did = cid.instance.def_id(); - let s = cid.display(tcx); - match err { - ValTreeCreationError::NodesOverflow => { - let msg = format!("maximum number of nodes exceeded in constant {}", &s); - let mut diag = match tcx.hir().span_if_local(did) { - Some(span) => tcx.sess.struct_span_err(span, &msg), - None => tcx.sess.struct_err(&msg), - }; - diag.emit(); - - Ok(None) - } - ValTreeCreationError::NonSupportedType | ValTreeCreationError::Other => Ok(None), - } - } + Err(_) => Ok(None), } } -/// This function should never fail for validated constants. However, it is also invoked from the -/// pretty printer which might attempt to format invalid constants and in that case it might fail. +/// Tries to destructure constants of type Array or Adt into the constants +/// of its fields. pub(crate) fn try_destructure_const<'tcx>( tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - val: ty::Const<'tcx>, -) -> InterpResult<'tcx, mir::DestructuredConst<'tcx>> { - trace!("destructure_const: {:?}", val); - let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false); - 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() { - ty::Array(_, len) => (usize::try_from(len.eval_usize(tcx, param_env)).unwrap(), None, op), - // Checks if we have any variants, to avoid downcasting to a non-existing variant (when - // there are no variants `read_discriminant` successfully returns a non-existing variant - // index). - ty::Adt(def, _) if def.variants().is_empty() => throw_ub!(Unreachable), - ty::Adt(def, _) => { - let variant = ecx.read_discriminant(&op)?.1; - let down = ecx.operand_downcast(&op, variant)?; - (def.variant(variant).fields.len(), Some(variant), down) - } - ty::Tuple(substs) => (substs.len(), None, op), - _ => bug!("cannot destructure constant {:?}", val), - }; - let fields = (0..field_count) - .map(|i| { - let field_op = ecx.operand_field(&down, i)?; - let val = op_to_const(&ecx, &field_op); - Ok(ty::Const::from_value(tcx, val, field_op.layout.ty)) - }) - .collect::<InterpResult<'tcx, Vec<_>>>()?; - let fields = tcx.arena.alloc_from_iter(fields); - Ok(mir::DestructuredConst { variant, fields }) + const_: ty::Const<'tcx>, +) -> Option<mir::DestructuredConst<'tcx>> { + if let ty::ConstKind::Value(valtree) = const_.val() { + let branches = match valtree { + ty::ValTree::Branch(b) => b, + _ => return None, + }; + + let (fields, variant) = match const_.ty().kind() { + ty::Array(inner_ty, _) | ty::Slice(inner_ty) => { + // construct the consts for the elements of the array/slice + let field_consts = branches + .iter() + .map(|b| { + tcx.mk_const(ty::ConstS { kind: ty::ConstKind::Value(*b), ty: *inner_ty }) + }) + .collect::<Vec<_>>(); + debug!(?field_consts); + + (field_consts, None) + } + ty::Adt(def, _) if def.variants().is_empty() => bug!("unreachable"), + ty::Adt(def, substs) => { + let variant_idx = if def.is_enum() { + VariantIdx::from_u32(branches[0].unwrap_leaf().try_to_u32().ok()?) + } else { + VariantIdx::from_u32(0) + }; + let fields = &def.variant(variant_idx).fields; + let mut field_consts = Vec::with_capacity(fields.len()); + + // Note: First element inValTree corresponds to variant of enum + let mut valtree_idx = if def.is_enum() { 1 } else { 0 }; + for field in fields { + let field_ty = field.ty(tcx, substs); + let field_valtree = branches[valtree_idx]; // first element of branches is variant + let field_const = tcx.mk_const(ty::ConstS { + kind: ty::ConstKind::Value(field_valtree), + ty: field_ty, + }); + field_consts.push(field_const); + valtree_idx += 1; + } + debug!(?field_consts); + + (field_consts, Some(variant_idx)) + } + ty::Tuple(elem_tys) => { + let fields = elem_tys + .iter() + .enumerate() + .map(|(i, elem_ty)| { + let elem_valtree = branches[i]; + tcx.mk_const(ty::ConstS { + kind: ty::ConstKind::Value(elem_valtree), + ty: elem_ty, + }) + }) + .collect::<Vec<_>>(); + + (fields, None) + } + _ => bug!("cannot destructure constant {:?}", const_), + }; + + let fields = tcx.arena.alloc_from_iter(fields.into_iter()); + + Some(mir::DestructuredConst { variant, fields }) + } else { + None + } } #[instrument(skip(tcx), level = "debug")] @@ -143,8 +164,8 @@ pub(crate) fn try_destructure_mir_constant<'tcx>( throw_ub!(Unreachable) } ty::Adt(def, _) => { - let variant = ecx.read_discriminant(&op).unwrap().1; - let down = ecx.operand_downcast(&op, variant).unwrap(); + let variant = ecx.read_discriminant(&op)?.1; + let down = ecx.operand_downcast(&op, variant)?; (def.variants()[variant].fields.len(), Some(variant), down) } ty::Tuple(substs) => (substs.len(), None, op), @@ -164,43 +185,6 @@ pub(crate) fn try_destructure_mir_constant<'tcx>( } #[instrument(skip(tcx), level = "debug")] -pub(crate) fn deref_const<'tcx>( - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - val: ty::Const<'tcx>, -) -> ty::Const<'tcx> { - trace!("deref_const: {:?}", val); - let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false); - 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!( - tcx.get_global_alloc(alloc_id).unwrap().unwrap_memory().inner().mutability, - Mutability::Not, - "deref_const cannot be used with mutable allocations as \ - that could allow pattern matching to observe mutable statics", - ); - } - - let ty = match mplace.meta { - MemPlaceMeta::None => mplace.layout.ty, - MemPlaceMeta::Poison => bug!("poison metadata in `deref_const`: {:#?}", mplace), - // In case of unsized types, figure out the real type behind. - MemPlaceMeta::Meta(scalar) => match mplace.layout.ty.kind() { - ty::Str => bug!("there's no sized equivalent of a `str`"), - ty::Slice(elem_ty) => tcx.mk_array(*elem_ty, scalar.to_machine_usize(&tcx).unwrap()), - _ => bug!( - "type {} should not have metadata, but had {:?}", - mplace.layout.ty, - mplace.meta - ), - }, - }; - - tcx.mk_const(ty::ConstS { kind: ty::ConstKind::Value(op_to_const(&ecx, &mplace.into())), ty }) -} - -#[instrument(skip(tcx), level = "debug")] pub(crate) fn deref_mir_constant<'tcx>( tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, @@ -211,16 +195,16 @@ pub(crate) fn deref_mir_constant<'tcx>( let mplace = ecx.deref_operand(&op).unwrap(); if let Some(alloc_id) = mplace.ptr.provenance { assert_eq!( - tcx.get_global_alloc(alloc_id).unwrap().unwrap_memory().0.0.mutability, + tcx.get_global_alloc(alloc_id).unwrap().unwrap_memory().0 .0.mutability, Mutability::Not, - "deref_const cannot be used with mutable allocations as \ + "deref_mir_constant cannot be used with mutable allocations as \ that could allow pattern matching to observe mutable statics", ); } let ty = match mplace.meta { MemPlaceMeta::None => mplace.layout.ty, - MemPlaceMeta::Poison => bug!("poison metadata in `deref_const`: {:#?}", mplace), + MemPlaceMeta::Poison => bug!("poison metadata in `deref_mir_constant`: {:#?}", mplace), // In case of unsized types, figure out the real type behind. MemPlaceMeta::Meta(scalar) => match mplace.layout.ty.kind() { ty::Str => bug!("there's no sized equivalent of a `str`"), diff --git a/compiler/rustc_const_eval/src/const_eval/valtrees.rs b/compiler/rustc_const_eval/src/const_eval/valtrees.rs index 7346d69bb5d..080133275a6 100644 --- a/compiler/rustc_const_eval/src/const_eval/valtrees.rs +++ b/compiler/rustc_const_eval/src/const_eval/valtrees.rs @@ -1,24 +1,21 @@ use super::eval_queries::{mk_eval_cx, op_to_const}; use super::machine::CompileTimeEvalContext; -use super::{ValTreeCreationError, ValTreeCreationResult, VALTREE_MAX_NODES}; +use super::{ValTreeCreationError, ValTreeCreationResult}; use crate::interpret::{ intern_const_alloc_recursive, ConstValue, ImmTy, Immediate, InternKind, MemPlaceMeta, MemoryKind, PlaceTy, Scalar, ScalarMaybeUninit, }; +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 crate::interpret::MPlaceTy; -use crate::interpret::Value; -use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt}; - #[instrument(skip(ecx), level = "debug")] fn branches<'tcx>( ecx: &CompileTimeEvalContext<'tcx, 'tcx>, place: &MPlaceTy<'tcx>, n: usize, variant: Option<VariantIdx>, - num_nodes: &mut usize, ) -> ValTreeCreationResult<'tcx> { let place = match variant { Some(variant) => ecx.mplace_downcast(&place, variant).unwrap(), @@ -30,7 +27,7 @@ fn branches<'tcx>( let mut fields = Vec::with_capacity(n); for i in 0..n { let field = ecx.mplace_field(&place, i).unwrap(); - let valtree = const_to_valtree_inner(ecx, &field, num_nodes)?; + let valtree = const_to_valtree_inner(ecx, &field)?; fields.push(Some(valtree)); } @@ -42,11 +39,6 @@ fn branches<'tcx>( .collect::<Option<Vec<_>>>() .expect("should have already checked for errors in ValTree creation"); - // Have to account for ZSTs here - if branches.len() == 0 { - *num_nodes += 1; - } - Ok(ty::ValTree::Branch(ecx.tcx.arena.alloc_from_iter(branches))) } @@ -54,7 +46,6 @@ fn branches<'tcx>( fn slice_branches<'tcx>( ecx: &CompileTimeEvalContext<'tcx, 'tcx>, place: &MPlaceTy<'tcx>, - num_nodes: &mut usize, ) -> ValTreeCreationResult<'tcx> { let n = place .len(&ecx.tcx.tcx) @@ -63,7 +54,7 @@ fn slice_branches<'tcx>( let mut elems = Vec::with_capacity(n as usize); for i in 0..n { let place_elem = ecx.mplace_index(place, i).unwrap(); - let valtree = const_to_valtree_inner(ecx, &place_elem, num_nodes)?; + let valtree = const_to_valtree_inner(ecx, &place_elem)?; elems.push(valtree); } @@ -74,18 +65,12 @@ fn slice_branches<'tcx>( pub(crate) fn const_to_valtree_inner<'tcx>( ecx: &CompileTimeEvalContext<'tcx, 'tcx>, place: &MPlaceTy<'tcx>, - num_nodes: &mut usize, ) -> ValTreeCreationResult<'tcx> { - if *num_nodes >= VALTREE_MAX_NODES { - return Err(ValTreeCreationError::NodesOverflow); - } - let ty = place.layout.ty; debug!("ty kind: {:?}", ty.kind()); match ty.kind() { ty::FnDef(..) => { - *num_nodes += 1; Ok(ty::ValTree::zst()) } ty::Bool | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Char => { @@ -93,7 +78,6 @@ pub(crate) fn const_to_valtree_inner<'tcx>( return Err(ValTreeCreationError::Other); }; let val = val.to_scalar().unwrap(); - *num_nodes += 1; Ok(ty::ValTree::Leaf(val.assert_int())) } @@ -110,11 +94,11 @@ pub(crate) fn const_to_valtree_inner<'tcx>( }; debug!(?derefd_place); - const_to_valtree_inner(ecx, &derefd_place, num_nodes) + const_to_valtree_inner(ecx, &derefd_place) } ty::Str | ty::Slice(_) | ty::Array(_, _) => { - slice_branches(ecx, place, num_nodes) + slice_branches(ecx, place) } // Trait objects are not allowed in type level constants, as we have no concept for // resolving their backing type, even if we can do that at const eval time. We may @@ -123,7 +107,7 @@ pub(crate) fn const_to_valtree_inner<'tcx>( ty::Dynamic(..) => Err(ValTreeCreationError::NonSupportedType), ty::Tuple(elem_tys) => { - branches(ecx, place, elem_tys.len(), None, num_nodes) + branches(ecx, place, elem_tys.len(), None) } ty::Adt(def, _) => { @@ -136,7 +120,7 @@ pub(crate) fn const_to_valtree_inner<'tcx>( let Ok((_, variant)) = ecx.read_discriminant(&place.into()) else { return Err(ValTreeCreationError::Other); }; - branches(ecx, place, def.variant(variant).fields.len(), def.is_enum().then_some(variant), num_nodes) + branches(ecx, place, def.variant(variant).fields.len(), def.is_enum().then_some(variant)) } ty::Never @@ -234,13 +218,9 @@ fn create_pointee_place<'tcx>( // Get the size of the memory behind the DST let dst_size = unsized_inner_ty_size.checked_mul(num_elems as u64, &tcx).unwrap(); - let ptr = ecx - .allocate_ptr( - size_of_sized_part.checked_add(dst_size, &tcx).unwrap(), - Align::from_bytes(1).unwrap(), - MemoryKind::Stack, - ) - .unwrap(); + let size = size_of_sized_part.checked_add(dst_size, &tcx).unwrap(); + let align = Align::from_bytes(size.bytes().next_power_of_two()).unwrap(); + let ptr = ecx.allocate_ptr(size, align, MemoryKind::Stack).unwrap(); debug!(?ptr); let place = MPlaceTy::from_aligned_ptr_with_meta( @@ -262,7 +242,7 @@ fn create_pointee_place<'tcx>( #[instrument(skip(tcx), level = "debug")] pub fn valtree_to_const_value<'tcx>( tcx: TyCtxt<'tcx>, - ty: Ty<'tcx>, + param_env_ty: ty::ParamEnvAnd<'tcx, Ty<'tcx>>, valtree: ty::ValTree<'tcx>, ) -> ConstValue<'tcx> { // Basic idea: We directly construct `Scalar` values from trivial `ValTree`s @@ -272,8 +252,8 @@ pub fn valtree_to_const_value<'tcx>( // create inner `MPlace`s which are filled recursively. // FIXME Does this need an example? - let mut ecx = mk_eval_cx(tcx, DUMMY_SP, ty::ParamEnv::empty(), false); - let param_env_ty = ty::ParamEnv::empty().and(ty); + let (param_env, ty) = param_env_ty.into_parts(); + let mut ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false); match ty.kind() { ty::FnDef(..) => { @@ -336,7 +316,6 @@ pub fn valtree_to_const_value<'tcx>( } } -// FIXME Needs a better/correct name #[instrument(skip(ecx), level = "debug")] fn valtree_into_mplace<'tcx>( ecx: &mut CompileTimeEvalContext<'tcx, 'tcx>, |
