1
Fork 0

Auto merge of #97740 - RalfJung:ctfe-cycle-spans, r=lcnr

use precise spans for recursive const evaluation

This fixes https://github.com/rust-lang/rust/issues/73283 by using a `TyCtxtAt` with a more precise span when the interpreter recursively calls itself. Hopefully such calls are sufficiently rare that this does not cost us too much performance.

(In theory, cycles can also arise through layout computation, as layout can depend on consts -- but layout computation happens all the time so we'd have to do something to not make this terrible for performance.)
This commit is contained in:
bors 2022-06-09 01:52:15 +00:00
commit 282445a288
12 changed files with 66 additions and 40 deletions

View file

@ -126,7 +126,9 @@ pub struct Frame<'mir, 'tcx, Tag: Provenance = AllocId, Extra = ()> {
/// this frame (can happen e.g. during frame initialization, and during unwinding on
/// frames without cleanup code).
/// We basically abuse `Result` as `Either`.
pub(super) loc: Result<mir::Location, Span>,
///
/// Needs to be public because ConstProp does unspeakable things to it.
pub loc: Result<mir::Location, Span>,
}
/// What we store about a frame in an interpreter backtrace.
@ -320,6 +322,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> LayoutOfHelpers<'tcx> for InterpC
#[inline]
fn layout_tcx_at_span(&self) -> Span {
// Using the cheap root span for performance.
self.tcx.span
}
@ -923,7 +926,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
self.param_env
};
let param_env = param_env.with_const();
let val = self.tcx.eval_to_allocation_raw(param_env.and(gid))?;
// Use a precise span for better cycle errors.
let val = self.tcx.at(self.cur_span()).eval_to_allocation_raw(param_env.and(gid))?;
self.raw_const_to_mplace(val)
}

View file

@ -169,7 +169,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
sym::needs_drop => self.tcx.types.bool,
sym::type_id => self.tcx.types.u64,
sym::type_name => self.tcx.mk_static_str(),
_ => bug!("already checked for nullary intrinsics"),
_ => bug!(),
};
let val =
self.tcx.const_eval_global_id(self.param_env, gid, Some(self.tcx.span))?;
@ -215,7 +215,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
sym::add_with_overflow => BinOp::Add,
sym::sub_with_overflow => BinOp::Sub,
sym::mul_with_overflow => BinOp::Mul,
_ => bug!("Already checked for int ops"),
_ => bug!(),
};
self.binop_with_overflow(bin_op, &lhs, &rhs, dest)?;
}
@ -251,7 +251,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
sym::unchecked_mul => BinOp::Mul,
sym::unchecked_div => BinOp::Div,
sym::unchecked_rem => BinOp::Rem,
_ => bug!("Already checked for int ops"),
_ => bug!(),
};
let (val, overflowed, _ty) = self.overflowing_binary_op(bin_op, &l, &r)?;
if overflowed {

View file

@ -70,7 +70,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
}
}
bug!("no non-`#[track_caller]` frame found")
span_bug!(self.cur_span(), "no non-`#[track_caller]` frame found")
}
/// Allocate a `const core::panic::Location` with the provided filename and line/column numbers.

View file

@ -504,7 +504,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
throw_unsup!(ReadExternStatic(def_id));
}
(self.tcx.eval_static_initializer(def_id)?, Some(def_id))
// Use a precise span for better cycle errors.
(self.tcx.at(self.cur_span()).eval_static_initializer(def_id)?, Some(def_id))
}
};
M::before_access_global(*self.tcx, &self.machine, id, alloc, def_id, is_write)?;

View file

@ -154,14 +154,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
let result = match bin_op {
Shl => l.checked_shl(r).unwrap(),
Shr => l.checked_shr(r).unwrap(),
_ => bug!("it has already been checked that this is a shift op"),
_ => bug!(),
};
result as u128
} else {
match bin_op {
Shl => l.checked_shl(r).unwrap(),
Shr => l.checked_shr(r).unwrap(),
_ => bug!("it has already been checked that this is a shift op"),
_ => bug!(),
}
};
let truncated = self.truncate(result, left_layout);

View file

@ -55,33 +55,32 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
};
let basic_block = &self.body().basic_blocks()[loc.block];
let old_frames = self.frame_idx();
if let Some(stmt) = basic_block.statements.get(loc.statement_index) {
assert_eq!(old_frames, self.frame_idx());
let old_frames = self.frame_idx();
self.statement(stmt)?;
// Make sure we are not updating `statement_index` of the wrong frame.
assert_eq!(old_frames, self.frame_idx());
// Advance the program counter.
self.frame_mut().loc.as_mut().unwrap().statement_index += 1;
return Ok(true);
}
M::before_terminator(self)?;
let terminator = basic_block.terminator();
assert_eq!(old_frames, self.frame_idx());
self.terminator(terminator)?;
Ok(true)
}
/// Runs the interpretation logic for the given `mir::Statement` at the current frame and
/// statement counter. This also moves the statement counter forward.
/// statement counter.
///
/// This does NOT move the statement counter forward, the caller has to do that!
pub fn statement(&mut self, stmt: &mir::Statement<'tcx>) -> InterpResult<'tcx> {
info!("{:?}", stmt);
use rustc_middle::mir::StatementKind::*;
// Some statements (e.g., box) push new stack frames.
// We have to record the stack frame number *before* executing the statement.
let frame_idx = self.frame_idx();
match &stmt.kind {
Assign(box (place, rvalue)) => self.eval_rvalue_into_place(rvalue, *place)?,
@ -144,7 +143,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
Nop => {}
}
self.stack_mut()[frame_idx].loc.as_mut().unwrap().statement_index += 1;
Ok(())
}
@ -300,6 +298,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
Ok(())
}
/// Evaluate the given terminator. Will also adjust the stack frame and statement position accordingly.
fn terminator(&mut self, terminator: &mir::Terminator<'tcx>) -> InterpResult<'tcx> {
info!("{:?}", terminator.kind);