use super::{
Pointer, AllocId, Allocation, GlobalId, AllocationExtra,
- EvalResult, Scalar, InterpError, GlobalAlloc, PointerArithmetic,
+ InterpResult, Scalar, InterpError, GlobalAlloc, PointerArithmetic,
Machine, AllocMap, MayLeak, ErrorHandled, CheckInAllocMsg, InboundsCheck,
};
}
}
+ #[inline]
+ pub fn tag_static_base_pointer(&self, ptr: Pointer) -> Pointer<M::PointerTag> {
+ ptr.with_tag(M::tag_static_base_pointer(ptr.alloc_id, &self.extra))
+ }
+
pub fn create_fn_alloc(&mut self, instance: Instance<'tcx>) -> Pointer<M::PointerTag> {
- // Default tag is okay because anyway you cannot access memory with this.
- Pointer::from(self.tcx.alloc_map.lock().create_fn_alloc(instance)).with_default_tag()
+ let id = self.tcx.alloc_map.lock().create_fn_alloc(instance);
+ self.tag_static_base_pointer(Pointer::from(id))
}
- pub fn allocate_static_bytes(&mut self, bytes: &[u8]) -> Pointer {
- Pointer::from(self.tcx.allocate_bytes(bytes))
+ pub fn allocate(
+ &mut self,
+ size: Size,
+ align: Align,
+ kind: MemoryKind<M::MemoryKinds>,
+ ) -> Pointer<M::PointerTag> {
+ let alloc = Allocation::undef(size, align);
+ self.allocate_with(alloc, kind)
}
- pub fn allocate_with(
+ pub fn allocate_static_bytes(
&mut self,
- alloc: Allocation<M::PointerTag, M::AllocExtra>,
+ bytes: &[u8],
kind: MemoryKind<M::MemoryKinds>,
- ) -> AllocId {
- let id = self.tcx.alloc_map.lock().reserve();
- self.alloc_map.insert(id, (kind, alloc));
- id
+ ) -> Pointer<M::PointerTag> {
+ let alloc = Allocation::from_byte_aligned_bytes(bytes);
+ self.allocate_with(alloc, kind)
}
- pub fn allocate(
+ pub fn allocate_with(
&mut self,
- size: Size,
- align: Align,
+ alloc: Allocation,
kind: MemoryKind<M::MemoryKinds>,
) -> Pointer<M::PointerTag> {
- let (extra, tag) = M::new_allocation(size, &self.extra, kind);
- Pointer::from(self.allocate_with(Allocation::undef(size, align, extra), kind)).with_tag(tag)
+ let id = self.tcx.alloc_map.lock().reserve();
+ let (alloc, tag) = M::tag_allocation(id, Cow::Owned(alloc), Some(kind), &self.extra);
+ self.alloc_map.insert(id, (kind, alloc.into_owned()));
+ Pointer::from(id).with_tag(tag)
}
pub fn reallocate(
new_size: Size,
new_align: Align,
kind: MemoryKind<M::MemoryKinds>,
- ) -> EvalResult<'tcx, Pointer<M::PointerTag>> {
+ ) -> InterpResult<'tcx, Pointer<M::PointerTag>> {
if ptr.offset.bytes() != 0 {
return err!(ReallocateNonBasePtr);
}
}
/// Deallocate a local, or do nothing if that local has been made into a static
- pub fn deallocate_local(&mut self, ptr: Pointer<M::PointerTag>) -> EvalResult<'tcx> {
+ pub fn deallocate_local(&mut self, ptr: Pointer<M::PointerTag>) -> InterpResult<'tcx> {
// The allocation might be already removed by static interning.
// This can only really happen in the CTFE instance, not in miri.
if self.alloc_map.contains_key(&ptr.alloc_id) {
ptr: Pointer<M::PointerTag>,
size_and_align: Option<(Size, Align)>,
kind: MemoryKind<M::MemoryKinds>,
- ) -> EvalResult<'tcx> {
+ ) -> InterpResult<'tcx> {
trace!("deallocating: {}", ptr.alloc_id);
if ptr.offset.bytes() != 0 {
&self,
ptr: Scalar<M::PointerTag>,
required_align: Align
- ) -> EvalResult<'tcx> {
+ ) -> InterpResult<'tcx> {
// Check non-NULL/Undef, extract offset
let (offset, alloc_align) = match ptr.to_bits_or_ptr(self.pointer_size(), self) {
Err(ptr) => {
ptr: Pointer<M::PointerTag>,
liveness: InboundsCheck,
msg: CheckInAllocMsg,
- ) -> EvalResult<'tcx, Align> {
+ ) -> InterpResult<'tcx, Align> {
let (allocation_size, align) = self.get_size_and_align(ptr.alloc_id, liveness)?;
ptr.check_in_alloc(allocation_size, msg)?;
Ok(align)
/// This attempts to return a reference to an existing allocation if
/// one can be found in `tcx`. That, however, is only possible if `tcx` and
/// this machine use the same pointer tag, so it is indirected through
- /// `M::static_with_default_tag`.
+ /// `M::tag_allocation`.
+ ///
+ /// Notice that every static has two `AllocId` that will resolve to the same
+ /// thing here: one maps to `GlobalAlloc::Static`, this is the "lazy" ID,
+ /// and the other one is maps to `GlobalAlloc::Memory`, this is returned by
+ /// `const_eval_raw` and it is the "resolved" ID.
+ /// The resolved ID is never used by the interpreted progrma, it is hidden.
+ /// The `GlobalAlloc::Memory` branch here is still reachable though; when a static
+ /// contains a reference to memory that was created during its evaluation (i.e., not to
+ /// another static), those inner references only exist in "resolved" form.
fn get_static_alloc(
id: AllocId,
tcx: TyCtxtAt<'a, 'tcx, 'tcx>,
memory_extra: &M::MemoryExtra,
- ) -> EvalResult<'tcx, Cow<'tcx, Allocation<M::PointerTag, M::AllocExtra>>> {
+ ) -> InterpResult<'tcx, Cow<'tcx, Allocation<M::PointerTag, M::AllocExtra>>> {
let alloc = tcx.alloc_map.lock().get(id);
- let def_id = match alloc {
- Some(GlobalAlloc::Memory(mem)) => {
- // We got tcx memory. Let the machine figure out whether and how to
- // turn that into memory with the right pointer tag.
- return Ok(M::adjust_static_allocation(mem, memory_extra))
- }
- Some(GlobalAlloc::Function(..)) => {
- return err!(DerefFunctionPointer)
- }
- Some(GlobalAlloc::Static(did)) => {
- did
- }
+ let alloc = match alloc {
+ Some(GlobalAlloc::Memory(mem)) =>
+ Cow::Borrowed(mem),
+ Some(GlobalAlloc::Function(..)) =>
+ return err!(DerefFunctionPointer),
None =>
return err!(DanglingPointerDeref),
- };
- // We got a "lazy" static that has not been computed yet, do some work
- trace!("static_alloc: Need to compute {:?}", def_id);
- if tcx.is_foreign_item(def_id) {
- return M::find_foreign_static(def_id, tcx, memory_extra);
- }
- let instance = Instance::mono(tcx.tcx, def_id);
- let gid = GlobalId {
- instance,
- promoted: None,
- };
- // use the raw query here to break validation cycles. Later uses of the static will call the
- // full query anyway
- tcx.const_eval_raw(ty::ParamEnv::reveal_all().and(gid)).map_err(|err| {
- // no need to report anything, the const_eval call takes care of that for statics
- assert!(tcx.is_static(def_id));
- match err {
- ErrorHandled::Reported => InterpError::ReferencedConstant.into(),
- ErrorHandled::TooGeneric => InterpError::TooGeneric.into(),
+ Some(GlobalAlloc::Static(def_id)) => {
+ // We got a "lazy" static that has not been computed yet.
+ if tcx.is_foreign_item(def_id) {
+ trace!("static_alloc: foreign item {:?}", def_id);
+ M::find_foreign_static(def_id, tcx)?
+ } else {
+ trace!("static_alloc: Need to compute {:?}", def_id);
+ let instance = Instance::mono(tcx.tcx, def_id);
+ let gid = GlobalId {
+ instance,
+ promoted: None,
+ };
+ // use the raw query here to break validation cycles. Later uses of the static
+ // will call the full query anyway
+ let raw_const = tcx.const_eval_raw(ty::ParamEnv::reveal_all().and(gid))
+ .map_err(|err| {
+ // no need to report anything, the const_eval call takes care of that
+ // for statics
+ assert!(tcx.is_static(def_id));
+ match err {
+ ErrorHandled::Reported => InterpError::ReferencedConstant,
+ ErrorHandled::TooGeneric => InterpError::TooGeneric,
+ }
+ })?;
+ // Make sure we use the ID of the resolved memory, not the lazy one!
+ let id = raw_const.alloc_id;
+ let allocation = tcx.alloc_map.lock().unwrap_memory(id);
+ Cow::Borrowed(allocation)
+ }
}
- }).map(|raw_const| {
- let allocation = tcx.alloc_map.lock().unwrap_memory(raw_const.alloc_id);
- // We got tcx memory. Let the machine figure out whether and how to
- // turn that into memory with the right pointer tag.
- M::adjust_static_allocation(allocation, memory_extra)
- })
+ };
+ // We got tcx memory. Let the machine figure out whether and how to
+ // turn that into memory with the right pointer tag.
+ Ok(M::tag_allocation(
+ id, // always use the ID we got as input, not the "hidden" one.
+ alloc,
+ M::STATIC_KIND.map(MemoryKind::Machine),
+ memory_extra
+ ).0)
}
- pub fn get(&self, id: AllocId) -> EvalResult<'tcx, &Allocation<M::PointerTag, M::AllocExtra>> {
+ pub fn get(
+ &self,
+ id: AllocId,
+ ) -> InterpResult<'tcx, &Allocation<M::PointerTag, M::AllocExtra>> {
// The error type of the inner closure here is somewhat funny. We have two
// ways of "erroring": An actual error, or because we got a reference from
// `get_static_alloc` that we can actually use directly without inserting anything anywhere.
- // So the error type is `EvalResult<'tcx, &Allocation<M::PointerTag>>`.
+ // So the error type is `InterpResult<'tcx, &Allocation<M::PointerTag>>`.
let a = self.alloc_map.get_or(id, || {
let alloc = Self::get_static_alloc(id, self.tcx, &self.extra).map_err(Err)?;
match alloc {
pub fn get_mut(
&mut self,
id: AllocId,
- ) -> EvalResult<'tcx, &mut Allocation<M::PointerTag, M::AllocExtra>> {
+ ) -> InterpResult<'tcx, &mut Allocation<M::PointerTag, M::AllocExtra>> {
let tcx = self.tcx;
let memory_extra = &self.extra;
let a = self.alloc_map.get_mut_or(id, || {
&self,
id: AllocId,
liveness: InboundsCheck,
- ) -> EvalResult<'static, (Size, Align)> {
+ ) -> InterpResult<'static, (Size, Align)> {
if let Ok(alloc) = self.get(id) {
return Ok((Size::from_bytes(alloc.bytes.len() as u64), alloc.align));
}
}
}
- pub fn get_fn(&self, ptr: Pointer<M::PointerTag>) -> EvalResult<'tcx, Instance<'tcx>> {
+ pub fn get_fn(&self, ptr: Pointer<M::PointerTag>) -> InterpResult<'tcx, Instance<'tcx>> {
if ptr.offset.bytes() != 0 {
return err!(InvalidFunctionPointer);
}
}
}
- pub fn mark_immutable(&mut self, id: AllocId) -> EvalResult<'tcx> {
+ pub fn mark_immutable(&mut self, id: AllocId) -> InterpResult<'tcx> {
self.get_mut(id)?.mutability = Mutability::Immutable;
Ok(())
}
&self,
ptr: Scalar<M::PointerTag>,
size: Size,
- ) -> EvalResult<'tcx, &[u8]> {
+ ) -> InterpResult<'tcx, &[u8]> {
if size.bytes() == 0 {
Ok(&[])
} else {
&mut self,
alloc_id: AllocId,
mutability: Mutability,
- ) -> EvalResult<'tcx> {
+ ) -> InterpResult<'tcx> {
trace!(
"mark_static_initialized {:?}, mutability: {:?}",
alloc_id,
dest_align: Align,
size: Size,
nonoverlapping: bool,
- ) -> EvalResult<'tcx> {
+ ) -> InterpResult<'tcx> {
self.copy_repeatedly(src, src_align, dest, dest_align, size, 1, nonoverlapping)
}
size: Size,
length: u64,
nonoverlapping: bool,
- ) -> EvalResult<'tcx> {
+ ) -> InterpResult<'tcx> {
self.check_align(src, src_align)?;
self.check_align(dest, dest_align)?;
if size.bytes() == 0 {
dest: Pointer<M::PointerTag>,
size: Size,
repeat: u64,
- ) -> EvalResult<'tcx> {
+ ) -> InterpResult<'tcx> {
// The bits have to be saved locally before writing to dest in case src and dest overlap.
assert_eq!(size.bytes() as usize as u64, size.bytes());