Merge const linting pass into const prop
This commit is contained in:
parent
0f72f0009a
commit
8c8a433532
@ -1087,13 +1087,6 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(trans: &TransCrate,
|
||||
stability::check_unused_or_stable_features(tcx)
|
||||
});
|
||||
|
||||
|
||||
time(time_passes,
|
||||
"MIR linting",
|
||||
|| for def_id in tcx.body_owners() {
|
||||
mir::check_const_err::check(tcx, def_id)
|
||||
});
|
||||
|
||||
time(time_passes, "lint checking", || lint::check_crate(tcx));
|
||||
|
||||
return Ok(f(tcx, analysis, rx, tcx.sess.compile_status()));
|
||||
|
@ -1,197 +0,0 @@
|
||||
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! Lints statically known runtime failures
|
||||
|
||||
use rustc::mir::*;
|
||||
use rustc::hir;
|
||||
use rustc::hir::map::Node;
|
||||
use rustc::mir::visit::Visitor;
|
||||
use rustc::mir::interpret::{Value, PrimVal, GlobalId};
|
||||
use rustc::middle::const_val::{ConstVal, ConstEvalErr, ErrKind};
|
||||
use rustc::hir::def::Def;
|
||||
use rustc::traits;
|
||||
use interpret::eval_body_with_mir;
|
||||
use rustc::ty::{TyCtxt, ParamEnv};
|
||||
use rustc::ty::Instance;
|
||||
use rustc::ty::layout::LayoutOf;
|
||||
use rustc::hir::def_id::DefId;
|
||||
use rustc::ty::subst::Substs;
|
||||
|
||||
fn is_const<'a, 'tcx: 'a>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> bool {
|
||||
if let Some(node) = tcx.hir.get_if_local(def_id) {
|
||||
match node {
|
||||
Node::NodeItem(&hir::Item {
|
||||
node: hir::ItemConst(..), ..
|
||||
}) => true,
|
||||
_ => false
|
||||
}
|
||||
} else {
|
||||
match tcx.describe_def(def_id) {
|
||||
Some(Def::Const(_)) => true,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check<'a, 'tcx: 'a>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) {
|
||||
let mir = &tcx.optimized_mir(def_id);
|
||||
let substs = Substs::identity_for_item(tcx, def_id);
|
||||
let instance = Instance::new(def_id, substs);
|
||||
let param_env = tcx.param_env(def_id);
|
||||
|
||||
if is_const(tcx, def_id) {
|
||||
let cid = GlobalId {
|
||||
instance,
|
||||
promoted: None,
|
||||
};
|
||||
eval_body_with_mir(tcx, cid, mir, param_env);
|
||||
}
|
||||
|
||||
ConstErrVisitor {
|
||||
tcx,
|
||||
mir,
|
||||
}.visit_mir(mir);
|
||||
let outer_def_id = if tcx.is_closure(def_id) {
|
||||
tcx.closure_base_def_id(def_id)
|
||||
} else {
|
||||
def_id
|
||||
};
|
||||
let generics = tcx.generics_of(outer_def_id);
|
||||
// FIXME: miri should be able to eval stuff that doesn't need info
|
||||
// from the generics
|
||||
if generics.parent_types as usize + generics.types.len() > 0 {
|
||||
return;
|
||||
}
|
||||
for i in 0.. mir.promoted.len() {
|
||||
use rustc_data_structures::indexed_vec::Idx;
|
||||
let cid = GlobalId {
|
||||
instance,
|
||||
promoted: Some(Promoted::new(i)),
|
||||
};
|
||||
eval_body_with_mir(tcx, cid, mir, param_env);
|
||||
}
|
||||
}
|
||||
|
||||
struct ConstErrVisitor<'a, 'tcx: 'a> {
|
||||
tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
mir: &'a Mir<'tcx>,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> ConstErrVisitor<'a, 'tcx> {
|
||||
fn eval_op(&self, op: &Operand<'tcx>) -> Option<u128> {
|
||||
let op = match *op {
|
||||
Operand::Constant(ref c) => c,
|
||||
_ => return None,
|
||||
};
|
||||
match op.literal {
|
||||
Literal::Value { value } => match value.val {
|
||||
ConstVal::Value(Value::ByVal(PrimVal::Bytes(b))) => Some(b),
|
||||
_ => return None,
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> Visitor<'tcx> for ConstErrVisitor<'a, 'tcx> {
|
||||
fn visit_terminator(&mut self,
|
||||
block: BasicBlock,
|
||||
terminator: &Terminator<'tcx>,
|
||||
location: Location) {
|
||||
self.super_terminator(block, terminator, location);
|
||||
match terminator.kind {
|
||||
TerminatorKind::Assert { ref cond, expected, ref msg, .. } => {
|
||||
let cond = match self.eval_op(cond) {
|
||||
Some(val) => val,
|
||||
None => return,
|
||||
};
|
||||
if (cond == 1) == expected {
|
||||
return;
|
||||
}
|
||||
assert!(cond <= 1);
|
||||
// If we know we always panic, and the error message
|
||||
// is also constant, then we can produce a warning.
|
||||
|
||||
let kind = match *msg {
|
||||
AssertMessage::BoundsCheck { ref len, ref index } => {
|
||||
let len = match self.eval_op(len) {
|
||||
Some(val) => val,
|
||||
None => return,
|
||||
};
|
||||
let index = match self.eval_op(index) {
|
||||
Some(val) => val,
|
||||
None => return,
|
||||
};
|
||||
ErrKind::IndexOutOfBounds {
|
||||
len: len as u64,
|
||||
index: index as u64
|
||||
}
|
||||
}
|
||||
AssertMessage::Math(ref err) => ErrKind::Math(err.clone()),
|
||||
AssertMessage::GeneratorResumedAfterReturn |
|
||||
// FIXME(oli-obk): can we report a const_err warning here?
|
||||
AssertMessage::GeneratorResumedAfterPanic => return,
|
||||
};
|
||||
let span = terminator.source_info.span;
|
||||
let msg = ConstEvalErr{ span, kind };
|
||||
let scope_info = match self.mir.visibility_scope_info {
|
||||
ClearCrossCrate::Set(ref data) => data,
|
||||
ClearCrossCrate::Clear => return,
|
||||
};
|
||||
let node_id = scope_info[terminator.source_info.scope].lint_root;
|
||||
self.tcx.lint_node(::rustc::lint::builtin::CONST_ERR,
|
||||
node_id,
|
||||
msg.span,
|
||||
&msg.description().into_oneline().into_owned());
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
fn visit_rvalue(&mut self,
|
||||
rvalue: &Rvalue<'tcx>,
|
||||
location: Location) {
|
||||
self.super_rvalue(rvalue, location);
|
||||
use rustc::mir::BinOp;
|
||||
match *rvalue {
|
||||
Rvalue::BinaryOp(BinOp::Shr, ref lop, ref rop) |
|
||||
Rvalue::BinaryOp(BinOp::Shl, ref lop, ref rop) => {
|
||||
let val = match self.eval_op(rop) {
|
||||
Some(val) => val,
|
||||
None => return,
|
||||
};
|
||||
let ty = lop.ty(self.mir, self.tcx);
|
||||
let param_env = ParamEnv::empty(traits::Reveal::All);
|
||||
let bits = (self.tcx, param_env).layout_of(ty).unwrap().size.bits();
|
||||
if val >= bits as u128 {
|
||||
let data = &self.mir[location.block];
|
||||
let stmt_idx = location.statement_index;
|
||||
let source_info = if stmt_idx < data.statements.len() {
|
||||
data.statements[stmt_idx].source_info
|
||||
} else {
|
||||
data.terminator().source_info
|
||||
};
|
||||
let span = source_info.span;
|
||||
let scope_info = match self.mir.visibility_scope_info {
|
||||
ClearCrossCrate::Set(ref data) => data,
|
||||
ClearCrossCrate::Clear => return,
|
||||
};
|
||||
let node_id = scope_info[source_info.scope].lint_root;
|
||||
self.tcx.lint_node(
|
||||
::rustc::lint::builtin::EXCEEDING_BITSHIFTS,
|
||||
node_id,
|
||||
span,
|
||||
"bitshift exceeds the type's number of bits");
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
@ -71,7 +71,6 @@ pub mod transform;
|
||||
pub mod util;
|
||||
pub mod interpret;
|
||||
pub mod monomorphize;
|
||||
pub mod check_const_err;
|
||||
|
||||
pub use hair::pattern::check_crate as matchck_crate;
|
||||
use rustc::ty::maps::Providers;
|
||||
|
@ -212,8 +212,30 @@ impl<'b, 'a, 'tcx:'b> OptimizationFinder<'b, 'a, 'tcx> {
|
||||
let instance = Instance::new(self.source.def_id, substs);
|
||||
let ecx = mk_borrowck_eval_cx(self.tcx, instance, self.mir, span).unwrap();
|
||||
|
||||
let l = ecx.value_to_primval(ValTy { value: left.0, ty: left.1 }).ok()?;
|
||||
let r = ecx.value_to_primval(ValTy { value: right.0, ty: right.1 }).ok()?;
|
||||
let param_env = ParamEnv::empty(traits::Reveal::All);
|
||||
let bits = (self.tcx, param_env).layout_of(left.ty).unwrap().size.bits();
|
||||
if r >= bits as u128 {
|
||||
let data = &self.mir[location.block];
|
||||
let stmt_idx = location.statement_index;
|
||||
let source_info = if stmt_idx < data.statements.len() {
|
||||
data.statements[stmt_idx].source_info
|
||||
} else {
|
||||
data.terminator().source_info
|
||||
};
|
||||
let span = source_info.span;
|
||||
let scope_info = match self.mir.visibility_scope_info {
|
||||
ClearCrossCrate::Set(ref data) => data,
|
||||
ClearCrossCrate::Clear => return,
|
||||
};
|
||||
let node_id = scope_info[source_info.scope].lint_root;
|
||||
self.tcx.lint_node(
|
||||
::rustc::lint::builtin::EXCEEDING_BITSHIFTS,
|
||||
node_id,
|
||||
span,
|
||||
"bitshift exceeds the type's number of bits");
|
||||
}
|
||||
let l = ecx.value_to_primval(ValTy { value: left.0, ty: left.1 }).ok()?;
|
||||
trace!("const evaluating {:?} for {:?} and {:?}", op, left, right);
|
||||
match ecx.binary_op(op, l, left.1, r, right.1) {
|
||||
Ok((val, overflow)) => {
|
||||
|
Loading…
Reference in New Issue
Block a user