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)
|
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));
|
time(time_passes, "lint checking", || lint::check_crate(tcx));
|
||||||
|
|
||||||
return Ok(f(tcx, analysis, rx, tcx.sess.compile_status()));
|
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 util;
|
||||||
pub mod interpret;
|
pub mod interpret;
|
||||||
pub mod monomorphize;
|
pub mod monomorphize;
|
||||||
pub mod check_const_err;
|
|
||||||
|
|
||||||
pub use hair::pattern::check_crate as matchck_crate;
|
pub use hair::pattern::check_crate as matchck_crate;
|
||||||
use rustc::ty::maps::Providers;
|
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 instance = Instance::new(self.source.def_id, substs);
|
||||||
let ecx = mk_borrowck_eval_cx(self.tcx, instance, self.mir, span).unwrap();
|
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 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);
|
trace!("const evaluating {:?} for {:?} and {:?}", op, left, right);
|
||||||
match ecx.binary_op(op, l, left.1, r, right.1) {
|
match ecx.binary_op(op, l, left.1, r, right.1) {
|
||||||
Ok((val, overflow)) => {
|
Ok((val, overflow)) => {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user