mirror of
https://github.com/rust-lang/rust.git
synced 2024-10-30 22:12:15 +00:00
[const-prop] Extract some functions out of _const_prop
This commit is contained in:
parent
e083273ec7
commit
397a2fd744
@ -469,6 +469,121 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
fn check_unary_op(&mut self, arg: &Operand<'tcx>, source_info: SourceInfo) -> Option<()> {
|
||||
self.use_ecx(source_info, |this| {
|
||||
let ty = arg.ty(&this.local_decls, this.tcx);
|
||||
|
||||
if ty.is_integral() {
|
||||
let arg = this.ecx.eval_operand(arg, None)?;
|
||||
let prim = this.ecx.read_immediate(arg)?;
|
||||
// Need to do overflow check here: For actual CTFE, MIR
|
||||
// generation emits code that does this before calling the op.
|
||||
if prim.to_bits()? == (1 << (prim.layout.size.bits() - 1)) {
|
||||
throw_panic!(OverflowNeg)
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
Some(())
|
||||
}
|
||||
|
||||
fn check_binary_op(
|
||||
&mut self,
|
||||
op: BinOp,
|
||||
left: &Operand<'tcx>,
|
||||
right: &Operand<'tcx>,
|
||||
source_info: SourceInfo,
|
||||
place_layout: TyLayout<'tcx>,
|
||||
overflow_check: bool,
|
||||
) -> Option<()> {
|
||||
let r = self.use_ecx(source_info, |this| {
|
||||
this.ecx.read_immediate(this.ecx.eval_operand(right, None)?)
|
||||
})?;
|
||||
if op == BinOp::Shr || op == BinOp::Shl {
|
||||
let left_bits = place_layout.size.bits();
|
||||
let right_size = r.layout.size;
|
||||
let r_bits = r.to_scalar().and_then(|r| r.to_bits(right_size));
|
||||
if r_bits.map_or(false, |b| b >= left_bits as u128) {
|
||||
let lint_root = self.lint_root(source_info)?;
|
||||
let dir = if op == BinOp::Shr { "right" } else { "left" };
|
||||
self.tcx.lint_hir(
|
||||
::rustc::lint::builtin::EXCEEDING_BITSHIFTS,
|
||||
lint_root,
|
||||
source_info.span,
|
||||
&format!("attempt to shift {} with overflow", dir),
|
||||
);
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
// If overflow checking is enabled (like in debug mode by default),
|
||||
// then we'll already catch overflow when we evaluate the `Assert` statement
|
||||
// in MIR. However, if overflow checking is disabled, then there won't be any
|
||||
// `Assert` statement and so we have to do additional checking here.
|
||||
if !overflow_check {
|
||||
self.use_ecx(source_info, |this| {
|
||||
let l = this.ecx.read_immediate(this.ecx.eval_operand(left, None)?)?;
|
||||
let (_, overflow, _ty) = this.ecx.overflowing_binary_op(op, l, r)?;
|
||||
|
||||
if overflow {
|
||||
let err = err_panic!(Overflow(op)).into();
|
||||
return Err(err);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
}
|
||||
|
||||
Some(())
|
||||
}
|
||||
|
||||
fn check_cast(
|
||||
&mut self,
|
||||
op: &Operand<'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
source_info: SourceInfo,
|
||||
place_layout: TyLayout<'tcx>,
|
||||
) -> Option<()> {
|
||||
if ty.is_integral() && op.ty(&self.local_decls, self.tcx).is_integral() {
|
||||
let value = self.use_ecx(source_info, |this| {
|
||||
this.ecx.read_immediate(this.ecx.eval_operand(op, None)?)
|
||||
})?;
|
||||
|
||||
// Do not try to read bits for ZSTs
|
||||
if !value.layout.is_zst() {
|
||||
let value_size = value.layout.size;
|
||||
let value_bits = value.to_scalar().and_then(|r| r.to_bits(value_size));
|
||||
if let Ok(value_bits) = value_bits {
|
||||
let truncated = truncate(value_bits, place_layout.size);
|
||||
if truncated != value_bits {
|
||||
let scope = source_info.scope;
|
||||
let lint_root = match &self.source_scopes[scope].local_data {
|
||||
ClearCrossCrate::Set(data) => data.lint_root,
|
||||
ClearCrossCrate::Clear => return None,
|
||||
};
|
||||
self.tcx.lint_hir(
|
||||
::rustc::lint::builtin::CONST_ERR,
|
||||
lint_root,
|
||||
source_info.span,
|
||||
&format!(
|
||||
"truncating cast: the value {} requires {} bits but \
|
||||
the target type is only {} bits",
|
||||
value_bits,
|
||||
value_size.bits(),
|
||||
place_layout.size.bits()
|
||||
),
|
||||
);
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some(())
|
||||
}
|
||||
|
||||
fn const_prop(
|
||||
&mut self,
|
||||
rvalue: &Rvalue<'tcx>,
|
||||
@ -476,8 +591,6 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
|
||||
source_info: SourceInfo,
|
||||
place: &Place<'tcx>,
|
||||
) -> Option<()> {
|
||||
let span = source_info.span;
|
||||
|
||||
// #66397: Don't try to eval into large places as that can cause an OOM
|
||||
if place_layout.size >= Size::from_bytes(MAX_ALLOC_LIMIT) {
|
||||
return None;
|
||||
@ -498,66 +611,14 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
|
||||
// if an overflow would occur.
|
||||
Rvalue::UnaryOp(UnOp::Neg, arg) if !overflow_check => {
|
||||
trace!("checking UnaryOp(op = Neg, arg = {:?})", arg);
|
||||
|
||||
self.use_ecx(source_info, |this| {
|
||||
let ty = arg.ty(&this.local_decls, this.tcx);
|
||||
|
||||
if ty.is_integral() {
|
||||
let arg = this.ecx.eval_operand(arg, None)?;
|
||||
let prim = this.ecx.read_immediate(arg)?;
|
||||
// Need to do overflow check here: For actual CTFE, MIR
|
||||
// generation emits code that does this before calling the op.
|
||||
if prim.to_bits()? == (1 << (prim.layout.size.bits() - 1)) {
|
||||
throw_panic!(OverflowNeg)
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
self.check_unary_op(arg, source_info)?;
|
||||
}
|
||||
|
||||
// Additional checking: check for overflows on integer binary operations and report
|
||||
// them to the user as lints.
|
||||
Rvalue::BinaryOp(op, left, right) => {
|
||||
trace!("checking BinaryOp(op = {:?}, left = {:?}, right = {:?})", op, left, right);
|
||||
|
||||
let r = self.use_ecx(source_info, |this| {
|
||||
this.ecx.read_immediate(this.ecx.eval_operand(right, None)?)
|
||||
})?;
|
||||
if *op == BinOp::Shr || *op == BinOp::Shl {
|
||||
let left_bits = place_layout.size.bits();
|
||||
let right_size = r.layout.size;
|
||||
let r_bits = r.to_scalar().and_then(|r| r.to_bits(right_size));
|
||||
if r_bits.map_or(false, |b| b >= left_bits as u128) {
|
||||
let lint_root = self.lint_root(source_info)?;
|
||||
let dir = if *op == BinOp::Shr { "right" } else { "left" };
|
||||
self.tcx.lint_hir(
|
||||
::rustc::lint::builtin::EXCEEDING_BITSHIFTS,
|
||||
lint_root,
|
||||
span,
|
||||
&format!("attempt to shift {} with overflow", dir),
|
||||
);
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
// If overflow checking is enabled (like in debug mode by default),
|
||||
// then we'll already catch overflow when we evaluate the `Assert` statement
|
||||
// in MIR. However, if overflow checking is disabled, then there won't be any
|
||||
// `Assert` statement and so we have to do additional checking here.
|
||||
if !overflow_check {
|
||||
self.use_ecx(source_info, |this| {
|
||||
let l = this.ecx.read_immediate(this.ecx.eval_operand(left, None)?)?;
|
||||
let (_, overflow, _ty) = this.ecx.overflowing_binary_op(*op, l, r)?;
|
||||
|
||||
if overflow {
|
||||
let err = err_panic!(Overflow(*op)).into();
|
||||
return Err(err);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
}
|
||||
self.check_binary_op(*op, left, right, source_info, place_layout, overflow_check)?;
|
||||
}
|
||||
|
||||
// Work around: avoid ICE in miri. FIXME(wesleywiser)
|
||||
@ -586,41 +647,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
|
||||
|
||||
Rvalue::Cast(CastKind::Misc, op, ty) => {
|
||||
trace!("checking Cast(Misc, {:?}, {:?})", op, ty);
|
||||
|
||||
if ty.is_integral() && op.ty(&self.local_decls, self.tcx).is_integral() {
|
||||
let value = self.use_ecx(source_info, |this| {
|
||||
this.ecx.read_immediate(this.ecx.eval_operand(op, None)?)
|
||||
})?;
|
||||
|
||||
// Do not try to read bits for ZSTs
|
||||
if !value.layout.is_zst() {
|
||||
let value_size = value.layout.size;
|
||||
let value_bits = value.to_scalar().and_then(|r| r.to_bits(value_size));
|
||||
if let Ok(value_bits) = value_bits {
|
||||
let truncated = truncate(value_bits, place_layout.size);
|
||||
if truncated != value_bits {
|
||||
let scope = source_info.scope;
|
||||
let lint_root = match &self.source_scopes[scope].local_data {
|
||||
ClearCrossCrate::Set(data) => data.lint_root,
|
||||
ClearCrossCrate::Clear => return None,
|
||||
};
|
||||
self.tcx.lint_hir(
|
||||
::rustc::lint::builtin::CONST_ERR,
|
||||
lint_root,
|
||||
span,
|
||||
&format!(
|
||||
"truncating cast: the value {} requires {} bits but \
|
||||
the target type is only {} bits",
|
||||
value_bits,
|
||||
value_size.bits(),
|
||||
place_layout.size.bits()
|
||||
),
|
||||
);
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
self.check_cast(op, ty, source_info, place_layout)?;
|
||||
}
|
||||
|
||||
_ => {}
|
||||
|
Loading…
Reference in New Issue
Block a user