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

@ -437,10 +437,12 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
source_info.scope.lint_root(self.source_scopes)
}
fn use_ecx<F, T>(&mut self, f: F) -> Option<T>
fn use_ecx<F, T>(&mut self, source_info: SourceInfo, f: F) -> Option<T>
where
F: FnOnce(&mut Self) -> InterpResult<'tcx, T>,
{
// Overwrite the PC -- whatever the interpreter does to it does not make any sense anyway.
self.ecx.frame_mut().loc = Err(source_info.span);
match f(self) {
Ok(val) => Some(val),
Err(error) => {
@ -501,9 +503,9 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
}
/// Returns the value, if any, of evaluating `place`.
fn eval_place(&mut self, place: Place<'tcx>) -> Option<OpTy<'tcx>> {
fn eval_place(&mut self, place: Place<'tcx>, source_info: SourceInfo) -> Option<OpTy<'tcx>> {
trace!("eval_place(place={:?})", place);
self.use_ecx(|this| this.ecx.eval_place_to_op(place, None))
self.use_ecx(source_info, |this| this.ecx.eval_place_to_op(place, None))
}
/// Returns the value, if any, of evaluating `op`. Calls upon `eval_constant`
@ -511,7 +513,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
fn eval_operand(&mut self, op: &Operand<'tcx>, source_info: SourceInfo) -> Option<OpTy<'tcx>> {
match *op {
Operand::Constant(ref c) => self.eval_constant(c, source_info),
Operand::Move(place) | Operand::Copy(place) => self.eval_place(place),
Operand::Move(place) | Operand::Copy(place) => self.eval_place(place, source_info),
}
}
@ -537,7 +539,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
arg: &Operand<'tcx>,
source_info: SourceInfo,
) -> Option<()> {
if let (val, true) = self.use_ecx(|this| {
if let (val, true) = self.use_ecx(source_info, |this| {
let val = this.ecx.read_immediate(&this.ecx.eval_operand(arg, None)?)?;
let (_res, overflow, _ty) = this.ecx.overflowing_unary_op(op, &val)?;
Ok((val, overflow))
@ -564,8 +566,12 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
right: &Operand<'tcx>,
source_info: SourceInfo,
) -> Option<()> {
let r = self.use_ecx(|this| this.ecx.read_immediate(&this.ecx.eval_operand(right, None)?));
let l = self.use_ecx(|this| this.ecx.read_immediate(&this.ecx.eval_operand(left, None)?));
let r = self.use_ecx(source_info, |this| {
this.ecx.read_immediate(&this.ecx.eval_operand(right, None)?)
});
let l = self.use_ecx(source_info, |this| {
this.ecx.read_immediate(&this.ecx.eval_operand(left, None)?)
});
// Check for exceeding shifts *even if* we cannot evaluate the LHS.
if op == BinOp::Shr || op == BinOp::Shl {
let r = r?;
@ -602,7 +608,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
if let (Some(l), Some(r)) = (&l, &r) {
// The remaining operators are handled through `overflowing_binary_op`.
if self.use_ecx(|this| {
if self.use_ecx(source_info, |this| {
let (_res, overflow, _ty) = this.ecx.overflowing_binary_op(op, l, r)?;
Ok(overflow)
})? {
@ -690,7 +696,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
return None;
}
self.use_ecx(|this| this.ecx.eval_rvalue_into_place(rvalue, place))
self.use_ecx(source_info, |this| this.ecx.eval_rvalue_into_place(rvalue, place))
}
}
@ -890,7 +896,10 @@ impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> {
StatementKind::SetDiscriminant { ref place, .. } => {
match self.ecx.machine.can_const_prop[place.local] {
ConstPropMode::FullConstProp | ConstPropMode::OnlyInsideOwnBlock => {
if self.use_ecx(|this| this.ecx.statement(statement)).is_some() {
if self
.use_ecx(source_info, |this| this.ecx.statement(statement))
.is_some()
{
trace!("propped discriminant into {:?}", place);
} else {
Self::remove_const(&mut self.ecx, place.local);