Skip to content

Commit 5fd8abd

Browse files
committed
Ensure that we don't cause *new* hard errors if we suddenly can evaluate more constants during const prop
1 parent b3abebd commit 5fd8abd

File tree

1 file changed

+41
-29
lines changed

1 file changed

+41
-29
lines changed

src/librustc_mir/transform/const_prop.rs

+41-29
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use std::cell::Cell;
66

77
use rustc::hir::def::DefKind;
88
use rustc::hir::def_id::DefId;
9+
use rustc::hir::HirId;
910
use rustc::mir::interpret::{InterpResult, PanicInfo, Scalar};
1011
use rustc::mir::visit::{
1112
MutVisitor, MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor,
@@ -33,7 +34,6 @@ use crate::interpret::{
3334
ScalarMaybeUndef, StackPopCleanup,
3435
};
3536
use crate::rustc::ty::subst::Subst;
36-
use crate::rustc::ty::TypeFoldable;
3737
use crate::transform::{MirPass, MirSource};
3838

3939
/// The maximum number of bytes that we'll allocate space for a return value.
@@ -261,6 +261,9 @@ struct ConstPropagator<'mir, 'tcx> {
261261
source_scopes: IndexVec<SourceScope, SourceScopeData>,
262262
local_decls: IndexVec<Local, LocalDecl<'tcx>>,
263263
ret: Option<OpTy<'tcx, ()>>,
264+
// Because we have `MutVisitor` we can't obtain the `SourceInfo` from a `Location`. So we store
265+
// the last known `SourceInfo` here and just keep revisiting it.
266+
source_info: Option<SourceInfo>,
264267
}
265268

266269
impl<'mir, 'tcx> LayoutOf for ConstPropagator<'mir, 'tcx> {
@@ -339,6 +342,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
339342
//FIXME(wesleywiser) we can't steal this because `Visitor::super_visit_body()` needs it
340343
local_decls: body.local_decls.clone(),
341344
ret: ret.map(Into::into),
345+
source_info: None,
342346
}
343347
}
344348

@@ -360,6 +364,13 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
360364
LocalState { value: LocalValue::Uninitialized, layout: Cell::new(None) };
361365
}
362366

367+
fn lint_root(&self, source_info: SourceInfo) -> Option<HirId> {
368+
match &self.source_scopes[source_info.scope].local_data {
369+
ClearCrossCrate::Set(data) => Some(data.lint_root),
370+
ClearCrossCrate::Clear => None,
371+
}
372+
}
373+
363374
fn use_ecx<F, T>(&mut self, source_info: SourceInfo, f: F) -> Option<T>
364375
where
365376
F: FnOnce(&mut Self) -> InterpResult<'tcx, T>,
@@ -368,10 +379,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
368379
// FIXME(eddyb) move this to the `Panic(_)` error case, so that
369380
// `f(self)` is always called, and that the only difference when the
370381
// scope's `local_data` is missing, is that the lint isn't emitted.
371-
let lint_root = match &self.source_scopes[source_info.scope].local_data {
372-
ClearCrossCrate::Set(data) => data.lint_root,
373-
ClearCrossCrate::Clear => return None,
374-
};
382+
let lint_root = self.lint_root(source_info)?;
375383
let r = match f(self) {
376384
Ok(val) => Some(val),
377385
Err(error) => {
@@ -417,19 +425,31 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
417425
r
418426
}
419427

420-
fn eval_constant(&mut self, c: &Constant<'tcx>) -> Option<Const<'tcx>> {
421-
// `eval_const_to_op` uses `Instance::resolve` which still has a bug (#66901) in the
422-
// presence of trait items with a default body. So we just bail out if we aren't 100%
423-
// monomorphic.
424-
if c.literal.needs_subst() {
425-
return None;
426-
}
428+
fn eval_constant(
429+
&mut self,
430+
c: &Constant<'tcx>,
431+
source_info: SourceInfo,
432+
) -> Option<Const<'tcx>> {
427433
self.ecx.tcx.span = c.span;
428434
match self.ecx.eval_const_to_op(c.literal, None) {
429435
Ok(op) => Some(op),
430436
Err(error) => {
431437
let err = error_to_const_error(&self.ecx, error);
432-
err.report_as_error(self.ecx.tcx, "erroneous constant used");
438+
match self.lint_root(source_info) {
439+
Some(lint_root) if c.literal.needs_subst() => {
440+
// Out of backwards compatibility we cannot report hard errors in unused
441+
// generic functions using associated constants of the generic parameters.
442+
err.report_as_lint(
443+
self.ecx.tcx,
444+
"erroneous constant used",
445+
lint_root,
446+
Some(c.span),
447+
);
448+
}
449+
_ => {
450+
err.report_as_error(self.ecx.tcx, "erroneous constant used");
451+
}
452+
}
433453
None
434454
}
435455
}
@@ -442,7 +462,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
442462

443463
fn eval_operand(&mut self, op: &Operand<'tcx>, source_info: SourceInfo) -> Option<Const<'tcx>> {
444464
match *op {
445-
Operand::Constant(ref c) => self.eval_constant(c),
465+
Operand::Constant(ref c) => self.eval_constant(c, source_info),
446466
Operand::Move(ref place) | Operand::Copy(ref place) => {
447467
self.eval_place(place, source_info)
448468
}
@@ -509,10 +529,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
509529
let right_size = r.layout.size;
510530
let r_bits = r.to_scalar().and_then(|r| r.to_bits(right_size));
511531
if r_bits.map_or(false, |b| b >= left_bits as u128) {
512-
let lint_root = match &self.source_scopes[source_info.scope].local_data {
513-
ClearCrossCrate::Set(data) => data.lint_root,
514-
ClearCrossCrate::Clear => return None,
515-
};
532+
let lint_root = self.lint_root(source_info)?;
516533
let dir = if *op == BinOp::Shr { "right" } else { "left" };
517534
self.tcx.lint_hir(
518535
::rustc::lint::builtin::EXCEEDING_BITSHIFTS,
@@ -570,13 +587,6 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
570587
_ => {}
571588
}
572589

573-
// `eval_rvalue_into_place` uses `Instance::resolve` for constants which still has a bug
574-
// (#66901) in the presence of trait items with a default body. So we just bail out if we
575-
// aren't 100% monomorphic.
576-
if rvalue.needs_subst() {
577-
return None;
578-
}
579-
580590
self.use_ecx(source_info, |this| {
581591
trace!("calling eval_rvalue_into_place(rvalue = {:?}, place = {:?})", rvalue, place);
582592
this.ecx.eval_rvalue_into_place(rvalue, place)?;
@@ -769,18 +779,19 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> {
769779
fn visit_constant(&mut self, constant: &mut Constant<'tcx>, location: Location) {
770780
trace!("visit_constant: {:?}", constant);
771781
self.super_constant(constant, location);
772-
self.eval_constant(constant);
782+
self.eval_constant(constant, self.source_info.unwrap());
773783
}
774784

775785
fn visit_statement(&mut self, statement: &mut Statement<'tcx>, location: Location) {
776786
trace!("visit_statement: {:?}", statement);
787+
let source_info = statement.source_info;
788+
self.source_info = Some(source_info);
777789
if let StatementKind::Assign(box (ref place, ref mut rval)) = statement.kind {
778790
let place_ty: Ty<'tcx> = place.ty(&self.local_decls, self.tcx).ty;
779791
if let Ok(place_layout) = self.tcx.layout_of(self.param_env.and(place_ty)) {
780792
if let Some(local) = place.as_local() {
781-
let source = statement.source_info;
782793
let can_const_prop = self.can_const_prop[local];
783-
if let Some(()) = self.const_prop(rval, place_layout, source, place) {
794+
if let Some(()) = self.const_prop(rval, place_layout, source_info, place) {
784795
if can_const_prop == ConstPropMode::FullConstProp
785796
|| can_const_prop == ConstPropMode::OnlyPropagateInto
786797
{
@@ -823,8 +834,9 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> {
823834
}
824835

825836
fn visit_terminator(&mut self, terminator: &mut Terminator<'tcx>, location: Location) {
826-
self.super_terminator(terminator, location);
827837
let source_info = terminator.source_info;
838+
self.source_info = Some(source_info);
839+
self.super_terminator(terminator, location);
828840
match &mut terminator.kind {
829841
TerminatorKind::Assert { expected, ref msg, ref mut cond, .. } => {
830842
if let Some(value) = self.eval_operand(&cond, source_info) {

0 commit comments

Comments
 (0)