+/// Attempts to prune a stacktrace to omit the Rust runtime, and returns a bool indicating if any
+/// frames were pruned. If the stacktrace does not have any local frames, we conclude that it must
+/// be pointing to a problem in the Rust runtime itself, and do not prune it at all.
+fn prune_stacktrace<'mir, 'tcx>(
+ ecx: &InterpCx<'mir, 'tcx, Evaluator<'mir, 'tcx>>,
+ mut stacktrace: Vec<FrameInfo<'tcx>>,
+) -> (Vec<FrameInfo<'tcx>>, bool) {
+ match ecx.machine.backtrace_style {
+ BacktraceStyle::Off => {
+ // Remove all frames marked with `caller_location` -- that attribute indicates we
+ // usually want to point at the caller, not them.
+ stacktrace.retain(|frame| !frame.instance.def.requires_caller_location(*ecx.tcx));
+ // Retain one frame so that we can print a span for the error itself
+ stacktrace.truncate(1);
+ (stacktrace, false)
+ }
+ BacktraceStyle::Short => {
+ let original_len = stacktrace.len();
+ // 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| ecx.machine.is_local(frame));
+ if has_local_frame {
+ // Remove all frames marked with `caller_location` -- that attribute indicates we
+ // usually want to point at the caller, not them.
+ stacktrace.retain(|frame| !frame.instance.def.requires_caller_location(*ecx.tcx));
+
+ // 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
+ // __rust_end_short_backtrace because the end symbol comes from a call to the default
+ // panic handler.
+ stacktrace = stacktrace
+ .into_iter()
+ .take_while(|frame| {
+ let def_id = frame.instance.def_id();
+ let path = ecx.tcx.tcx.def_path_str(def_id);
+ !path.contains("__rust_begin_short_backtrace")
+ })
+ .collect::<Vec<_>>();
+
+ // After we prune frames from the bottom, there are a few left that are part of the
+ // Rust runtime. So we remove frames until we get to a local symbol, which should be
+ // main or a test.
+ // 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, |frame| !ecx.machine.is_local(frame))
+ {
+ stacktrace.pop();
+ }
+ }
+ let was_pruned = stacktrace.len() != original_len;
+ (stacktrace, was_pruned)
+ }
+ BacktraceStyle::Full => (stacktrace, false),
+ }
+}
+