rustc_middle/mir/interpret/
mod.rs1#[macro_use]
4mod error;
5
6mod allocation;
7mod pointer;
8mod queries;
9mod value;
10
11use std::io::{Read, Write};
12use std::num::NonZero;
13use std::{fmt, io};
14
15use rustc_abi::{AddressSpace, Align, Endian, HasDataLayout, Size};
16use rustc_ast::{LitKind, Mutability};
17use rustc_data_structures::fx::FxHashMap;
18use rustc_data_structures::sharded::ShardedHashMap;
19use rustc_data_structures::sync::{AtomicU64, Lock};
20use rustc_hir::def::DefKind;
21use rustc_hir::def_id::{DefId, LocalDefId};
22use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable};
23use rustc_serialize::{Decodable, Encodable};
24use tracing::{debug, trace};
25pub use {
27    err_exhaust, err_inval, err_machine_stop, err_ub, err_ub_custom, err_ub_format, err_unsup,
28    err_unsup_format, throw_exhaust, throw_inval, throw_machine_stop, throw_ub, throw_ub_custom,
29    throw_ub_format, throw_unsup, throw_unsup_format,
30};
31
32pub use self::allocation::{
33    AllocBytes, AllocError, AllocInit, AllocRange, AllocResult, Allocation, ConstAllocation,
34    InitChunk, InitChunkIter, alloc_range,
35};
36pub use self::error::{
37    BadBytesAccess, CheckAlignMsg, CheckInAllocMsg, ErrorHandled, EvalStaticInitializerRawResult,
38    EvalToAllocationRawResult, EvalToConstValueResult, EvalToValTreeResult, ExpectedKind,
39    InterpErrorInfo, InterpErrorKind, InterpResult, InvalidMetaKind, InvalidProgramInfo,
40    MachineStopType, Misalignment, PointerKind, ReportedErrorInfo, ResourceExhaustionInfo,
41    ScalarSizeMismatch, UndefinedBehaviorInfo, UnsupportedOpInfo, ValTreeCreationError,
42    ValidationErrorInfo, ValidationErrorKind, interp_ok,
43};
44pub use self::pointer::{CtfeProvenance, Pointer, PointerArithmetic, Provenance};
45pub use self::value::Scalar;
46use crate::mir;
47use crate::ty::codec::{TyDecoder, TyEncoder};
48use crate::ty::print::with_no_trimmed_paths;
49use crate::ty::{self, Instance, Ty, TyCtxt};
50
51#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, TyEncodable, TyDecodable)]
55#[derive(HashStable, TypeFoldable, TypeVisitable)]
56pub struct GlobalId<'tcx> {
57    pub instance: ty::Instance<'tcx>,
60
61    pub promoted: Option<mir::Promoted>,
63}
64
65impl<'tcx> GlobalId<'tcx> {
66    pub fn display(self, tcx: TyCtxt<'tcx>) -> String {
67        let instance_name = with_no_trimmed_paths!(tcx.def_path_str(self.instance.def.def_id()));
68        if let Some(promoted) = self.promoted {
69            format!("{instance_name}::{promoted:?}")
70        } else {
71            instance_name
72        }
73    }
74}
75
76#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, HashStable)]
78pub struct LitToConstInput<'tcx> {
79    pub lit: LitKind,
81    pub ty: Ty<'tcx>,
83    pub neg: bool,
85}
86
87#[derive(Copy, Clone, Eq, Hash, Ord, PartialEq, PartialOrd)]
88pub struct AllocId(pub NonZero<u64>);
89
90impl fmt::Debug for AllocId {
93    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
94        if f.alternate() { write!(f, "a{}", self.0) } else { write!(f, "alloc{}", self.0) }
95    }
96}
97
98#[derive(TyDecodable, TyEncodable)]
101enum AllocDiscriminant {
102    Alloc,
103    Fn,
104    VTable,
105    Static,
106    Type,
107}
108
109pub fn specialized_encode_alloc_id<'tcx, E: TyEncoder<'tcx>>(
110    encoder: &mut E,
111    tcx: TyCtxt<'tcx>,
112    alloc_id: AllocId,
113) {
114    match tcx.global_alloc(alloc_id) {
115        GlobalAlloc::Memory(alloc) => {
116            trace!("encoding {:?} with {:#?}", alloc_id, alloc);
117            AllocDiscriminant::Alloc.encode(encoder);
118            alloc.encode(encoder);
119        }
120        GlobalAlloc::Function { instance } => {
121            trace!("encoding {:?} with {:#?}", alloc_id, instance);
122            AllocDiscriminant::Fn.encode(encoder);
123            instance.encode(encoder);
124        }
125        GlobalAlloc::VTable(ty, poly_trait_ref) => {
126            trace!("encoding {:?} with {ty:#?}, {poly_trait_ref:#?}", alloc_id);
127            AllocDiscriminant::VTable.encode(encoder);
128            ty.encode(encoder);
129            poly_trait_ref.encode(encoder);
130        }
131        GlobalAlloc::TypeId { ty } => {
132            trace!("encoding {alloc_id:?} with {ty:#?}");
133            AllocDiscriminant::Type.encode(encoder);
134            ty.encode(encoder);
135        }
136        GlobalAlloc::Static(did) => {
137            assert!(!tcx.is_thread_local_static(did));
138            AllocDiscriminant::Static.encode(encoder);
141            Encodable::<E>::encode(&did, encoder);
144        }
145    }
146}
147
148#[derive(Clone)]
149enum State {
150    Empty,
151    Done(AllocId),
152}
153
154pub struct AllocDecodingState {
155    decoding_state: Vec<Lock<State>>,
157    data_offsets: Vec<u64>,
159}
160
161impl AllocDecodingState {
162    #[inline]
163    pub fn new_decoding_session(&self) -> AllocDecodingSession<'_> {
164        AllocDecodingSession { state: self }
165    }
166
167    pub fn new(data_offsets: Vec<u64>) -> Self {
168        let decoding_state =
169            std::iter::repeat_with(|| Lock::new(State::Empty)).take(data_offsets.len()).collect();
170
171        Self { decoding_state, data_offsets }
172    }
173}
174
175#[derive(Copy, Clone)]
176pub struct AllocDecodingSession<'s> {
177    state: &'s AllocDecodingState,
178}
179
180impl<'s> AllocDecodingSession<'s> {
181    pub fn decode_alloc_id<'tcx, D>(&self, decoder: &mut D) -> AllocId
183    where
184        D: TyDecoder<'tcx>,
185    {
186        let idx = usize::try_from(decoder.read_u32()).unwrap();
188        let pos = usize::try_from(self.state.data_offsets[idx]).unwrap();
189
190        let (alloc_kind, pos) = decoder.with_position(pos, |decoder| {
193            let alloc_kind = AllocDiscriminant::decode(decoder);
194            (alloc_kind, decoder.position())
195        });
196
197        let mut entry = self.state.decoding_state[idx].lock();
210        if let State::Done(alloc_id) = *entry {
213            return alloc_id;
214        }
215
216        let alloc_id = decoder.with_position(pos, |decoder| match alloc_kind {
218            AllocDiscriminant::Alloc => {
219                trace!("creating memory alloc ID");
220                let alloc = <ConstAllocation<'tcx> as Decodable<_>>::decode(decoder);
221                trace!("decoded alloc {:?}", alloc);
222                decoder.interner().reserve_and_set_memory_alloc(alloc)
223            }
224            AllocDiscriminant::Fn => {
225                trace!("creating fn alloc ID");
226                let instance = ty::Instance::decode(decoder);
227                trace!("decoded fn alloc instance: {:?}", instance);
228                decoder.interner().reserve_and_set_fn_alloc(instance, CTFE_ALLOC_SALT)
229            }
230            AllocDiscriminant::VTable => {
231                trace!("creating vtable alloc ID");
232                let ty = Decodable::decode(decoder);
233                let poly_trait_ref = Decodable::decode(decoder);
234                trace!("decoded vtable alloc instance: {ty:?}, {poly_trait_ref:?}");
235                decoder.interner().reserve_and_set_vtable_alloc(ty, poly_trait_ref, CTFE_ALLOC_SALT)
236            }
237            AllocDiscriminant::Type => {
238                trace!("creating typeid alloc ID");
239                let ty = Decodable::decode(decoder);
240                trace!("decoded typid: {ty:?}");
241                decoder.interner().reserve_and_set_type_id_alloc(ty)
242            }
243            AllocDiscriminant::Static => {
244                trace!("creating extern static alloc ID");
245                let did = <DefId as Decodable<D>>::decode(decoder);
246                trace!("decoded static def-ID: {:?}", did);
247                decoder.interner().reserve_and_set_static_alloc(did)
248            }
249        });
250
251        *entry = State::Done(alloc_id);
252
253        alloc_id
254    }
255}
256
257#[derive(Debug, Clone, Eq, PartialEq, Hash, TyDecodable, TyEncodable, HashStable)]
260pub enum GlobalAlloc<'tcx> {
261    Function { instance: Instance<'tcx> },
263    VTable(Ty<'tcx>, &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>),
268    Static(DefId),
271    Memory(ConstAllocation<'tcx>),
273    TypeId { ty: Ty<'tcx> },
276}
277
278impl<'tcx> GlobalAlloc<'tcx> {
279    #[track_caller]
281    #[inline]
282    pub fn unwrap_memory(&self) -> ConstAllocation<'tcx> {
283        match *self {
284            GlobalAlloc::Memory(mem) => mem,
285            _ => bug!("expected memory, got {:?}", self),
286        }
287    }
288
289    #[track_caller]
291    #[inline]
292    pub fn unwrap_fn(&self) -> Instance<'tcx> {
293        match *self {
294            GlobalAlloc::Function { instance, .. } => instance,
295            _ => bug!("expected function, got {:?}", self),
296        }
297    }
298
299    #[track_caller]
301    #[inline]
302    pub fn unwrap_vtable(&self) -> (Ty<'tcx>, Option<ty::PolyExistentialTraitRef<'tcx>>) {
303        match *self {
304            GlobalAlloc::VTable(ty, dyn_ty) => (ty, dyn_ty.principal()),
305            _ => bug!("expected vtable, got {:?}", self),
306        }
307    }
308
309    #[inline]
311    pub fn address_space(&self, cx: &impl HasDataLayout) -> AddressSpace {
312        match self {
313            GlobalAlloc::Function { .. } => cx.data_layout().instruction_address_space,
314            GlobalAlloc::TypeId { .. }
315            | GlobalAlloc::Static(..)
316            | GlobalAlloc::Memory(..)
317            | GlobalAlloc::VTable(..) => AddressSpace::ZERO,
318        }
319    }
320
321    pub fn mutability(&self, tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>) -> Mutability {
322        match self {
324            GlobalAlloc::Static(did) => {
325                let DefKind::Static { safety: _, mutability, nested } = tcx.def_kind(did) else {
326                    bug!()
327                };
328                if nested {
329                    if cfg!(debug_assertions) {
332                        let alloc = tcx.eval_static_initializer(did).unwrap();
333                        assert_eq!(alloc.0.mutability, mutability);
334                    }
335                    mutability
336                } else {
337                    let mutability = match mutability {
338                        Mutability::Not
339                            if !tcx
340                                .type_of(did)
341                                .no_bound_vars()
342                                .expect("statics should not have generic parameters")
343                                .is_freeze(tcx, typing_env) =>
344                        {
345                            Mutability::Mut
346                        }
347                        _ => mutability,
348                    };
349                    mutability
350                }
351            }
352            GlobalAlloc::Memory(alloc) => alloc.inner().mutability,
353            GlobalAlloc::TypeId { .. } | GlobalAlloc::Function { .. } | GlobalAlloc::VTable(..) => {
354                Mutability::Not
356            }
357        }
358    }
359
360    pub fn size_and_align(
361        &self,
362        tcx: TyCtxt<'tcx>,
363        typing_env: ty::TypingEnv<'tcx>,
364    ) -> (Size, Align) {
365        match self {
366            GlobalAlloc::Static(def_id) => {
367                let DefKind::Static { nested, .. } = tcx.def_kind(def_id) else {
368                    bug!("GlobalAlloc::Static is not a static")
369                };
370
371                if nested {
372                    let alloc = tcx.eval_static_initializer(def_id).unwrap();
377                    (alloc.0.size(), alloc.0.align)
378                } else {
379                    let ty = tcx
384                        .type_of(def_id)
385                        .no_bound_vars()
386                        .expect("statics should not have generic parameters");
387                    let layout = tcx.layout_of(typing_env.as_query_input(ty)).unwrap();
388                    assert!(layout.is_sized());
389
390                    let align = match tcx.codegen_fn_attrs(def_id).alignment {
392                        Some(align_from_attribute) => {
393                            Ord::max(align_from_attribute, layout.align.abi)
394                        }
395                        None => layout.align.abi,
396                    };
397
398                    (layout.size, align)
399                }
400            }
401            GlobalAlloc::Memory(alloc) => {
402                let alloc = alloc.inner();
403                (alloc.size(), alloc.align)
404            }
405            GlobalAlloc::Function { .. } => (Size::ZERO, Align::ONE),
406            GlobalAlloc::VTable(..) => {
407                (Size::ZERO, tcx.data_layout.pointer_align().abi)
409            }
410            GlobalAlloc::TypeId { .. } => (Size::ZERO, Align::ONE),
412        }
413    }
414}
415
416pub const CTFE_ALLOC_SALT: usize = 0;
417
418pub(crate) struct AllocMap<'tcx> {
419    to_alloc: ShardedHashMap<AllocId, GlobalAlloc<'tcx>>,
427
428    dedup: Lock<FxHashMap<(GlobalAlloc<'tcx>, usize), AllocId>>,
433
434    next_id: AtomicU64,
437}
438
439impl<'tcx> AllocMap<'tcx> {
440    pub(crate) fn new() -> Self {
441        AllocMap {
442            to_alloc: Default::default(),
443            dedup: Default::default(),
444            next_id: AtomicU64::new(1),
445        }
446    }
447    fn reserve(&self) -> AllocId {
448        let next_id = self.next_id.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
452        AllocId(NonZero::new(next_id).unwrap())
453    }
454}
455
456impl<'tcx> TyCtxt<'tcx> {
457    pub fn reserve_alloc_id(self) -> AllocId {
463        self.alloc_map.reserve()
464    }
465
466    fn reserve_and_set_dedup(self, alloc: GlobalAlloc<'tcx>, salt: usize) -> AllocId {
469        if let GlobalAlloc::Memory(mem) = alloc {
470            if mem.inner().mutability.is_mut() {
471                bug!("trying to dedup-reserve mutable memory");
472            }
473        }
474        let alloc_salt = (alloc, salt);
475        let mut dedup = self.alloc_map.dedup.lock();
477        if let Some(&alloc_id) = dedup.get(&alloc_salt) {
478            return alloc_id;
479        }
480        let id = self.alloc_map.reserve();
481        debug!("creating alloc {:?} with id {id:?}", alloc_salt.0);
482        let had_previous = self.alloc_map.to_alloc.insert(id, alloc_salt.0.clone()).is_some();
483        assert!(!had_previous);
485        dedup.insert(alloc_salt, id);
486        id
487    }
488
489    pub fn reserve_and_set_memory_dedup(self, mem: ConstAllocation<'tcx>, salt: usize) -> AllocId {
492        self.reserve_and_set_dedup(GlobalAlloc::Memory(mem), salt)
493    }
494
495    pub fn reserve_and_set_static_alloc(self, static_id: DefId) -> AllocId {
498        let salt = 0; self.reserve_and_set_dedup(GlobalAlloc::Static(static_id), salt)
500    }
501
502    pub fn reserve_and_set_fn_alloc(self, instance: Instance<'tcx>, salt: usize) -> AllocId {
504        self.reserve_and_set_dedup(GlobalAlloc::Function { instance }, salt)
505    }
506
507    pub fn reserve_and_set_vtable_alloc(
509        self,
510        ty: Ty<'tcx>,
511        dyn_ty: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
512        salt: usize,
513    ) -> AllocId {
514        self.reserve_and_set_dedup(GlobalAlloc::VTable(ty, dyn_ty), salt)
515    }
516
517    pub fn reserve_and_set_type_id_alloc(self, ty: Ty<'tcx>) -> AllocId {
519        self.reserve_and_set_dedup(GlobalAlloc::TypeId { ty }, 0)
520    }
521
522    pub fn reserve_and_set_memory_alloc(self, mem: ConstAllocation<'tcx>) -> AllocId {
528        let id = self.reserve_alloc_id();
529        self.set_alloc_id_memory(id, mem);
530        id
531    }
532
533    #[inline]
539    pub fn try_get_global_alloc(self, id: AllocId) -> Option<GlobalAlloc<'tcx>> {
540        self.alloc_map.to_alloc.get(&id)
541    }
542
543    #[inline]
544    #[track_caller]
545    pub fn global_alloc(self, id: AllocId) -> GlobalAlloc<'tcx> {
550        match self.try_get_global_alloc(id) {
551            Some(alloc) => alloc,
552            None => bug!("could not find allocation for {id:?}"),
553        }
554    }
555
556    pub fn set_alloc_id_memory(self, id: AllocId, mem: ConstAllocation<'tcx>) {
559        if let Some(old) = self.alloc_map.to_alloc.insert(id, GlobalAlloc::Memory(mem)) {
560            bug!("tried to set allocation ID {id:?}, but it was already existing as {old:#?}");
561        }
562    }
563
564    pub fn set_nested_alloc_id_static(self, id: AllocId, def_id: LocalDefId) {
567        if let Some(old) =
568            self.alloc_map.to_alloc.insert(id, GlobalAlloc::Static(def_id.to_def_id()))
569        {
570            bug!("tried to set allocation ID {id:?}, but it was already existing as {old:#?}");
571        }
572    }
573}
574
575#[inline]
580pub fn write_target_uint(
581    endianness: Endian,
582    mut target: &mut [u8],
583    data: u128,
584) -> Result<(), io::Error> {
585    match endianness {
588        Endian::Little => target.write(&data.to_le_bytes())?,
589        Endian::Big => target.write(&data.to_be_bytes()[16 - target.len()..])?,
590    };
591    debug_assert!(target.len() == 0); Ok(())
593}
594
595#[inline]
596pub fn read_target_uint(endianness: Endian, mut source: &[u8]) -> Result<u128, io::Error> {
597    let mut buf = [0u8; size_of::<u128>()];
599    let uint = match endianness {
601        Endian::Little => {
602            source.read_exact(&mut buf[..source.len()])?;
603            Ok(u128::from_le_bytes(buf))
604        }
605        Endian::Big => {
606            source.read_exact(&mut buf[16 - source.len()..])?;
607            Ok(u128::from_be_bytes(buf))
608        }
609    };
610    debug_assert!(source.len() == 0); uint
612}