Skip to content

Commit 397a2fd

Browse files
committed
[const-prop] Extract some functions out of _const_prop
1 parent e083273 commit 397a2fd

File tree

1 file changed

+118
-91
lines changed

1 file changed

+118
-91
lines changed

src/librustc_mir/transform/const_prop.rs

+118-91
Original file line numberDiff line numberDiff line change
@@ -469,15 +469,128 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
469469
}
470470
}
471471

472+
fn check_unary_op(&mut self, arg: &Operand<'tcx>, source_info: SourceInfo) -> Option<()> {
473+
self.use_ecx(source_info, |this| {
474+
let ty = arg.ty(&this.local_decls, this.tcx);
475+
476+
if ty.is_integral() {
477+
let arg = this.ecx.eval_operand(arg, None)?;
478+
let prim = this.ecx.read_immediate(arg)?;
479+
// Need to do overflow check here: For actual CTFE, MIR
480+
// generation emits code that does this before calling the op.
481+
if prim.to_bits()? == (1 << (prim.layout.size.bits() - 1)) {
482+
throw_panic!(OverflowNeg)
483+
}
484+
}
485+
486+
Ok(())
487+
})?;
488+
489+
Some(())
490+
}
491+
492+
fn check_binary_op(
493+
&mut self,
494+
op: BinOp,
495+
left: &Operand<'tcx>,
496+
right: &Operand<'tcx>,
497+
source_info: SourceInfo,
498+
place_layout: TyLayout<'tcx>,
499+
overflow_check: bool,
500+
) -> Option<()> {
501+
let r = self.use_ecx(source_info, |this| {
502+
this.ecx.read_immediate(this.ecx.eval_operand(right, None)?)
503+
})?;
504+
if op == BinOp::Shr || op == BinOp::Shl {
505+
let left_bits = place_layout.size.bits();
506+
let right_size = r.layout.size;
507+
let r_bits = r.to_scalar().and_then(|r| r.to_bits(right_size));
508+
if r_bits.map_or(false, |b| b >= left_bits as u128) {
509+
let lint_root = self.lint_root(source_info)?;
510+
let dir = if op == BinOp::Shr { "right" } else { "left" };
511+
self.tcx.lint_hir(
512+
::rustc::lint::builtin::EXCEEDING_BITSHIFTS,
513+
lint_root,
514+
source_info.span,
515+
&format!("attempt to shift {} with overflow", dir),
516+
);
517+
return None;
518+
}
519+
}
520+
521+
// If overflow checking is enabled (like in debug mode by default),
522+
// then we'll already catch overflow when we evaluate the `Assert` statement
523+
// in MIR. However, if overflow checking is disabled, then there won't be any
524+
// `Assert` statement and so we have to do additional checking here.
525+
if !overflow_check {
526+
self.use_ecx(source_info, |this| {
527+
let l = this.ecx.read_immediate(this.ecx.eval_operand(left, None)?)?;
528+
let (_, overflow, _ty) = this.ecx.overflowing_binary_op(op, l, r)?;
529+
530+
if overflow {
531+
let err = err_panic!(Overflow(op)).into();
532+
return Err(err);
533+
}
534+
535+
Ok(())
536+
})?;
537+
}
538+
539+
Some(())
540+
}
541+
542+
fn check_cast(
543+
&mut self,
544+
op: &Operand<'tcx>,
545+
ty: Ty<'tcx>,
546+
source_info: SourceInfo,
547+
place_layout: TyLayout<'tcx>,
548+
) -> Option<()> {
549+
if ty.is_integral() && op.ty(&self.local_decls, self.tcx).is_integral() {
550+
let value = self.use_ecx(source_info, |this| {
551+
this.ecx.read_immediate(this.ecx.eval_operand(op, None)?)
552+
})?;
553+
554+
// Do not try to read bits for ZSTs
555+
if !value.layout.is_zst() {
556+
let value_size = value.layout.size;
557+
let value_bits = value.to_scalar().and_then(|r| r.to_bits(value_size));
558+
if let Ok(value_bits) = value_bits {
559+
let truncated = truncate(value_bits, place_layout.size);
560+
if truncated != value_bits {
561+
let scope = source_info.scope;
562+
let lint_root = match &self.source_scopes[scope].local_data {
563+
ClearCrossCrate::Set(data) => data.lint_root,
564+
ClearCrossCrate::Clear => return None,
565+
};
566+
self.tcx.lint_hir(
567+
::rustc::lint::builtin::CONST_ERR,
568+
lint_root,
569+
source_info.span,
570+
&format!(
571+
"truncating cast: the value {} requires {} bits but \
572+
the target type is only {} bits",
573+
value_bits,
574+
value_size.bits(),
575+
place_layout.size.bits()
576+
),
577+
);
578+
return None;
579+
}
580+
}
581+
}
582+
}
583+
584+
Some(())
585+
}
586+
472587
fn const_prop(
473588
&mut self,
474589
rvalue: &Rvalue<'tcx>,
475590
place_layout: TyLayout<'tcx>,
476591
source_info: SourceInfo,
477592
place: &Place<'tcx>,
478593
) -> Option<()> {
479-
let span = source_info.span;
480-
481594
// #66397: Don't try to eval into large places as that can cause an OOM
482595
if place_layout.size >= Size::from_bytes(MAX_ALLOC_LIMIT) {
483596
return None;
@@ -498,66 +611,14 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
498611
// if an overflow would occur.
499612
Rvalue::UnaryOp(UnOp::Neg, arg) if !overflow_check => {
500613
trace!("checking UnaryOp(op = Neg, arg = {:?})", arg);
501-
502-
self.use_ecx(source_info, |this| {
503-
let ty = arg.ty(&this.local_decls, this.tcx);
504-
505-
if ty.is_integral() {
506-
let arg = this.ecx.eval_operand(arg, None)?;
507-
let prim = this.ecx.read_immediate(arg)?;
508-
// Need to do overflow check here: For actual CTFE, MIR
509-
// generation emits code that does this before calling the op.
510-
if prim.to_bits()? == (1 << (prim.layout.size.bits() - 1)) {
511-
throw_panic!(OverflowNeg)
512-
}
513-
}
514-
515-
Ok(())
516-
})?;
614+
self.check_unary_op(arg, source_info)?;
517615
}
518616

519617
// Additional checking: check for overflows on integer binary operations and report
520618
// them to the user as lints.
521619
Rvalue::BinaryOp(op, left, right) => {
522620
trace!("checking BinaryOp(op = {:?}, left = {:?}, right = {:?})", op, left, right);
523-
524-
let r = self.use_ecx(source_info, |this| {
525-
this.ecx.read_immediate(this.ecx.eval_operand(right, None)?)
526-
})?;
527-
if *op == BinOp::Shr || *op == BinOp::Shl {
528-
let left_bits = place_layout.size.bits();
529-
let right_size = r.layout.size;
530-
let r_bits = r.to_scalar().and_then(|r| r.to_bits(right_size));
531-
if r_bits.map_or(false, |b| b >= left_bits as u128) {
532-
let lint_root = self.lint_root(source_info)?;
533-
let dir = if *op == BinOp::Shr { "right" } else { "left" };
534-
self.tcx.lint_hir(
535-
::rustc::lint::builtin::EXCEEDING_BITSHIFTS,
536-
lint_root,
537-
span,
538-
&format!("attempt to shift {} with overflow", dir),
539-
);
540-
return None;
541-
}
542-
}
543-
544-
// If overflow checking is enabled (like in debug mode by default),
545-
// then we'll already catch overflow when we evaluate the `Assert` statement
546-
// in MIR. However, if overflow checking is disabled, then there won't be any
547-
// `Assert` statement and so we have to do additional checking here.
548-
if !overflow_check {
549-
self.use_ecx(source_info, |this| {
550-
let l = this.ecx.read_immediate(this.ecx.eval_operand(left, None)?)?;
551-
let (_, overflow, _ty) = this.ecx.overflowing_binary_op(*op, l, r)?;
552-
553-
if overflow {
554-
let err = err_panic!(Overflow(*op)).into();
555-
return Err(err);
556-
}
557-
558-
Ok(())
559-
})?;
560-
}
621+
self.check_binary_op(*op, left, right, source_info, place_layout, overflow_check)?;
561622
}
562623

563624
// Work around: avoid ICE in miri. FIXME(wesleywiser)
@@ -586,41 +647,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
586647

587648
Rvalue::Cast(CastKind::Misc, op, ty) => {
588649
trace!("checking Cast(Misc, {:?}, {:?})", op, ty);
589-
590-
if ty.is_integral() && op.ty(&self.local_decls, self.tcx).is_integral() {
591-
let value = self.use_ecx(source_info, |this| {
592-
this.ecx.read_immediate(this.ecx.eval_operand(op, None)?)
593-
})?;
594-
595-
// Do not try to read bits for ZSTs
596-
if !value.layout.is_zst() {
597-
let value_size = value.layout.size;
598-
let value_bits = value.to_scalar().and_then(|r| r.to_bits(value_size));
599-
if let Ok(value_bits) = value_bits {
600-
let truncated = truncate(value_bits, place_layout.size);
601-
if truncated != value_bits {
602-
let scope = source_info.scope;
603-
let lint_root = match &self.source_scopes[scope].local_data {
604-
ClearCrossCrate::Set(data) => data.lint_root,
605-
ClearCrossCrate::Clear => return None,
606-
};
607-
self.tcx.lint_hir(
608-
::rustc::lint::builtin::CONST_ERR,
609-
lint_root,
610-
span,
611-
&format!(
612-
"truncating cast: the value {} requires {} bits but \
613-
the target type is only {} bits",
614-
value_bits,
615-
value_size.bits(),
616-
place_layout.size.bits()
617-
),
618-
);
619-
return None;
620-
}
621-
}
622-
}
623-
}
650+
self.check_cast(op, ty, source_info, place_layout)?;
624651
}
625652

626653
_ => {}

0 commit comments

Comments
 (0)