about summary refs log tree commit diff
path: root/compiler/rustc_const_eval/src
diff options
context:
space:
mode:
authorScott McMurray <scottmcm@users.noreply.github.com>2023-06-03 00:41:50 -0700
committerScott McMurray <scottmcm@users.noreply.github.com>2023-06-19 01:47:03 -0700
commit39788e07bae91e9974ba6879a63e4e8b97503f5b (patch)
tree2b1d01608304f228b79e764cb41831c29dcfa604 /compiler/rustc_const_eval/src
parent8d1fa473dddd12efb2430302e5f5dfcc3eb73f8b (diff)
downloadrust-39788e07bae91e9974ba6879a63e4e8b97503f5b.tar.gz
rust-39788e07bae91e9974ba6879a63e4e8b97503f5b.zip
Promote unchecked_add/sub/mul/shl/shr to mir::BinOp
Diffstat (limited to 'compiler/rustc_const_eval/src')
-rw-r--r--compiler/rustc_const_eval/src/interpret/operator.rs66
-rw-r--r--compiler/rustc_const_eval/src/interpret/step.rs8
-rw-r--r--compiler/rustc_const_eval/src/transform/promote_consts.rs7
-rw-r--r--compiler/rustc_const_eval/src/transform/validate.rs20
4 files changed, 74 insertions, 27 deletions
diff --git a/compiler/rustc_const_eval/src/interpret/operator.rs b/compiler/rustc_const_eval/src/interpret/operator.rs
index 7186148daf0..7bca7efdf5a 100644
--- a/compiler/rustc_const_eval/src/interpret/operator.rs
+++ b/compiler/rustc_const_eval/src/interpret/operator.rs
@@ -3,10 +3,13 @@ use rustc_middle::mir;
 use rustc_middle::mir::interpret::{InterpResult, Scalar};
 use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
 use rustc_middle::ty::{self, FloatTy, Ty};
+use rustc_span::symbol::sym;
 use rustc_target::abi::Abi;
 
 use super::{ImmTy, Immediate, InterpCx, Machine, PlaceTy};
 
+use crate::fluent_generated as fluent;
+
 impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     /// Applies the binary operation `op` to the two operands and writes a tuple of the result
     /// and a boolean signifying the potential overflow to the destination.
@@ -139,8 +142,17 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     ) -> InterpResult<'tcx, (Scalar<M::Provenance>, bool, Ty<'tcx>)> {
         use rustc_middle::mir::BinOp::*;
 
+        let throw_ub_on_overflow = match bin_op {
+            AddUnchecked => Some(sym::unchecked_add),
+            SubUnchecked => Some(sym::unchecked_sub),
+            MulUnchecked => Some(sym::unchecked_mul),
+            ShlUnchecked => Some(sym::unchecked_shl),
+            ShrUnchecked => Some(sym::unchecked_shr),
+            _ => None,
+        };
+
         // Shift ops can have an RHS with a different numeric type.
-        if bin_op == Shl || bin_op == Shr {
+        if matches!(bin_op, Shl | ShlUnchecked | Shr | ShrUnchecked) {
             let size = u128::from(left_layout.size.bits());
             // Even if `r` is signed, we treat it as if it was unsigned (i.e., we use its
             // zero-extended form). This matches the codegen backend:
@@ -155,6 +167,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
             // integers are maximally 128bits wide, so negative shifts *always* overflow and we have
             // consistent results for the same value represented at different bit widths.
             assert!(size <= 128);
+            let original_r = r;
             let overflow = r >= size;
             // The shift offset is implicitly masked to the type size, to make sure this operation
             // is always defined. This is the one MIR operator that does *not* directly map to a
@@ -166,19 +179,28 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
             let result = if left_layout.abi.is_signed() {
                 let l = self.sign_extend(l, left_layout) as i128;
                 let result = match bin_op {
-                    Shl => l.checked_shl(r).unwrap(),
-                    Shr => l.checked_shr(r).unwrap(),
+                    Shl | ShlUnchecked => l.checked_shl(r).unwrap(),
+                    Shr | ShrUnchecked => l.checked_shr(r).unwrap(),
                     _ => bug!(),
                 };
                 result as u128
             } else {
                 match bin_op {
-                    Shl => l.checked_shl(r).unwrap(),
-                    Shr => l.checked_shr(r).unwrap(),
+                    Shl | ShlUnchecked => l.checked_shl(r).unwrap(),
+                    Shr | ShrUnchecked => l.checked_shr(r).unwrap(),
                     _ => bug!(),
                 }
             };
             let truncated = self.truncate(result, left_layout);
+
+            if overflow && let Some(intrinsic_name) = throw_ub_on_overflow {
+                throw_ub_custom!(
+                    fluent::const_eval_overflow_shift,
+                    val = original_r,
+                    name = intrinsic_name
+                );
+            }
+
             return Ok((Scalar::from_uint(truncated, left_layout.size), overflow, left_layout.ty));
         }
 
@@ -216,9 +238,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 Rem if r == 0 => throw_ub!(RemainderByZero),
                 Div => Some(i128::overflowing_div),
                 Rem => Some(i128::overflowing_rem),
-                Add => Some(i128::overflowing_add),
-                Sub => Some(i128::overflowing_sub),
-                Mul => Some(i128::overflowing_mul),
+                Add | AddUnchecked => Some(i128::overflowing_add),
+                Sub | SubUnchecked => Some(i128::overflowing_sub),
+                Mul | MulUnchecked => Some(i128::overflowing_mul),
                 _ => None,
             };
             if let Some(op) = op {
@@ -242,11 +264,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 // If that truncation loses any information, we have an overflow.
                 let result = result as u128;
                 let truncated = self.truncate(result, left_layout);
-                return Ok((
-                    Scalar::from_uint(truncated, size),
-                    oflo || self.sign_extend(truncated, left_layout) != result,
-                    left_layout.ty,
-                ));
+                let overflow = oflo || self.sign_extend(truncated, left_layout) != result;
+                if overflow && let Some(intrinsic_name) = throw_ub_on_overflow {
+                    throw_ub_custom!(fluent::const_eval_overflow, name = intrinsic_name);
+                }
+                return Ok((Scalar::from_uint(truncated, size), overflow, left_layout.ty));
             }
         }
 
@@ -263,12 +285,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
             BitAnd => (Scalar::from_uint(l & r, size), left_layout.ty),
             BitXor => (Scalar::from_uint(l ^ r, size), left_layout.ty),
 
-            Add | Sub | Mul | Rem | Div => {
+            Add | AddUnchecked | Sub | SubUnchecked | Mul | MulUnchecked | Rem | Div => {
                 assert!(!left_layout.abi.is_signed());
                 let op: fn(u128, u128) -> (u128, bool) = match bin_op {
-                    Add => u128::overflowing_add,
-                    Sub => u128::overflowing_sub,
-                    Mul => u128::overflowing_mul,
+                    Add | AddUnchecked => u128::overflowing_add,
+                    Sub | SubUnchecked => u128::overflowing_sub,
+                    Mul | MulUnchecked => u128::overflowing_mul,
                     Div if r == 0 => throw_ub!(DivisionByZero),
                     Rem if r == 0 => throw_ub!(RemainderByZero),
                     Div => u128::overflowing_div,
@@ -279,11 +301,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 // Truncate to target type.
                 // If that truncation loses any information, we have an overflow.
                 let truncated = self.truncate(result, left_layout);
-                return Ok((
-                    Scalar::from_uint(truncated, size),
-                    oflo || truncated != result,
-                    left_layout.ty,
-                ));
+                let overflow = oflo || truncated != result;
+                if overflow && let Some(intrinsic_name) = throw_ub_on_overflow {
+                    throw_ub_custom!(fluent::const_eval_overflow, name = intrinsic_name);
+                }
+                return Ok((Scalar::from_uint(truncated, size), overflow, left_layout.ty));
             }
 
             _ => span_bug!(
diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs
index 1e60a1e72ea..050ed291b4f 100644
--- a/compiler/rustc_const_eval/src/interpret/step.rs
+++ b/compiler/rustc_const_eval/src/interpret/step.rs
@@ -16,7 +16,8 @@ use super::{ImmTy, InterpCx, Machine};
 fn binop_left_homogeneous(op: mir::BinOp) -> bool {
     use rustc_middle::mir::BinOp::*;
     match op {
-        Add | Sub | Mul | Div | Rem | BitXor | BitAnd | BitOr | Offset | Shl | Shr => true,
+        Add | AddUnchecked | Sub | SubUnchecked | Mul | MulUnchecked | Div | Rem | BitXor
+        | BitAnd | BitOr | Offset | Shl | ShlUnchecked | Shr | ShrUnchecked => true,
         Eq | Ne | Lt | Le | Gt | Ge => false,
     }
 }
@@ -26,8 +27,9 @@ fn binop_left_homogeneous(op: mir::BinOp) -> bool {
 fn binop_right_homogeneous(op: mir::BinOp) -> bool {
     use rustc_middle::mir::BinOp::*;
     match op {
-        Add | Sub | Mul | Div | Rem | BitXor | BitAnd | BitOr | Eq | Ne | Lt | Le | Gt | Ge => true,
-        Offset | Shl | Shr => false,
+        Add | AddUnchecked | Sub | SubUnchecked | Mul | MulUnchecked | Div | Rem | BitXor
+        | BitAnd | BitOr | Eq | Ne | Lt | Le | Gt | Ge => true,
+        Offset | Shl | ShlUnchecked | Shr | ShrUnchecked => false,
     }
 }
 
diff --git a/compiler/rustc_const_eval/src/transform/promote_consts.rs b/compiler/rustc_const_eval/src/transform/promote_consts.rs
index 44b143c77f3..3802df5d3fc 100644
--- a/compiler/rustc_const_eval/src/transform/promote_consts.rs
+++ b/compiler/rustc_const_eval/src/transform/promote_consts.rs
@@ -569,13 +569,18 @@ impl<'tcx> Validator<'_, 'tcx> {
                     | BinOp::Gt
                     | BinOp::Offset
                     | BinOp::Add
+                    | BinOp::AddUnchecked
                     | BinOp::Sub
+                    | BinOp::SubUnchecked
                     | BinOp::Mul
+                    | BinOp::MulUnchecked
                     | BinOp::BitXor
                     | BinOp::BitAnd
                     | BinOp::BitOr
                     | BinOp::Shl
-                    | BinOp::Shr => {}
+                    | BinOp::ShlUnchecked
+                    | BinOp::Shr
+                    | BinOp::ShrUnchecked => {}
                 }
 
                 self.validate_operand(lhs)?;
diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs
index 3c350e25ba6..d421c7a2755 100644
--- a/compiler/rustc_const_eval/src/transform/validate.rs
+++ b/compiler/rustc_const_eval/src/transform/validate.rs
@@ -556,7 +556,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
                             );
                         }
                     }
-                    Shl | Shr => {
+                    Shl | ShlUnchecked | Shr | ShrUnchecked => {
                         for x in [a, b] {
                             check_kinds!(
                                 x,
@@ -601,6 +601,24 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
                             );
                         }
                     }
+                    AddUnchecked | SubUnchecked | MulUnchecked => {
+                        for x in [a, b] {
+                            check_kinds!(
+                                x,
+                                "Cannot perform unchecked arithmetic on type {:?}",
+                                ty::Uint(..) | ty::Int(..)
+                            )
+                        }
+                        if a != b {
+                            self.fail(
+                                location,
+                                format!(
+                                    "Cannot perform unchecked arithmetic on unequal types {:?} and {:?}",
+                                    a, b
+                                ),
+                            );
+                        }
+                    }
                 }
             }
             Rvalue::CheckedBinaryOp(op, vals) => {