diff --git a/cargo-miri/bin.rs b/cargo-miri/bin.rs index a946f79888..8630c61592 100644 --- a/cargo-miri/bin.rs +++ b/cargo-miri/bin.rs @@ -482,6 +482,51 @@ path = "lib.rs" } } +fn collect_local_crate_names() { + #[derive(Deserialize)] + struct Metadata { + workspace_members: Vec, + } + let mut cmd = cargo(); + // `-Zunstable-options` is required by `--config`. + cmd.args(["metadata", "--no-deps", "--format-version=1", "-Zunstable-options"]); + // The `build.target-dir` config can be passed by `--config` flags, so forward them to + // `cargo metadata`. + let config_flag = "--config"; + for arg in ArgSplitFlagValue::new( + env::args().skip(3), // skip the program name, "miri" and "run" / "test" + config_flag, + ) { + if let Ok(config) = arg { + cmd.arg(config_flag).arg(config); + } + } + let mut child = cmd + .stdin(process::Stdio::null()) + .stdout(process::Stdio::piped()) + .spawn() + .expect("failed ro run `cargo metadata`"); + // Check this `Result` after `status.success()` is checked, so we don't print the error + // to stderr if `cargo metadata` is also printing to stderr. + let metadata: Result = serde_json::from_reader(child.stdout.take().unwrap()); + let status = child.wait().expect("failed to wait for `cargo metadata` to exit"); + if !status.success() { + std::process::exit(status.code().unwrap_or(-1)); + } + let metadata = metadata + .unwrap_or_else(|e| show_error(format!("invalid `cargo metadata` output: {}", e))); + assert!(metadata.workspace_members.len() > 0); + + let mut flags = env::var("MIRIFLAGS").unwrap_or_default(); + if !flags.contains("-Zmiri-local-crates") { + for member in metadata.workspace_members { + flags += &format!(" -Zmiri-local-crates={}", member.split(" ").nth(0).unwrap().replace("-", "_")); + } + } + env::set_var("MIRIFLAGS", flags); +} + + /// Detect the target directory by calling `cargo metadata`. fn detect_target_dir() -> PathBuf { #[derive(Deserialize)] @@ -595,6 +640,8 @@ fn phase_cargo_miri(mut args: env::Args) { } } + collect_local_crate_names(); + // Detect the target directory if it's not specified via `--target-dir`. let target_dir = target_dir.get_or_insert_with(detect_target_dir); diff --git a/src/bin/miri.rs b/src/bin/miri.rs index a1f7c617f0..8249ae9928 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -465,6 +465,11 @@ fn main() { _ => panic!("-Zmiri-backtrace may only be 0, 1, or full"), }; } + arg if arg.starts_with("-Zmiri-local-crates=") => { + miri_config + .local_crates + .push(arg.strip_prefix("-Zmiri-local-crates=").unwrap().to_string()); + } _ => { // Forward to rustc. rustc_args.push(arg); diff --git a/src/diagnostics.rs b/src/diagnostics.rs index fbcb2e0d0f..7b6dbef1e7 100644 --- a/src/diagnostics.rs +++ b/src/diagnostics.rs @@ -4,12 +4,20 @@ use std::num::NonZeroU64; use log::trace; -use rustc_middle::ty::{self, TyCtxt}; +use rustc_middle::ty; use rustc_span::{source_map::DUMMY_SP, Span, SpanData, Symbol}; -use crate::stacked_borrows::{AccessKind, SbTag}; +use crate::stacked_borrows::{AccessKind, SbTag, TagHistory}; use crate::*; +struct HexRange(AllocRange); + +impl std::fmt::Display for HexRange { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "[{:#x}..{:#x}]", self.0.start.bytes(), self.0.end().bytes()) + } +} + /// Details of premature program termination. pub enum TerminationInfo { Exit(i64), @@ -19,6 +27,7 @@ pub enum TerminationInfo { msg: String, help: Option, url: String, + history: Option, }, Deadlock, MultipleSymbolDefinitions { @@ -94,7 +103,8 @@ fn prune_stacktrace<'mir, 'tcx>( // Only prune frames if there is at least one local frame. This check ensures that if // we get a backtrace that never makes it to the user code because it has detected a // bug in the Rust runtime, we don't prune away every frame. - let has_local_frame = stacktrace.iter().any(|frame| frame.instance.def_id().is_local()); + let has_local_frame = + stacktrace.iter().any(|frame| ecx.machine.is_local(frame.instance.def_id())); if has_local_frame { // This is part of the logic that `std` uses to select the relevant part of a // backtrace. But here, we only look for __rust_begin_short_backtrace, not @@ -115,7 +125,9 @@ fn prune_stacktrace<'mir, 'tcx>( // This len check ensures that we don't somehow remove every frame, as doing so breaks // the primary error message. while stacktrace.len() > 1 - && stacktrace.last().map_or(false, |e| !e.instance.def_id().is_local()) + && stacktrace + .last() + .map_or(false, |frame| !ecx.machine.is_local(frame.instance.def_id())) { stacktrace.pop(); } @@ -155,12 +167,38 @@ pub fn report_error<'tcx, 'mir>( (None, format!("pass the flag `-Zmiri-disable-isolation` to disable isolation;")), (None, format!("or pass `-Zmiri-isolation-error=warn` to configure Miri to return an error code from isolated operations (if supported for that operation) and continue with a warning")), ], - ExperimentalUb { url, help, .. } => { + ExperimentalUb { url, help, history, .. } => { msg.extend(help.clone()); - vec![ + let mut helps = vec![ (None, format!("this indicates a potential bug in the program: it performed an invalid operation, but the rules it violated are still experimental")), - (None, format!("see {} for further information", url)) - ] + (None, format!("see {} for further information", url)), + ]; + match history { + Some(TagHistory::Tagged {tag, created: (created_range, created_span), invalidated}) => { + let msg = format!("{:?} was created due to a retag at offsets {}", tag, HexRange(*created_range)); + helps.push((Some(created_span.clone()), msg)); + if let Some((invalidated_range, invalidated_span)) = invalidated { + let msg = format!("{:?} was later invalidated due to a retag at offsets {}", tag, HexRange(*invalidated_range)); + helps.push((Some(invalidated_span.clone()), msg)); + } + } + Some(TagHistory::Untagged{ recently_created, recently_invalidated, matching_created }) => { + if let Some((range, span)) = recently_created { + let msg = format!("tag was most recently created at offsets {}", HexRange(*range)); + helps.push((Some(span.clone()), msg)); + } + if let Some((range, span)) = recently_invalidated { + let msg = format!("tag was later invalidated at offsets {}", HexRange(*range)); + helps.push((Some(span.clone()), msg)); + } + if let Some((range, span)) = matching_created { + let msg = format!("this tag was also created here at offsets {}", HexRange(*range)); + helps.push((Some(span.clone()), msg)); + } + } + None => {} + } + helps } MultipleSymbolDefinitions { first, first_crate, second, second_crate, .. } => vec![ @@ -218,7 +256,7 @@ pub fn report_error<'tcx, 'mir>( e.print_backtrace(); msg.insert(0, e.to_string()); report_msg( - *ecx.tcx, + ecx, DiagLevel::Error, &if let Some(title) = title { format!("{}: {}", title, msg[0]) } else { msg[0].clone() }, msg, @@ -264,8 +302,8 @@ pub fn report_error<'tcx, 'mir>( /// We want to present a multi-line span message for some errors. Diagnostics do not support this /// directly, so we pass the lines as a `Vec` and display each line after the first with an /// additional `span_label` or `note` call. -fn report_msg<'tcx>( - tcx: TyCtxt<'tcx>, +fn report_msg<'mir, 'tcx>( + ecx: &MiriEvalContext<'mir, 'tcx>, diag_level: DiagLevel, title: &str, span_msg: Vec, @@ -273,10 +311,11 @@ fn report_msg<'tcx>( stacktrace: &[FrameInfo<'tcx>], ) { let span = stacktrace.first().map_or(DUMMY_SP, |fi| fi.span); + let sess = ecx.tcx.sess; let mut err = match diag_level { - DiagLevel::Error => tcx.sess.struct_span_err(span, title).forget_guarantee(), - DiagLevel::Warning => tcx.sess.struct_span_warn(span, title), - DiagLevel::Note => tcx.sess.diagnostic().span_note_diag(span, title), + DiagLevel::Error => sess.struct_span_err(span, title).forget_guarantee(), + DiagLevel::Warning => sess.struct_span_warn(span, title), + DiagLevel::Note => sess.diagnostic().span_note_diag(span, title), }; // Show main message. @@ -306,7 +345,7 @@ fn report_msg<'tcx>( } // Add backtrace for (idx, frame_info) in stacktrace.iter().enumerate() { - let is_local = frame_info.instance.def_id().is_local(); + let is_local = ecx.machine.is_local(frame_info.instance.def_id()); // No span for non-local frames and the first frame (which is the error site). if is_local && idx > 0 { err.span_note(frame_info.span, &frame_info.to_string()); @@ -426,7 +465,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx _ => ("tracking was triggered", DiagLevel::Note), }; - report_msg(*this.tcx, diag_level, title, vec![msg], vec![], &stacktrace); + report_msg(this, diag_level, title, vec![msg], vec![], &stacktrace); } }); } diff --git a/src/eval.rs b/src/eval.rs index 97856d9202..48f8979779 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -111,6 +111,8 @@ pub struct MiriConfig { /// Panic when unsupported functionality is encountered pub panic_on_unsupported: bool, pub backtrace_style: BacktraceStyle, + /// Crates which are considered local for the purposes of error reporting + pub local_crates: Vec, } impl Default for MiriConfig { @@ -136,6 +138,7 @@ impl Default for MiriConfig { measureme_out: None, panic_on_unsupported: false, backtrace_style: BacktraceStyle::Short, + local_crates: Vec::new(), } } } @@ -279,6 +282,20 @@ pub fn create_ecx<'mir, 'tcx: 'mir>( Ok((ecx, ret_place)) } +// This is potentially a performance hazard. +// Factoring it into its own function lets us keep an eye on how much it shows up in a profile. +fn set_current_span<'mir, 'tcx: 'mir>(ecx: &mut MiriEvalContext<'mir, 'tcx>) { + let current_span = Machine::stack(&ecx) + .into_iter() + .rev() + .find(|frame| ecx.machine.is_local(frame.instance.def_id())) + .map(|frame| frame.current_span()) + .unwrap_or(rustc_span::DUMMY_SP); + if let Some(sb) = ecx.memory.extra.stacked_borrows.as_mut() { + sb.get_mut().current_span = current_span; + } +} + /// Evaluates the entry function specified by `entry_id`. /// Returns `Some(return_code)` if program executed completed. /// Returns `None` if an evaluation error occured. @@ -306,6 +323,9 @@ pub fn eval_entry<'tcx>( let info = ecx.preprocess_diagnostics(); match ecx.schedule()? { SchedulingAction::ExecuteStep => { + if ecx.memory.extra.stacked_borrows.is_some() { + set_current_span(&mut ecx); + } assert!(ecx.step()?, "a terminated thread was scheduled for execution"); } SchedulingAction::ExecuteTimeoutCallback => { diff --git a/src/machine.rs b/src/machine.rs index bb43cb9550..6c536374f5 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -19,7 +19,7 @@ use rustc_middle::{ Instance, TyCtxt, }, }; -use rustc_span::def_id::DefId; +use rustc_span::def_id::{CrateNum, DefId}; use rustc_span::symbol::{sym, Symbol}; use rustc_target::abi::Size; use rustc_target::spec::abi::Abi; @@ -349,10 +349,23 @@ pub struct Evaluator<'mir, 'tcx> { /// Equivalent setting as RUST_BACKTRACE on encountering an error. pub(crate) backtrace_style: BacktraceStyle, + + /// Crates which are considered local for the purposes of error reporting + pub(crate) local_crates: Vec, } impl<'mir, 'tcx> Evaluator<'mir, 'tcx> { pub(crate) fn new(config: &MiriConfig, layout_cx: LayoutCx<'tcx, TyCtxt<'tcx>>) -> Self { + // Convert the local crate names from the passed-in config into CrateNums so that they can + // be looked up quickly during execution + let mut local_crates = Vec::new(); + for num in layout_cx.tcx.crates(()) { + let name = layout_cx.tcx.crate_name(*num).as_str().to_string(); + if config.local_crates.contains(&name) { + local_crates.push(*num); + } + } + let layouts = PrimitiveLayouts::new(layout_cx).expect("Couldn't get layouts of primitive types"); let profiler = config.measureme_out.as_ref().map(|out| { @@ -381,12 +394,17 @@ impl<'mir, 'tcx> Evaluator<'mir, 'tcx> { exported_symbols_cache: FxHashMap::default(), panic_on_unsupported: config.panic_on_unsupported, backtrace_style: config.backtrace_style, + local_crates, } } pub(crate) fn communicate(&self) -> bool { self.isolated_op == IsolatedOp::Allow } + + pub(crate) fn is_local(&self, def_id: DefId) -> bool { + def_id.is_local() || self.local_crates.contains(&def_id.krate) + } } /// A rustc InterpCx for Miri. diff --git a/src/stacked_borrows.rs b/src/stacked_borrows.rs index 777b8e9331..ceb8c816cd 100644 --- a/src/stacked_borrows.rs +++ b/src/stacked_borrows.rs @@ -3,6 +3,7 @@ use log::trace; use std::cell::RefCell; +use std::collections::HashMap; use std::fmt; use std::num::NonZeroU64; @@ -14,6 +15,7 @@ use rustc_middle::ty::{ layout::{HasParamEnv, LayoutOf}, }; use rustc_span::DUMMY_SP; +use rustc_span::{Span, SpanData}; use rustc_target::abi::Size; use crate::*; @@ -110,7 +112,42 @@ pub struct GlobalState { tracked_call_id: Option, /// Whether to track raw pointers. tag_raw: bool, + /// Extra per-allocation information + extras: HashMap, + /// Current span + pub(crate) current_span: Span, } + +#[derive(Debug, Default)] +struct AllocHistory { + // The time tags can be compressed down to one bit per event, by just storing a Vec + // where each bit is set to indicate if the event was a creation or a retag + current_time: usize, + creations: Vec, + invalidations: Vec, +} + +#[derive(Debug)] +struct Event { + time: usize, + tag: SbTag, + range: AllocRange, + span: Span, +} + +pub enum TagHistory { + Tagged { + tag: SbTag, + created: (AllocRange, SpanData), + invalidated: Option<(AllocRange, SpanData)>, + }, + Untagged { + recently_created: Option<(AllocRange, SpanData)>, + recently_invalidated: Option<(AllocRange, SpanData)>, + matching_created: Option<(AllocRange, SpanData)>, + }, +} + /// Memory extra state gives us interior mutable access to the global state. pub type MemoryExtra = RefCell; @@ -170,6 +207,8 @@ impl GlobalState { tracked_pointer_tag, tracked_call_id, tag_raw, + extras: HashMap::new(), + current_span: DUMMY_SP, } } @@ -217,16 +256,111 @@ impl GlobalState { self.base_ptr_ids.try_insert(id, tag).unwrap(); tag } + + fn add_creation(&mut self, tag: SbTag, alloc: AllocId, range: AllocRange) { + let mut extras = self.extras.entry(alloc).or_default(); + extras.creations.push(Event { + tag, + range, + span: self.current_span, + time: extras.current_time, + }); + extras.current_time += 1; + } + + fn add_invalidation(&mut self, tag: SbTag, alloc: AllocId, range: AllocRange) { + let mut extras = self.extras.entry(alloc).or_default(); + extras.invalidations.push(Event { + tag, + range, + span: self.current_span, + time: extras.current_time, + }); + extras.current_time += 1; + } + + fn get_stack_history( + &self, + tag: SbTag, + alloc: AllocId, + alloc_range: AllocRange, + offset: Size, + ) -> Option { + let extras = self.extras.get(&alloc)?; + if let SbTag::Tagged(_) = tag { + let get_matching = |events: &[Event]| { + events.iter().rev().find_map(|event| { + if event.tag == tag { Some((event.range, event.span.data())) } else { None } + }) + }; + Some(TagHistory::Tagged { + tag, + created: get_matching(&extras.creations).unwrap(), + invalidated: get_matching(&extras.invalidations), + }) + } else { + let mut created_time = 0; + // Find the most recently created tag that satsfies this offset + let recently_created = extras.creations.iter().rev().find_map(|event| { + if event.tag == tag && offset >= event.range.start && offset < event.range.end() { + created_time = event.time; + Some((event.range, event.span.data())) + } else { + None + } + }); + + // Find a different recently created tag that satisfies this whole operation, predates + // the recently created tag, and has a different span. + // We're trying to make a guess at which span the user wanted to provide the tag that + // they're using. + let matching_created = if let Some((_created_range, created_span)) = recently_created { + extras.creations.iter().rev().find_map(|event| { + if event.tag == tag + && alloc_range.start >= event.range.start + && alloc_range.end() <= event.range.end() + && event.span.data() != created_span + && event.time != created_time + { + Some((event.range, event.span.data())) + } else { + None + } + }) + } else { + None + }; + + let recently_invalidated = if recently_created.is_some() { + // Find the most recent invalidation of this tag which post-dates the creation + let mut found = None; + for event in extras.invalidations.iter().rev() { + if event.time < created_time { + break; + } + if event.tag == tag && offset >= event.range.start && offset < event.range.end() + { + found = Some((event.range, event.span.data())) + } + } + found + } else { + None + }; + Some(TagHistory::Untagged { recently_created, matching_created, recently_invalidated }) + } + } } /// Error reporting -fn err_sb_ub(msg: String, help: Option) -> InterpError<'static> { +fn err_sb_ub(msg: String, help: Option, history: Option) -> InterpError<'static> { err_machine_stop!(TerminationInfo::ExperimentalUb { msg, help, url: format!( "https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md" ), + history }) } @@ -327,11 +461,13 @@ impl<'tcx> Stack { tag, item ), None, + None, ))? } else { Err(err_sb_ub( format!("deallocating while item is protected: {:?}", item), None, + None, ))? } } @@ -347,15 +483,15 @@ impl<'tcx> Stack { &mut self, access: AccessKind, tag: SbTag, - (alloc_id, range, offset): (AllocId, AllocRange, Size), // just for debug printing and error messages - global: &GlobalState, + (alloc_id, alloc_range, offset): (AllocId, AllocRange, Size), // just for debug printing amd error messages + global: &mut GlobalState, ) -> InterpResult<'tcx> { // 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(|| self.access_error(access, tag, alloc_id, range, offset))?; + .ok_or_else(|| self.access_error(access, tag, alloc_id, alloc_range, offset, &*global))?; // Step 2: Remove incompatible items above them. Make sure we do not remove protected // items. Behavior differs for reads and writes. @@ -366,6 +502,7 @@ impl<'tcx> Stack { for item in self.borrows.drain(first_incompatible_idx..).rev() { trace!("access: popping item {:?}", item); Stack::check_protector(&item, Some((tag, access)), global)?; + global.add_invalidation(item.tag, alloc_id, alloc_range); } } else { // On a read, *disable* all `Unique` above the granting item. This ensures U2 for read accesses. @@ -382,6 +519,7 @@ impl<'tcx> Stack { trace!("access: disabling item {:?}", item); Stack::check_protector(item, Some((tag, access)), global)?; item.perm = Permission::Disabled; + global.add_invalidation(item.tag, alloc_id, alloc_range); } } } @@ -395,15 +533,18 @@ impl<'tcx> Stack { fn dealloc( &mut self, tag: SbTag, - dbg_ptr: Pointer, // just for debug printing and error messages + (alloc_id, alloc_range, offset): (AllocId, AllocRange, Size), // just for debug printing amd error messages global: &GlobalState, ) -> InterpResult<'tcx> { // Step 1: Find granting item. self.find_granting(AccessKind::Write, tag).ok_or_else(|| { err_sb_ub(format!( "no item granting write access for deallocation to tag {:?} at {:?} found in borrow stack", - tag, dbg_ptr, - ), None) + tag, alloc_id, + ), + None, + global.get_stack_history(tag, alloc_id, alloc_range, offset), + ) })?; // Step 2: Remove all items. Also checks for protectors. @@ -424,8 +565,8 @@ impl<'tcx> Stack { &mut self, derived_from: SbTag, new: Item, - (alloc_id, alloc_range, offset): (AllocId, AllocRange, Size), // just for debug printing and error messages - global: &GlobalState, + (alloc_id, alloc_range, offset): (AllocId, AllocRange, Size), // just for debug printing amd error messages + global: &mut GlobalState, ) -> InterpResult<'tcx> { // Figure out which access `perm` corresponds to. let access = @@ -434,7 +575,7 @@ impl<'tcx> Stack { // We use that to determine where to put the new item. let granting_idx = self .find_granting(access, derived_from) - .ok_or_else(|| self.grant_error(derived_from, new, alloc_id, alloc_range, offset))?; + .ok_or_else(|| self.grant_error(derived_from, new, alloc_id, alloc_range, offset, &*global))?; // Compute where to put the new item. // Either way, we ensure that we insert the new item in a way such that between @@ -482,6 +623,7 @@ impl<'tcx> Stack { alloc_id: AllocId, alloc_range: AllocRange, error_offset: Size, + global: &GlobalState, ) -> InterpError<'static> { let action = format!( "trying to reborrow {:?} for {:?} permission at {}[{:#x}]", @@ -493,6 +635,7 @@ impl<'tcx> Stack { err_sb_ub( format!("{}{}", action, self.error_cause(derived_from)), Some(Self::operation_summary("a reborrow", alloc_id, alloc_range)), + global.get_stack_history(derived_from, alloc_id, alloc_range, error_offset), ) } @@ -504,6 +647,7 @@ impl<'tcx> Stack { alloc_id: AllocId, alloc_range: AllocRange, error_offset: Size, + global: &GlobalState, ) -> InterpError<'static> { let action = format!( "attempting a {} using {:?} at {}[{:#x}]", @@ -515,6 +659,7 @@ impl<'tcx> Stack { err_sb_ub( format!("{}{}", action, self.error_cause(tag)), Some(Self::operation_summary("an access", alloc_id, alloc_range)), + global.get_stack_history(tag, alloc_id, alloc_range, error_offset), ) } @@ -619,6 +764,7 @@ impl Stacks { (tag, Permission::SharedReadWrite) } }; + extra.add_creation(base_tag, id, alloc_range(Size::ZERO, size)); Stacks::new(size, perm, base_tag) } @@ -636,8 +782,8 @@ impl Stacks { Pointer::new(alloc_id, range.start), range.size.bytes() ); - let global = &*extra.borrow(); self.for_each(range, move |offset, stack| { + let global = &mut *extra.borrow_mut(); stack.access(AccessKind::Read, tag, (alloc_id, range, offset), global) }) } @@ -648,7 +794,7 @@ impl Stacks { alloc_id: AllocId, tag: SbTag, range: AllocRange, - extra: &mut MemoryExtra, + extra: &MemoryExtra, ) -> InterpResult<'tcx> { trace!( "write access with tag {:?}: {:?}, size {}", @@ -656,8 +802,8 @@ impl Stacks { Pointer::new(alloc_id, range.start), range.size.bytes() ); - let global = extra.get_mut(); self.for_each_mut(range, move |offset, stack| { + let global = &mut *extra.borrow_mut(); stack.access(AccessKind::Write, tag, (alloc_id, range, offset), global) }) } @@ -673,8 +819,10 @@ impl Stacks { trace!("deallocation with tag {:?}: {:?}, size {}", tag, alloc_id, range.size.bytes()); let global = extra.get_mut(); self.for_each_mut(range, move |offset, stack| { - stack.dealloc(tag, Pointer::new(alloc_id, offset), global) - }) + stack.dealloc(tag, (alloc_id, range, offset), global) + })?; + extra.borrow_mut().extras.remove(&alloc_id); + Ok(()) } } @@ -753,7 +901,6 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx let extra = this.memory.get_alloc_extra(alloc_id)?; let stacked_borrows = extra.stacked_borrows.as_ref().expect("we should have Stacked Borrows data"); - let global = this.memory.extra.stacked_borrows.as_ref().unwrap().borrow(); this.visit_freeze_sensitive(place, size, |mut range, frozen| { // Adjust range. range.start += base_offset; @@ -765,7 +912,9 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx }; let item = Item { perm, tag: new_tag, protector }; stacked_borrows.for_each(range, |offset, stack| { - stack.grant(orig_tag, item, (alloc_id, range, offset), &*global) + let mut global = + this.memory.extra.stacked_borrows.as_ref().unwrap().borrow_mut(); + stack.grant(orig_tag, item, (alloc_id, range, offset), &mut *global) }) })?; return Ok(()); @@ -777,11 +926,11 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx let (alloc_extra, memory_extra) = this.memory.get_alloc_extra_mut(alloc_id)?; let stacked_borrows = alloc_extra.stacked_borrows.as_mut().expect("we should have Stacked Borrows data"); - let global = memory_extra.stacked_borrows.as_mut().unwrap().get_mut(); let item = Item { perm, tag: new_tag, protector }; let range = alloc_range(base_offset, size); - stacked_borrows.for_each_mut(alloc_range(base_offset, size), |offset, stack| { - stack.grant(orig_tag, item, (alloc_id, range, offset), global) + stacked_borrows.for_each_mut(range, |offset, stack| { + let mut global = memory_extra.stacked_borrows.as_ref().unwrap().borrow_mut(); + stack.grant(orig_tag, item, (alloc_id, range, offset), &mut *global) })?; Ok(()) } @@ -806,17 +955,22 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx None => return Ok(*val), }; + // May return Err for retags of size 0 + let alloc = this.memory.ptr_get_alloc(place.ptr); + // Compute new borrow. - let new_tag = { - let mem_extra = this.memory.extra.stacked_borrows.as_mut().unwrap().get_mut(); - match kind { - // Give up tracking for raw pointers. - RefKind::Raw { .. } if !mem_extra.tag_raw => SbTag::Untagged, - // All other pointers are properly tracked. - _ => SbTag::Tagged(mem_extra.new_ptr()), - } + let mem_extra = this.memory.extra.stacked_borrows.as_mut().unwrap().get_mut(); + let new_tag = match kind { + // Give up tracking for raw pointers. + RefKind::Raw { .. } if !mem_extra.tag_raw => SbTag::Untagged, + // All other pointers are properly tracked. + _ => SbTag::Tagged(mem_extra.new_ptr()), }; + if let Ok((alloc_id, base_offset, _ptr)) = alloc { + mem_extra.add_creation(new_tag, alloc_id, alloc_range(base_offset, base_offset + size)); + } + // Reborrow. this.reborrow(&place, size, kind, new_tag, protect)?;