|
1 | 1 | //! Propagates constants for early reporting of statically known
|
2 | 2 | //! assertion failures
|
3 | 3 |
|
| 4 | +use rustc_const_eval::interpret::{ |
| 5 | + self, compile_time_machine, AllocId, ConstAllocation, FnArg, Frame, ImmTy, InterpCx, |
| 6 | + InterpResult, OpTy, PlaceTy, Pointer, |
| 7 | +}; |
| 8 | +use rustc_data_structures::fx::FxHashSet; |
4 | 9 | use rustc_index::bit_set::BitSet;
|
5 | 10 | use rustc_index::IndexVec;
|
6 | 11 | use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
|
7 | 12 | use rustc_middle::mir::*;
|
8 |
| -use rustc_middle::ty::{ParamEnv, TyCtxt}; |
| 13 | +use rustc_middle::query::TyCtxtAt; |
| 14 | +use rustc_middle::ty::layout::TyAndLayout; |
| 15 | +use rustc_middle::ty::{self, ParamEnv, TyCtxt}; |
| 16 | +use rustc_span::def_id::DefId; |
9 | 17 | use rustc_target::abi::Size;
|
| 18 | +use rustc_target::spec::abi::Abi as CallAbi; |
10 | 19 |
|
11 | 20 | /// The maximum number of bytes that we'll allocate space for a local or the return value.
|
12 | 21 | /// Needed for #66397, because otherwise we eval into large places and that can cause OOM or just
|
@@ -40,6 +49,162 @@ pub(crate) macro throw_machine_stop_str($($tt:tt)*) {{
|
40 | 49 | throw_machine_stop!(Zst)
|
41 | 50 | }}
|
42 | 51 |
|
| 52 | +pub(crate) struct ConstPropMachine<'mir, 'tcx> { |
| 53 | + /// The virtual call stack. |
| 54 | + stack: Vec<Frame<'mir, 'tcx>>, |
| 55 | + pub written_only_inside_own_block_locals: FxHashSet<Local>, |
| 56 | + pub can_const_prop: IndexVec<Local, ConstPropMode>, |
| 57 | +} |
| 58 | + |
| 59 | +impl ConstPropMachine<'_, '_> { |
| 60 | + pub fn new(can_const_prop: IndexVec<Local, ConstPropMode>) -> Self { |
| 61 | + Self { |
| 62 | + stack: Vec::new(), |
| 63 | + written_only_inside_own_block_locals: Default::default(), |
| 64 | + can_const_prop, |
| 65 | + } |
| 66 | + } |
| 67 | +} |
| 68 | + |
| 69 | +impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine<'mir, 'tcx> { |
| 70 | + compile_time_machine!(<'mir, 'tcx>); |
| 71 | + |
| 72 | + const PANIC_ON_ALLOC_FAIL: bool = true; // all allocations are small (see `MAX_ALLOC_LIMIT`) |
| 73 | + |
| 74 | + const POST_MONO_CHECKS: bool = false; // this MIR is still generic! |
| 75 | + |
| 76 | + type MemoryKind = !; |
| 77 | + |
| 78 | + #[inline(always)] |
| 79 | + fn enforce_alignment(_ecx: &InterpCx<'mir, 'tcx, Self>) -> bool { |
| 80 | + false // no reason to enforce alignment |
| 81 | + } |
| 82 | + |
| 83 | + #[inline(always)] |
| 84 | + fn enforce_validity(_ecx: &InterpCx<'mir, 'tcx, Self>, _layout: TyAndLayout<'tcx>) -> bool { |
| 85 | + false // for now, we don't enforce validity |
| 86 | + } |
| 87 | + |
| 88 | + fn load_mir( |
| 89 | + _ecx: &InterpCx<'mir, 'tcx, Self>, |
| 90 | + _instance: ty::InstanceDef<'tcx>, |
| 91 | + ) -> InterpResult<'tcx, &'tcx Body<'tcx>> { |
| 92 | + throw_machine_stop_str!("calling functions isn't supported in ConstProp") |
| 93 | + } |
| 94 | + |
| 95 | + fn panic_nounwind(_ecx: &mut InterpCx<'mir, 'tcx, Self>, _msg: &str) -> InterpResult<'tcx> { |
| 96 | + throw_machine_stop_str!("panicking isn't supported in ConstProp") |
| 97 | + } |
| 98 | + |
| 99 | + fn find_mir_or_eval_fn( |
| 100 | + _ecx: &mut InterpCx<'mir, 'tcx, Self>, |
| 101 | + _instance: ty::Instance<'tcx>, |
| 102 | + _abi: CallAbi, |
| 103 | + _args: &[FnArg<'tcx>], |
| 104 | + _destination: &PlaceTy<'tcx>, |
| 105 | + _target: Option<BasicBlock>, |
| 106 | + _unwind: UnwindAction, |
| 107 | + ) -> InterpResult<'tcx, Option<(&'mir Body<'tcx>, ty::Instance<'tcx>)>> { |
| 108 | + Ok(None) |
| 109 | + } |
| 110 | + |
| 111 | + fn call_intrinsic( |
| 112 | + _ecx: &mut InterpCx<'mir, 'tcx, Self>, |
| 113 | + _instance: ty::Instance<'tcx>, |
| 114 | + _args: &[OpTy<'tcx>], |
| 115 | + _destination: &PlaceTy<'tcx>, |
| 116 | + _target: Option<BasicBlock>, |
| 117 | + _unwind: UnwindAction, |
| 118 | + ) -> InterpResult<'tcx> { |
| 119 | + throw_machine_stop_str!("calling intrinsics isn't supported in ConstProp") |
| 120 | + } |
| 121 | + |
| 122 | + fn assert_panic( |
| 123 | + _ecx: &mut InterpCx<'mir, 'tcx, Self>, |
| 124 | + _msg: &rustc_middle::mir::AssertMessage<'tcx>, |
| 125 | + _unwind: rustc_middle::mir::UnwindAction, |
| 126 | + ) -> InterpResult<'tcx> { |
| 127 | + bug!("panics terminators are not evaluated in ConstProp") |
| 128 | + } |
| 129 | + |
| 130 | + fn binary_ptr_op( |
| 131 | + _ecx: &InterpCx<'mir, 'tcx, Self>, |
| 132 | + _bin_op: BinOp, |
| 133 | + _left: &ImmTy<'tcx>, |
| 134 | + _right: &ImmTy<'tcx>, |
| 135 | + ) -> InterpResult<'tcx, (ImmTy<'tcx>, bool)> { |
| 136 | + // We can't do this because aliasing of memory can differ between const eval and llvm |
| 137 | + throw_machine_stop_str!("pointer arithmetic or comparisons aren't supported in ConstProp") |
| 138 | + } |
| 139 | + |
| 140 | + fn before_access_local_mut<'a>( |
| 141 | + ecx: &'a mut InterpCx<'mir, 'tcx, Self>, |
| 142 | + frame: usize, |
| 143 | + local: Local, |
| 144 | + ) -> InterpResult<'tcx> { |
| 145 | + assert_eq!(frame, 0); |
| 146 | + match ecx.machine.can_const_prop[local] { |
| 147 | + ConstPropMode::NoPropagation => { |
| 148 | + throw_machine_stop_str!( |
| 149 | + "tried to write to a local that is marked as not propagatable" |
| 150 | + ) |
| 151 | + } |
| 152 | + ConstPropMode::OnlyInsideOwnBlock => { |
| 153 | + ecx.machine.written_only_inside_own_block_locals.insert(local); |
| 154 | + } |
| 155 | + ConstPropMode::FullConstProp => {} |
| 156 | + } |
| 157 | + Ok(()) |
| 158 | + } |
| 159 | + |
| 160 | + fn before_access_global( |
| 161 | + _tcx: TyCtxtAt<'tcx>, |
| 162 | + _machine: &Self, |
| 163 | + _alloc_id: AllocId, |
| 164 | + alloc: ConstAllocation<'tcx>, |
| 165 | + _static_def_id: Option<DefId>, |
| 166 | + is_write: bool, |
| 167 | + ) -> InterpResult<'tcx> { |
| 168 | + if is_write { |
| 169 | + throw_machine_stop_str!("can't write to global"); |
| 170 | + } |
| 171 | + // If the static allocation is mutable, then we can't const prop it as its content |
| 172 | + // might be different at runtime. |
| 173 | + if alloc.inner().mutability.is_mut() { |
| 174 | + throw_machine_stop_str!("can't access mutable globals in ConstProp"); |
| 175 | + } |
| 176 | + |
| 177 | + Ok(()) |
| 178 | + } |
| 179 | + |
| 180 | + #[inline(always)] |
| 181 | + fn expose_ptr(_ecx: &mut InterpCx<'mir, 'tcx, Self>, _ptr: Pointer) -> InterpResult<'tcx> { |
| 182 | + throw_machine_stop_str!("exposing pointers isn't supported in ConstProp") |
| 183 | + } |
| 184 | + |
| 185 | + #[inline(always)] |
| 186 | + fn init_frame_extra( |
| 187 | + _ecx: &mut InterpCx<'mir, 'tcx, Self>, |
| 188 | + frame: Frame<'mir, 'tcx>, |
| 189 | + ) -> InterpResult<'tcx, Frame<'mir, 'tcx>> { |
| 190 | + Ok(frame) |
| 191 | + } |
| 192 | + |
| 193 | + #[inline(always)] |
| 194 | + fn stack<'a>( |
| 195 | + ecx: &'a InterpCx<'mir, 'tcx, Self>, |
| 196 | + ) -> &'a [Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>] { |
| 197 | + &ecx.machine.stack |
| 198 | + } |
| 199 | + |
| 200 | + #[inline(always)] |
| 201 | + fn stack_mut<'a>( |
| 202 | + ecx: &'a mut InterpCx<'mir, 'tcx, Self>, |
| 203 | + ) -> &'a mut Vec<Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>> { |
| 204 | + &mut ecx.machine.stack |
| 205 | + } |
| 206 | +} |
| 207 | + |
43 | 208 | /// The mode that `ConstProp` is allowed to run in for a given `Local`.
|
44 | 209 | #[derive(Clone, Copy, Debug, PartialEq)]
|
45 | 210 | pub enum ConstPropMode {
|
|
0 commit comments