Skip to content

Commit aa3a9ea

Browse files
committed
interpret: make overflowing binops just normal binops
1 parent 9cb6bb8 commit aa3a9ea

40 files changed

+323
-349
lines changed

Diff for: compiler/rustc_const_eval/messages.ftl

+3-4
Original file line numberDiff line numberDiff line change
@@ -246,11 +246,10 @@ const_eval_offset_from_unsigned_overflow =
246246
247247
const_eval_operator_non_const =
248248
cannot call non-const operator in {const_eval_const_context}s
249-
const_eval_overflow =
250-
overflow executing `{$name}`
251-
249+
const_eval_overflow_arith =
250+
arithmetic overflow in `{$intrinsic}`
252251
const_eval_overflow_shift =
253-
overflowing shift by {$val} in `{$name}`
252+
overflowing shift by {$shift_amount} in `{$intrinsic}`
254253
255254
const_eval_panic =
256255
the evaluated program panicked at '{$msg}', {$file}:{$line}:{$col}

Diff for: compiler/rustc_const_eval/src/const_eval/dummy_machine.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ impl<'mir, 'tcx: 'mir> interpret::Machine<'mir, 'tcx> for DummyMachine {
125125
bin_op: BinOp,
126126
left: &interpret::ImmTy<'tcx, Self::Provenance>,
127127
right: &interpret::ImmTy<'tcx, Self::Provenance>,
128-
) -> interpret::InterpResult<'tcx, (ImmTy<'tcx, Self::Provenance>, bool)> {
128+
) -> interpret::InterpResult<'tcx, ImmTy<'tcx, Self::Provenance>> {
129129
use rustc_middle::mir::BinOp::*;
130130
Ok(match bin_op {
131131
Eq | Ne | Lt | Le | Gt | Ge => {
@@ -154,7 +154,7 @@ impl<'mir, 'tcx: 'mir> interpret::Machine<'mir, 'tcx> for DummyMachine {
154154
Ge => left >= right,
155155
_ => bug!(),
156156
};
157-
(ImmTy::from_bool(res, *ecx.tcx), false)
157+
ImmTy::from_bool(res, *ecx.tcx)
158158
}
159159

160160
// Some more operations are possible with atomics.

Diff for: compiler/rustc_const_eval/src/const_eval/machine.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -589,7 +589,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
589589
_bin_op: mir::BinOp,
590590
_left: &ImmTy<'tcx>,
591591
_right: &ImmTy<'tcx>,
592-
) -> InterpResult<'tcx, (ImmTy<'tcx>, bool)> {
592+
) -> InterpResult<'tcx, ImmTy<'tcx>> {
593593
throw_unsup_format!("pointer arithmetic or comparison is not supported at compile-time");
594594
}
595595

Diff for: compiler/rustc_const_eval/src/errors.rs

+16
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use std::borrow::Cow;
22

3+
use either::Either;
34
use rustc_errors::{
45
codes::*, Diag, DiagArgValue, DiagCtxt, DiagMessage, Diagnostic, EmissionGuarantee, Level,
56
};
@@ -481,6 +482,8 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
481482
DivisionOverflow => const_eval_division_overflow,
482483
RemainderOverflow => const_eval_remainder_overflow,
483484
PointerArithOverflow => const_eval_pointer_arithmetic_overflow,
485+
ArithOverflow { .. } => const_eval_overflow_arith,
486+
ShiftOverflow { .. } => const_eval_overflow_shift,
484487
InvalidMeta(InvalidMetaKind::SliceTooBig) => const_eval_invalid_meta_slice,
485488
InvalidMeta(InvalidMetaKind::TooBig) => const_eval_invalid_meta,
486489
UnterminatedCString(_) => const_eval_unterminated_c_string,
@@ -539,6 +542,19 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
539542
| UninhabitedEnumVariantWritten(_)
540543
| UninhabitedEnumVariantRead(_) => {}
541544

545+
ArithOverflow { intrinsic } => {
546+
diag.arg("intrinsic", intrinsic);
547+
}
548+
ShiftOverflow { intrinsic, shift_amount } => {
549+
diag.arg("intrinsic", intrinsic);
550+
diag.arg(
551+
"shift_amount",
552+
match shift_amount {
553+
Either::Left(v) => v.to_string(),
554+
Either::Right(v) => v.to_string(),
555+
},
556+
);
557+
}
542558
BoundsCheckFailed { len, index } => {
543559
diag.arg("len", len);
544560
diag.arg("index", index);

Diff for: compiler/rustc_const_eval/src/interpret/discriminant.rs

+2-6
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
172172
let tag_val = ImmTy::from_uint(tag_bits, tag_layout);
173173
let niche_start_val = ImmTy::from_uint(niche_start, tag_layout);
174174
let variant_index_relative_val =
175-
self.wrapping_binary_op(mir::BinOp::Sub, &tag_val, &niche_start_val)?;
175+
self.binary_op(mir::BinOp::Sub, &tag_val, &niche_start_val)?;
176176
let variant_index_relative =
177177
variant_index_relative_val.to_scalar().assert_bits(tag_val.layout.size);
178178
// Check if this is in the range that indicates an actual discriminant.
@@ -292,11 +292,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
292292
let variant_index_relative_val =
293293
ImmTy::from_uint(variant_index_relative, tag_layout);
294294
let tag = self
295-
.wrapping_binary_op(
296-
mir::BinOp::Add,
297-
&variant_index_relative_val,
298-
&niche_start_val,
299-
)?
295+
.binary_op(mir::BinOp::Add, &variant_index_relative_val, &niche_start_val)?
300296
.to_scalar()
301297
.assert_int();
302298
Ok(Some((tag, tag_field)))

Diff for: compiler/rustc_const_eval/src/interpret/intrinsics.rs

+13-11
Original file line numberDiff line numberDiff line change
@@ -285,9 +285,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
285285
let (val, overflowed) = {
286286
let a_offset = ImmTy::from_uint(a_offset, usize_layout);
287287
let b_offset = ImmTy::from_uint(b_offset, usize_layout);
288-
self.overflowing_binary_op(BinOp::Sub, &a_offset, &b_offset)?
288+
self.binary_op(BinOp::SubWithOverflow, &a_offset, &b_offset)?
289+
.to_scalar_pair()
289290
};
290-
if overflowed {
291+
if overflowed.to_bool()? {
291292
// a < b
292293
if intrinsic_name == sym::ptr_offset_from_unsigned {
293294
throw_ub_custom!(
@@ -299,7 +300,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
299300
// The signed form of the intrinsic allows this. If we interpret the
300301
// difference as isize, we'll get the proper signed difference. If that
301302
// seems *positive*, they were more than isize::MAX apart.
302-
let dist = val.to_scalar().to_target_isize(self)?;
303+
let dist = val.to_target_isize(self)?;
303304
if dist >= 0 {
304305
throw_ub_custom!(
305306
fluent::const_eval_offset_from_underflow,
@@ -309,7 +310,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
309310
dist
310311
} else {
311312
// b >= a
312-
let dist = val.to_scalar().to_target_isize(self)?;
313+
let dist = val.to_target_isize(self)?;
313314
// If converting to isize produced a *negative* result, we had an overflow
314315
// because they were more than isize::MAX apart.
315316
if dist < 0 {
@@ -515,17 +516,17 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
515516
// Performs an exact division, resulting in undefined behavior where
516517
// `x % y != 0` or `y == 0` or `x == T::MIN && y == -1`.
517518
// First, check x % y != 0 (or if that computation overflows).
518-
let (res, overflow) = self.overflowing_binary_op(BinOp::Rem, a, b)?;
519-
assert!(!overflow); // All overflow is UB, so this should never return on overflow.
520-
if res.to_scalar().assert_bits(a.layout.size) != 0 {
519+
let rem = self.binary_op(BinOp::Rem, a, b)?;
520+
if rem.to_scalar().assert_bits(a.layout.size) != 0 {
521521
throw_ub_custom!(
522522
fluent::const_eval_exact_div_has_remainder,
523523
a = format!("{a}"),
524524
b = format!("{b}")
525525
)
526526
}
527527
// `Rem` says this is all right, so we can let `Div` do its job.
528-
self.binop_ignore_overflow(BinOp::Div, a, b, &dest.clone().into())
528+
let res = self.binary_op(BinOp::Div, a, b)?;
529+
self.write_immediate(*res, dest)
529530
}
530531

531532
pub fn saturating_arith(
@@ -538,8 +539,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
538539
assert!(matches!(l.layout.ty.kind(), ty::Int(..) | ty::Uint(..)));
539540
assert!(matches!(mir_op, BinOp::Add | BinOp::Sub));
540541

541-
let (val, overflowed) = self.overflowing_binary_op(mir_op, l, r)?;
542-
Ok(if overflowed {
542+
let (val, overflowed) =
543+
self.binary_op(mir_op.wrapping_to_overflowing().unwrap(), l, r)?.to_scalar_pair();
544+
Ok(if overflowed.to_bool()? {
543545
let size = l.layout.size;
544546
let num_bits = size.bits();
545547
if l.layout.abi.is_signed() {
@@ -570,7 +572,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
570572
}
571573
}
572574
} else {
573-
val.to_scalar()
575+
val
574576
})
575577
}
576578

Diff for: compiler/rustc_const_eval/src/interpret/machine.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized {
252252
bin_op: mir::BinOp,
253253
left: &ImmTy<'tcx, Self::Provenance>,
254254
right: &ImmTy<'tcx, Self::Provenance>,
255-
) -> InterpResult<'tcx, (ImmTy<'tcx, Self::Provenance>, bool)>;
255+
) -> InterpResult<'tcx, ImmTy<'tcx, Self::Provenance>>;
256256

257257
/// Generate the NaN returned by a float operation, given the list of inputs.
258258
/// (This is all inputs, not just NaN inputs!)

Diff for: compiler/rustc_const_eval/src/interpret/operand.rs

+21-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use either::{Either, Left, Right};
77

88
use rustc_hir::def::Namespace;
99
use rustc_middle::mir::interpret::ScalarSizeMismatch;
10-
use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
10+
use rustc_middle::ty::layout::{HasParamEnv, HasTyCtxt, LayoutOf, TyAndLayout};
1111
use rustc_middle::ty::print::{FmtPrinter, PrettyPrinter};
1212
use rustc_middle::ty::{ConstInt, ScalarInt, Ty, TyCtxt};
1313
use rustc_middle::{bug, span_bug};
@@ -249,6 +249,15 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> {
249249
Self::from_scalar(Scalar::from_i8(c as i8), layout)
250250
}
251251

252+
pub fn from_pair(a: Self, b: Self, tcx: TyCtxt<'tcx>) -> Self {
253+
let layout = tcx
254+
.layout_of(
255+
ty::ParamEnv::reveal_all().and(Ty::new_tup(tcx, &[a.layout.ty, b.layout.ty])),
256+
)
257+
.unwrap();
258+
Self::from_scalar_pair(a.to_scalar(), b.to_scalar(), layout)
259+
}
260+
252261
/// Return the immediate as a `ScalarInt`. Ensures that it has the size that the layout of the
253262
/// immediate indicates.
254263
#[inline]
@@ -270,6 +279,17 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> {
270279
ConstInt::new(int, self.layout.ty.is_signed(), self.layout.ty.is_ptr_sized_integral())
271280
}
272281

282+
#[inline]
283+
#[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980)
284+
pub fn to_pair(self, cx: &(impl HasTyCtxt<'tcx> + HasParamEnv<'tcx>)) -> (Self, Self) {
285+
let layout = self.layout;
286+
let (val0, val1) = self.to_scalar_pair();
287+
(
288+
ImmTy::from_scalar(val0, layout.field(cx, 0)),
289+
ImmTy::from_scalar(val1, layout.field(cx, 1)),
290+
)
291+
}
292+
273293
/// Compute the "sub-immediate" that is located within the `base` at the given offset with the
274294
/// given layout.
275295
// Not called `offset` to avoid confusion with the trait method.

0 commit comments

Comments
 (0)