Skip to content

use precise spans for recursive const evaluation #97740

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jun 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions compiler/rustc_const_eval/src/interpret/eval_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
}

Expand Down Expand Up @@ -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)
}

Expand Down
6 changes: 3 additions & 3 deletions compiler/rustc_const_eval/src/interpret/intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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))?;
Expand Down Expand Up @@ -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)?;
}
Expand Down Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_const_eval/src/interpret/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)?;
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_const_eval/src/interpret/operator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
19 changes: 9 additions & 10 deletions compiler/rustc_const_eval/src/interpret/step.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have no idea what the point of this was; it is probably an ancient leftover.

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.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is no longer true since the Box nullary op got removed. :)

// 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)?,

Expand Down Expand Up @@ -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(())
}

Expand Down Expand Up @@ -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);

Expand Down
19 changes: 16 additions & 3 deletions compiler/rustc_middle/src/mir/interpret/queries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ use super::{ErrorHandled, EvalToConstValueResult, GlobalId};
use crate::mir;
use crate::ty::fold::TypeFoldable;
use crate::ty::subst::InternalSubsts;
use crate::ty::{self, TyCtxt};
use crate::ty::{self, query::TyCtxtAt, TyCtxt};
use rustc_hir::def_id::DefId;
use rustc_span::Span;
use rustc_span::{Span, DUMMY_SP};

impl<'tcx> TyCtxt<'tcx> {
/// Evaluates a constant without providing any substitutions. This is useful to evaluate consts
Expand Down Expand Up @@ -86,14 +86,25 @@ impl<'tcx> TyCtxt<'tcx> {
}
}

/// Evaluate a static's initializer, returning the allocation of the initializer's memory.
#[inline(always)]
pub fn eval_static_initializer(
self,
def_id: DefId,
) -> Result<mir::ConstAllocation<'tcx>, ErrorHandled> {
self.at(DUMMY_SP).eval_static_initializer(def_id)
}
}

impl<'tcx> TyCtxtAt<'tcx> {
/// Evaluate a static's initializer, returning the allocation of the initializer's memory.
pub fn eval_static_initializer(
self,
def_id: DefId,
) -> Result<mir::ConstAllocation<'tcx>, ErrorHandled> {
trace!("eval_static_initializer: Need to compute {:?}", def_id);
assert!(self.is_static(def_id));
let instance = ty::Instance::mono(self, def_id);
let instance = ty::Instance::mono(*self, def_id);
let gid = GlobalId { instance, promoted: None };
self.eval_to_allocation(gid, ty::ParamEnv::reveal_all())
}
Expand All @@ -109,7 +120,9 @@ impl<'tcx> TyCtxt<'tcx> {
let raw_const = self.eval_to_allocation_raw(param_env.and(gid))?;
Ok(self.global_alloc(raw_const.alloc_id).unwrap_memory())
}
}

impl<'tcx> TyCtxt<'tcx> {
/// Destructure a type-level constant ADT or array into its variant index and its field values.
/// Panics if the destructuring fails, use `try_destructure_const` for fallible version.
pub fn destructure_const(
Expand Down
29 changes: 19 additions & 10 deletions compiler/rustc_mir_transform/src/const_prop_lint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand Down Expand Up @@ -501,17 +503,17 @@ 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`
/// or `eval_place`, depending on the variant of `Operand` used.
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),
}
}

Expand All @@ -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))
Expand All @@ -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?;
Expand Down Expand Up @@ -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)
})? {
Expand Down Expand Up @@ -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))
}
}

Expand Down Expand Up @@ -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);
Expand Down
4 changes: 2 additions & 2 deletions src/test/ui/consts/recursive-zst-static.default.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ LL | static FOO: () = FOO;
| ^^^^^^^^^^^^^^^^^^^^^
|
note: ...which requires const-evaluating + checking `FOO`...
--> $DIR/recursive-zst-static.rs:10:1
--> $DIR/recursive-zst-static.rs:10:18
|
LL | static FOO: () = FOO;
| ^^^^^^^^^^^^^^^^^^^^^
| ^^^
= note: ...which again requires const-evaluating + checking `FOO`, completing the cycle
= note: cycle used when running analysis passes on this crate

Expand Down
4 changes: 2 additions & 2 deletions src/test/ui/consts/recursive-zst-static.unleash.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ LL | static FOO: () = FOO;
| ^^^^^^^^^^^^^^^^^^^^^
|
note: ...which requires const-evaluating + checking `FOO`...
--> $DIR/recursive-zst-static.rs:10:1
--> $DIR/recursive-zst-static.rs:10:18
|
LL | static FOO: () = FOO;
| ^^^^^^^^^^^^^^^^^^^^^
| ^^^
= note: ...which again requires const-evaluating + checking `FOO`, completing the cycle
= note: cycle used when running analysis passes on this crate

Expand Down
4 changes: 2 additions & 2 deletions src/test/ui/consts/write-to-static-mut-in-static.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ LL | pub static mut C: u32 = unsafe { C = 1; 0 };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: ...which requires const-evaluating + checking `C`...
--> $DIR/write-to-static-mut-in-static.rs:5:1
--> $DIR/write-to-static-mut-in-static.rs:5:34
|
LL | pub static mut C: u32 = unsafe { C = 1; 0 };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| ^^^^^
= note: ...which again requires const-evaluating + checking `C`, completing the cycle
= note: cycle used when running analysis passes on this crate

Expand Down
4 changes: 2 additions & 2 deletions src/test/ui/recursion/recursive-static-definition.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ LL | pub static FOO: u32 = FOO;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: ...which requires const-evaluating + checking `FOO`...
--> $DIR/recursive-static-definition.rs:1:1
--> $DIR/recursive-static-definition.rs:1:23
|
LL | pub static FOO: u32 = FOO;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
| ^^^
= note: ...which again requires const-evaluating + checking `FOO`, completing the cycle
= note: cycle used when running analysis passes on this crate

Expand Down