//! for further information.
use std::cell::RefCell;
-use std::collections::{HashMap, HashSet};
use std::fmt;
use std::num::NonZeroU64;
use std::rc::Rc;
-use rustc_hir::Mutability;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc::mir::RetagKind;
use rustc::ty::{self, layout::Size};
-use rustc_mir::interpret::InterpError;
+use rustc_hir::Mutability;
use crate::*;
/// Table storing the "base" tag for each allocation.
/// The base tag is the one used for the initial pointer.
/// We need this in a separate table to handle cyclic statics.
- base_ptr_ids: HashMap<AllocId, Tag>,
+ base_ptr_ids: FxHashMap<AllocId, Tag>,
/// Next unused call ID (for protectors).
next_call_id: CallId,
/// Those call IDs corresponding to functions that are still running.
- active_calls: HashSet<CallId>,
+ active_calls: FxHashSet<CallId>,
/// The id to trace in this execution run
tracked_pointer_tag: Option<PtrId>,
}
pub fn new(tracked_pointer_tag: Option<PtrId>) -> Self {
GlobalState {
next_ptr_id: NonZeroU64::new(1).unwrap(),
- base_ptr_ids: HashMap::default(),
+ base_ptr_ids: FxHashMap::default(),
next_call_id: NonZeroU64::new(1).unwrap(),
- active_calls: HashSet::default(),
+ active_calls: FxHashSet::default(),
tracked_pointer_tag,
}
}
self.active_calls.contains(&id)
}
- pub fn static_base_ptr(&mut self, id: AllocId) -> Tag {
+ pub fn global_base_ptr(&mut self, id: AllocId) -> Tag {
self.base_ptr_ids.get(&id).copied().unwrap_or_else(|| {
let tag = Tag::Tagged(self.new_ptr());
trace!("New allocation {:?} has base tag {:?}", id, tag);
}
}
+/// Error reporting
+fn err_sb_ub(msg: String) -> InterpError<'static> {
+ err_machine_stop!(TerminationInfo::ExperimentalUb {
+ msg,
+ url: format!("https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md"),
+ })
+}
+
// # Stacked Borrows Core Begin
/// We need to make at least the following things true:
fn check_protector(item: &Item, tag: Option<Tag>, global: &GlobalState) -> InterpResult<'tcx> {
if let Tag::Tagged(id) = item.tag {
if Some(id) == global.tracked_pointer_tag {
- register_err(
- InterpError::MachineStop(Box::new(TerminationInfo::PoppedTrackedPointerTag(
- item.clone(),
- )))
- .into(),
- );
+ register_diagnostic(NonHaltingDiagnostic::PoppedTrackedPointerTag(item.clone()));
}
}
if let Some(call) = item.protector {
if global.is_active(call) {
if let Some(tag) = tag {
- throw_ub!(UbExperimental(format!(
+ Err(err_sb_ub(format!(
"not granting access to tag {:?} because incompatible item is protected: {:?}",
tag, item
- )));
+ )))?
} else {
- throw_ub!(UbExperimental(format!(
+ Err(err_sb_ub(format!(
"deallocating while item is protected: {:?}",
item
- )));
+ )))?
}
}
}
// Two main steps: Find granting item, remove incompatible items above.
// Step 1: Find granting item.
- let granting_idx = self.find_granting(access, tag).ok_or_else(|| err_ub!(UbExperimental(
- format!("no item granting {} to tag {:?} found in borrow stack.", access, tag),
- )))?;
+ let granting_idx = self.find_granting(access, tag).ok_or_else(|| {
+ err_sb_ub(format!(
+ "no item granting {} to tag {:?} found in borrow stack.",
+ access, tag
+ ))
+ })?;
// Step 2: Remove incompatible items above them. Make sure we do not remove protected
// items. Behavior differs for reads and writes.
/// active protectors at all because we will remove all items.
fn dealloc(&mut self, tag: Tag, global: &GlobalState) -> InterpResult<'tcx> {
// Step 1: Find granting item.
- self.find_granting(AccessKind::Write, tag).ok_or_else(|| err_ub!(UbExperimental(format!(
- "no item granting write access for deallocation to tag {:?} found in borrow stack",
- tag,
- ))))?;
+ self.find_granting(AccessKind::Write, tag).ok_or_else(|| {
+ err_sb_ub(format!(
+ "no item granting write access for deallocation to tag {:?} found in borrow stack",
+ tag,
+ ))
+ })?;
// Step 2: Remove all items. Also checks for protectors.
for item in self.borrows.drain(..).rev() {
// Now we figure out which item grants our parent (`derived_from`) this kind of access.
// We use that to determine where to put the new item.
let granting_idx = self.find_granting(access, derived_from)
- .ok_or_else(|| err_ub!(UbExperimental(format!(
+ .ok_or_else(|| err_sb_ub(format!(
"trying to reborrow for {:?}, but parent tag {:?} does not have an appropriate item in the borrow stack",
new.perm, derived_from,
- ))))?;
+ )))?;
// Compute where to put the new item.
// Either way, we ensure that we insert the new item in a way such that between
// everything else off the stack, invalidating all previous pointers,
// and in particular, *all* raw pointers.
MemoryKind::Stack => (Tag::Tagged(extra.borrow_mut().new_ptr()), Permission::Unique),
- // Static memory can be referenced by "global" pointers from `tcx`.
- // Thus we call `static_base_ptr` such that the global pointers get the same tag
+ // Global memory can be referenced by global pointers from `tcx`.
+ // Thus we call `global_base_ptr` such that the global pointers get the same tag
// as what we use here.
+ // `Machine` is used for extern statics, and thus must also be listed here.
+ // `Env` we list because we can get away with precise tracking there.
// The base pointer is not unique, so the base permission is `SharedReadWrite`.
- MemoryKind::Machine(MiriMemoryKind::Static) =>
- (extra.borrow_mut().static_base_ptr(id), Permission::SharedReadWrite),
+ MemoryKind::Machine(MiriMemoryKind::Global | MiriMemoryKind::Machine | MiriMemoryKind::Env) =>
+ (extra.borrow_mut().global_base_ptr(id), Permission::SharedReadWrite),
// Everything else we handle entirely untagged for now.
// FIXME: experiment with more precise tracking.
_ => (Tag::Untagged, Permission::SharedReadWrite),
// breaking `Rc::from_raw`.
RefKind::Raw { .. } => Tag::Untagged,
// All other pointesr are properly tracked.
- _ => Tag::Tagged(this.memory.extra.stacked_borrows.borrow_mut().new_ptr()),
+ _ => Tag::Tagged(
+ this.memory.extra.stacked_borrows.as_ref().unwrap().borrow_mut().new_ptr(),
+ ),
};
// Reborrow.
Ok(())
}
}
-