Skip to content

Commit c90eb48

Browse files
committed
Auto merge of rust-lang#108282 - cjgillot:mir-checked-sh, r=tmiasko
Implement checked Shl/Shr at MIR building. This does not require any special handling by codegen backends, as the overflow behaviour is entirely determined by the rhs (shift amount). This allows MIR ConstProp to remove the overflow check for constant shifts. ~There is an existing different behaviour between cg_llvm and cg_clif (cc `@bjorn3).` I took cg_llvm's one as reference: overflow if `rhs < 0 || rhs > number_of_bits_in_lhs_ty`.~ EDIT: `cg_llvm` and `cg_clif` implement the overflow check differently. This PR uses `cg_llvm`'s implementation based on a `BitAnd` instead of `cg_clif`'s one based on an unsigned comparison.
2 parents ab65486 + 0422f44 commit c90eb48

File tree

8 files changed

+109
-111
lines changed

8 files changed

+109
-111
lines changed

compiler/rustc_codegen_cranelift/src/num.rs

-23
Original file line numberDiff line numberDiff line change
@@ -170,14 +170,6 @@ pub(crate) fn codegen_checked_int_binop<'tcx>(
170170
in_lhs: CValue<'tcx>,
171171
in_rhs: CValue<'tcx>,
172172
) -> CValue<'tcx> {
173-
if bin_op != BinOp::Shl && bin_op != BinOp::Shr {
174-
assert_eq!(
175-
in_lhs.layout().ty,
176-
in_rhs.layout().ty,
177-
"checked int binop requires lhs and rhs of same type"
178-
);
179-
}
180-
181173
let lhs = in_lhs.load_scalar(fx);
182174
let rhs = in_rhs.load_scalar(fx);
183175

@@ -271,21 +263,6 @@ pub(crate) fn codegen_checked_int_binop<'tcx>(
271263
_ => unreachable!("invalid non-integer type {}", ty),
272264
}
273265
}
274-
BinOp::Shl => {
275-
let val = fx.bcx.ins().ishl(lhs, rhs);
276-
let ty = fx.bcx.func.dfg.value_type(val);
277-
let max_shift = i64::from(ty.bits()) - 1;
278-
let has_overflow = fx.bcx.ins().icmp_imm(IntCC::UnsignedGreaterThan, rhs, max_shift);
279-
(val, has_overflow)
280-
}
281-
BinOp::Shr => {
282-
let val =
283-
if !signed { fx.bcx.ins().ushr(lhs, rhs) } else { fx.bcx.ins().sshr(lhs, rhs) };
284-
let ty = fx.bcx.func.dfg.value_type(val);
285-
let max_shift = i64::from(ty.bits()) - 1;
286-
let has_overflow = fx.bcx.ins().icmp_imm(IntCC::UnsignedGreaterThan, rhs, max_shift);
287-
(val, has_overflow)
288-
}
289266
_ => bug!("binop {:?} on checked int/uint lhs: {:?} rhs: {:?}", bin_op, in_lhs, in_rhs),
290267
};
291268

compiler/rustc_codegen_ssa/src/mir/rvalue.rs

-11
Original file line numberDiff line numberDiff line change
@@ -663,17 +663,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
663663
};
664664
bx.checked_binop(oop, input_ty, lhs, rhs)
665665
}
666-
mir::BinOp::Shl | mir::BinOp::Shr => {
667-
let lhs_llty = bx.cx().val_ty(lhs);
668-
let rhs_llty = bx.cx().val_ty(rhs);
669-
let invert_mask = common::shift_mask_val(bx, lhs_llty, rhs_llty, true);
670-
let outer_bits = bx.and(rhs, invert_mask);
671-
672-
let of = bx.icmp(IntPredicate::IntNE, outer_bits, bx.cx().const_null(rhs_llty));
673-
let val = self.codegen_scalar_binop(bx, op, lhs, rhs, input_ty);
674-
675-
(val, of)
676-
}
677666
_ => bug!("Operator `{:?}` is not a checkable operator", op),
678667
};
679668

compiler/rustc_const_eval/src/transform/validate.rs

-9
Original file line numberDiff line numberDiff line change
@@ -564,15 +564,6 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
564564
);
565565
}
566566
}
567-
Shl | Shr => {
568-
for x in [a, b] {
569-
check_kinds!(
570-
x,
571-
"Cannot perform checked shift on non-integer type {:?}",
572-
ty::Uint(..) | ty::Int(..)
573-
)
574-
}
575-
}
576567
_ => self.fail(location, format!("There is no checked version of {:?}", op)),
577568
}
578569
}

compiler/rustc_middle/src/mir/syntax.rs

-4
Original file line numberDiff line numberDiff line change
@@ -1083,10 +1083,6 @@ pub enum Rvalue<'tcx> {
10831083
/// For addition, subtraction, and multiplication on integers the error condition is set when
10841084
/// the infinite precision result would be unequal to the actual result.
10851085
///
1086-
/// For shift operations on integers the error condition is set when the value of right-hand
1087-
/// side is greater than or equal to the number of bits in the type of the left-hand side, or
1088-
/// when the value of right-hand side is negative.
1089-
///
10901086
/// Other combinations of types and operators are unsupported.
10911087
CheckedBinaryOp(BinOp, Box<(Operand<'tcx>, Operand<'tcx>)>),
10921088

compiler/rustc_middle/src/ty/util.rs

+12-12
Original file line numberDiff line numberDiff line change
@@ -60,22 +60,13 @@ impl<'tcx> fmt::Display for Discr<'tcx> {
6060
}
6161
}
6262

63-
fn int_size_and_signed<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> (Size, bool) {
64-
let (int, signed) = match *ty.kind() {
65-
ty::Int(ity) => (Integer::from_int_ty(&tcx, ity), true),
66-
ty::Uint(uty) => (Integer::from_uint_ty(&tcx, uty), false),
67-
_ => bug!("non integer discriminant"),
68-
};
69-
(int.size(), signed)
70-
}
71-
7263
impl<'tcx> Discr<'tcx> {
7364
/// Adds `1` to the value and wraps around if the maximum for the type is reached.
7465
pub fn wrap_incr(self, tcx: TyCtxt<'tcx>) -> Self {
7566
self.checked_add(tcx, 1).0
7667
}
7768
pub fn checked_add(self, tcx: TyCtxt<'tcx>, n: u128) -> (Self, bool) {
78-
let (size, signed) = int_size_and_signed(tcx, self.ty);
69+
let (size, signed) = self.ty.int_size_and_signed(tcx);
7970
let (val, oflo) = if signed {
8071
let min = size.signed_int_min();
8172
let max = size.signed_int_max();
@@ -929,12 +920,21 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for OpaqueTypeExpander<'tcx> {
929920
}
930921

931922
impl<'tcx> Ty<'tcx> {
923+
pub fn int_size_and_signed(self, tcx: TyCtxt<'tcx>) -> (Size, bool) {
924+
let (int, signed) = match *self.kind() {
925+
ty::Int(ity) => (Integer::from_int_ty(&tcx, ity), true),
926+
ty::Uint(uty) => (Integer::from_uint_ty(&tcx, uty), false),
927+
_ => bug!("non integer discriminant"),
928+
};
929+
(int.size(), signed)
930+
}
931+
932932
/// Returns the maximum value for the given numeric type (including `char`s)
933933
/// or returns `None` if the type is not numeric.
934934
pub fn numeric_max_val(self, tcx: TyCtxt<'tcx>) -> Option<ty::Const<'tcx>> {
935935
let val = match self.kind() {
936936
ty::Int(_) | ty::Uint(_) => {
937-
let (size, signed) = int_size_and_signed(tcx, self);
937+
let (size, signed) = self.int_size_and_signed(tcx);
938938
let val =
939939
if signed { size.signed_int_max() as u128 } else { size.unsigned_int_max() };
940940
Some(val)
@@ -955,7 +955,7 @@ impl<'tcx> Ty<'tcx> {
955955
pub fn numeric_min_val(self, tcx: TyCtxt<'tcx>) -> Option<ty::Const<'tcx>> {
956956
let val = match self.kind() {
957957
ty::Int(_) | ty::Uint(_) => {
958-
let (size, signed) = int_size_and_signed(tcx, self);
958+
let (size, signed) = self.int_size_and_signed(tcx);
959959
let val = if signed { size.truncate(size.signed_int_min() as u128) } else { 0 };
960960
Some(val)
961961
}

compiler/rustc_mir_build/src/build/expr/as_rvalue.rs

+63-22
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use crate::build::expr::category::{Category, RvalueFunc};
99
use crate::build::{BlockAnd, BlockAndExtension, Builder, NeedsTemporary};
1010
use rustc_hir::lang_items::LangItem;
1111
use rustc_middle::middle::region;
12+
use rustc_middle::mir::interpret::Scalar;
1213
use rustc_middle::mir::AssertKind;
1314
use rustc_middle::mir::Place;
1415
use rustc_middle::mir::*;
@@ -519,30 +520,68 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
519520
) -> BlockAnd<Rvalue<'tcx>> {
520521
let source_info = self.source_info(span);
521522
let bool_ty = self.tcx.types.bool;
522-
if self.check_overflow && op.is_checkable() && ty.is_integral() {
523-
let result_tup = self.tcx.mk_tup(&[ty, bool_ty]);
524-
let result_value = self.temp(result_tup, span);
523+
let rvalue = match op {
524+
BinOp::Add | BinOp::Sub | BinOp::Mul if self.check_overflow && ty.is_integral() => {
525+
let result_tup = self.tcx.mk_tup(&[ty, bool_ty]);
526+
let result_value = self.temp(result_tup, span);
525527

526-
self.cfg.push_assign(
527-
block,
528-
source_info,
529-
result_value,
530-
Rvalue::CheckedBinaryOp(op, Box::new((lhs.to_copy(), rhs.to_copy()))),
531-
);
532-
let val_fld = Field::new(0);
533-
let of_fld = Field::new(1);
528+
self.cfg.push_assign(
529+
block,
530+
source_info,
531+
result_value,
532+
Rvalue::CheckedBinaryOp(op, Box::new((lhs.to_copy(), rhs.to_copy()))),
533+
);
534+
let val_fld = Field::new(0);
535+
let of_fld = Field::new(1);
536+
537+
let tcx = self.tcx;
538+
let val = tcx.mk_place_field(result_value, val_fld, ty);
539+
let of = tcx.mk_place_field(result_value, of_fld, bool_ty);
534540

535-
let tcx = self.tcx;
536-
let val = tcx.mk_place_field(result_value, val_fld, ty);
537-
let of = tcx.mk_place_field(result_value, of_fld, bool_ty);
541+
let err = AssertKind::Overflow(op, lhs, rhs);
542+
block = self.assert(block, Operand::Move(of), false, err, span);
538543

539-
let err = AssertKind::Overflow(op, lhs, rhs);
544+
Rvalue::Use(Operand::Move(val))
545+
}
546+
BinOp::Shl | BinOp::Shr if self.check_overflow && ty.is_integral() => {
547+
// Consider that the shift overflows if `rhs < 0` or `rhs >= bits`.
548+
// This can be encoded as a single operation as `(rhs & -bits) != 0`.
549+
let (size, _) = ty.int_size_and_signed(self.tcx);
550+
let bits = size.bits();
551+
debug_assert!(bits.is_power_of_two());
552+
let mask = !((bits - 1) as u128);
553+
554+
let rhs_ty = rhs.ty(&self.local_decls, self.tcx);
555+
let (rhs_size, _) = rhs_ty.int_size_and_signed(self.tcx);
556+
let mask = Operand::const_from_scalar(
557+
self.tcx,
558+
rhs_ty,
559+
Scalar::from_uint(rhs_size.truncate(mask), rhs_size),
560+
span,
561+
);
540562

541-
block = self.assert(block, Operand::Move(of), false, err, span);
563+
let outer_bits = self.temp(rhs_ty, span);
564+
self.cfg.push_assign(
565+
block,
566+
source_info,
567+
outer_bits,
568+
Rvalue::BinaryOp(BinOp::BitAnd, Box::new((rhs.to_copy(), mask))),
569+
);
542570

543-
block.and(Rvalue::Use(Operand::Move(val)))
544-
} else {
545-
if ty.is_integral() && (op == BinOp::Div || op == BinOp::Rem) {
571+
let overflows = self.temp(bool_ty, span);
572+
let zero = self.zero_literal(span, rhs_ty);
573+
self.cfg.push_assign(
574+
block,
575+
source_info,
576+
overflows,
577+
Rvalue::BinaryOp(BinOp::Ne, Box::new((Operand::Move(outer_bits), zero))),
578+
);
579+
580+
let overflow_err = AssertKind::Overflow(op, lhs.to_copy(), rhs.to_copy());
581+
block = self.assert(block, Operand::Move(overflows), false, overflow_err, span);
582+
Rvalue::BinaryOp(op, Box::new((lhs, rhs)))
583+
}
584+
BinOp::Div | BinOp::Rem if ty.is_integral() => {
546585
// Checking division and remainder is more complex, since we 1. always check
547586
// and 2. there are two possible failure cases, divide-by-zero and overflow.
548587

@@ -601,10 +640,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
601640

602641
block = self.assert(block, Operand::Move(of), false, overflow_err, span);
603642
}
604-
}
605643

606-
block.and(Rvalue::BinaryOp(op, Box::new((lhs, rhs))))
607-
}
644+
Rvalue::BinaryOp(op, Box::new((lhs, rhs)))
645+
}
646+
_ => Rvalue::BinaryOp(op, Box::new((lhs, rhs))),
647+
};
648+
block.and(rvalue)
608649
}
609650

610651
fn build_zero_repeat(

tests/mir-opt/issue_101973.inner.ConstProp.diff

+31-27
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,14 @@
1212
let mut _7: u32; // in scope 0 at $DIR/issue_101973.rs:+1:31: +1:52
1313
let mut _8: u32; // in scope 0 at $DIR/issue_101973.rs:+1:32: +1:45
1414
let mut _9: u32; // in scope 0 at $DIR/issue_101973.rs:+1:33: +1:39
15-
let mut _10: (u32, bool); // in scope 0 at $DIR/issue_101973.rs:+1:32: +1:45
16-
let mut _11: (u32, bool); // in scope 0 at $DIR/issue_101973.rs:+1:31: +1:57
15+
let mut _10: i32; // in scope 0 at $DIR/issue_101973.rs:+1:32: +1:45
16+
let mut _11: bool; // in scope 0 at $DIR/issue_101973.rs:+1:32: +1:45
17+
let mut _12: i32; // in scope 0 at $DIR/issue_101973.rs:+1:31: +1:57
18+
let mut _13: bool; // in scope 0 at $DIR/issue_101973.rs:+1:31: +1:57
1719
scope 1 (inlined imm8) { // at $DIR/issue_101973.rs:14:5: 14:17
1820
debug x => _1; // in scope 1 at $DIR/issue_101973.rs:5:13: 5:14
19-
let mut _12: u32; // in scope 1 at $DIR/issue_101973.rs:7:12: 7:27
20-
let mut _13: u32; // in scope 1 at $DIR/issue_101973.rs:7:12: 7:20
21-
let mut _14: (u32, bool); // in scope 1 at $DIR/issue_101973.rs:7:12: 7:20
21+
let mut _14: u32; // in scope 1 at $DIR/issue_101973.rs:7:12: 7:27
22+
let mut _15: u32; // in scope 1 at $DIR/issue_101973.rs:7:12: 7:20
2223
scope 2 {
2324
debug out => _4; // in scope 2 at $DIR/issue_101973.rs:6:9: 6:16
2425
}
@@ -32,43 +33,46 @@
3233
StorageLive(_2); // scope 0 at $DIR/issue_101973.rs:+1:5: +1:65
3334
StorageLive(_3); // scope 0 at $DIR/issue_101973.rs:+1:5: +1:58
3435
StorageLive(_4); // scope 0 at $DIR/issue_101973.rs:+1:5: +1:17
35-
StorageLive(_12); // scope 2 at $DIR/issue_101973.rs:7:12: 7:27
36-
StorageLive(_13); // scope 2 at $DIR/issue_101973.rs:7:12: 7:20
37-
_14 = CheckedShr(_1, const 0_i32); // scope 2 at $DIR/issue_101973.rs:7:12: 7:20
38-
assert(!move (_14.1: bool), "attempt to shift right by `{}`, which would overflow", const 0_i32) -> bb3; // scope 2 at $DIR/issue_101973.rs:7:12: 7:20
36+
StorageLive(_14); // scope 2 at $DIR/issue_101973.rs:7:12: 7:27
37+
StorageLive(_15); // scope 2 at $DIR/issue_101973.rs:7:12: 7:20
38+
_15 = Shr(_1, const 0_i32); // scope 2 at $DIR/issue_101973.rs:7:12: 7:20
39+
_14 = BitAnd(move _15, const 255_u32); // scope 2 at $DIR/issue_101973.rs:7:12: 7:27
40+
StorageDead(_15); // scope 2 at $DIR/issue_101973.rs:7:26: 7:27
41+
_4 = BitOr(const 0_u32, move _14); // scope 2 at $DIR/issue_101973.rs:7:5: 7:27
42+
StorageDead(_14); // scope 2 at $DIR/issue_101973.rs:7:26: 7:27
43+
StorageLive(_6); // scope 0 at $DIR/issue_101973.rs:+1:31: +1:57
44+
StorageLive(_7); // scope 0 at $DIR/issue_101973.rs:+1:31: +1:52
45+
StorageLive(_8); // scope 0 at $DIR/issue_101973.rs:+1:32: +1:45
46+
- _10 = BitAnd(const 8_i32, const -32_i32); // scope 0 at $DIR/issue_101973.rs:+1:32: +1:45
47+
- _11 = Ne(move _10, const 0_i32); // scope 0 at $DIR/issue_101973.rs:+1:32: +1:45
48+
- assert(!move _11, "attempt to shift right by `{}`, which would overflow", const 8_i32) -> bb1; // scope 0 at $DIR/issue_101973.rs:+1:32: +1:45
49+
+ _10 = const 0_i32; // scope 0 at $DIR/issue_101973.rs:+1:32: +1:45
50+
+ _11 = const false; // scope 0 at $DIR/issue_101973.rs:+1:32: +1:45
51+
+ assert(!const false, "attempt to shift right by `{}`, which would overflow", const 8_i32) -> bb1; // scope 0 at $DIR/issue_101973.rs:+1:32: +1:45
3952
}
4053

4154
bb1: {
42-
_8 = move (_10.0: u32); // scope 0 at $DIR/issue_101973.rs:+1:32: +1:45
55+
_8 = Shr(_1, const 8_i32); // scope 0 at $DIR/issue_101973.rs:+1:32: +1:45
4356
_7 = BitAnd(move _8, const 15_u32); // scope 0 at $DIR/issue_101973.rs:+1:31: +1:52
4457
StorageDead(_8); // scope 0 at $DIR/issue_101973.rs:+1:51: +1:52
45-
_11 = CheckedShl(_7, const 1_i32); // scope 0 at $DIR/issue_101973.rs:+1:31: +1:57
46-
assert(!move (_11.1: bool), "attempt to shift left by `{}`, which would overflow", const 1_i32) -> bb2; // scope 0 at $DIR/issue_101973.rs:+1:31: +1:57
58+
- _12 = BitAnd(const 1_i32, const -32_i32); // scope 0 at $DIR/issue_101973.rs:+1:31: +1:57
59+
- _13 = Ne(move _12, const 0_i32); // scope 0 at $DIR/issue_101973.rs:+1:31: +1:57
60+
- assert(!move _13, "attempt to shift left by `{}`, which would overflow", const 1_i32) -> bb2; // scope 0 at $DIR/issue_101973.rs:+1:31: +1:57
61+
+ _12 = const 0_i32; // scope 0 at $DIR/issue_101973.rs:+1:31: +1:57
62+
+ _13 = const false; // scope 0 at $DIR/issue_101973.rs:+1:31: +1:57
63+
+ assert(!const false, "attempt to shift left by `{}`, which would overflow", const 1_i32) -> bb2; // scope 0 at $DIR/issue_101973.rs:+1:31: +1:57
4764
}
4865

4966
bb2: {
50-
_6 = move (_11.0: u32); // scope 0 at $DIR/issue_101973.rs:+1:31: +1:57
67+
_6 = Shl(move _7, const 1_i32); // scope 0 at $DIR/issue_101973.rs:+1:31: +1:57
5168
StorageDead(_7); // scope 0 at $DIR/issue_101973.rs:+1:56: +1:57
52-
_3 = rotate_right::<u32>(_4, _6) -> bb4; // scope 3 at $SRC_DIR/core/src/num/uint_macros.rs:LL:COL
69+
_3 = rotate_right::<u32>(_4, _6) -> bb3; // scope 3 at $SRC_DIR/core/src/num/uint_macros.rs:LL:COL
5370
// mir::Constant
5471
// + span: $SRC_DIR/core/src/num/uint_macros.rs:LL:COL
5572
// + literal: Const { ty: extern "rust-intrinsic" fn(u32, u32) -> u32 {rotate_right::<u32>}, val: Value(<ZST>) }
5673
}
5774

5875
bb3: {
59-
_13 = move (_14.0: u32); // scope 2 at $DIR/issue_101973.rs:7:12: 7:20
60-
_12 = BitAnd(move _13, const 255_u32); // scope 2 at $DIR/issue_101973.rs:7:12: 7:27
61-
StorageDead(_13); // scope 2 at $DIR/issue_101973.rs:7:26: 7:27
62-
_4 = BitOr(const 0_u32, move _12); // scope 2 at $DIR/issue_101973.rs:7:5: 7:27
63-
StorageDead(_12); // scope 2 at $DIR/issue_101973.rs:7:26: 7:27
64-
StorageLive(_6); // scope 0 at $DIR/issue_101973.rs:+1:31: +1:57
65-
StorageLive(_7); // scope 0 at $DIR/issue_101973.rs:+1:31: +1:52
66-
StorageLive(_8); // scope 0 at $DIR/issue_101973.rs:+1:32: +1:45
67-
_10 = CheckedShr(_1, const 8_i32); // scope 0 at $DIR/issue_101973.rs:+1:32: +1:45
68-
assert(!move (_10.1: bool), "attempt to shift right by `{}`, which would overflow", const 8_i32) -> bb1; // scope 0 at $DIR/issue_101973.rs:+1:32: +1:45
69-
}
70-
71-
bb4: {
7276
StorageDead(_6); // scope 0 at $DIR/issue_101973.rs:+1:57: +1:58
7377
StorageDead(_4); // scope 0 at $DIR/issue_101973.rs:+1:57: +1:58
7478
_2 = move _3 as i32 (IntToInt); // scope 0 at $DIR/issue_101973.rs:+1:5: +1:65

tests/ui/consts/offset_from_ub.stderr

+3-3
Original file line numberDiff line numberDiff line change
@@ -39,19 +39,19 @@ error[E0080]: evaluation of constant value failed
3939
--> $DIR/offset_from_ub.rs:53:14
4040
|
4141
LL | unsafe { ptr_offset_from(end_ptr, start_ptr) }
42-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds offset_from: alloc18 has size 4, so pointer to 10 bytes starting at offset 0 is out-of-bounds
42+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds offset_from: alloc17 has size 4, so pointer to 10 bytes starting at offset 0 is out-of-bounds
4343

4444
error[E0080]: evaluation of constant value failed
4545
--> $DIR/offset_from_ub.rs:62:14
4646
|
4747
LL | unsafe { ptr_offset_from(start_ptr, end_ptr) }
48-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds offset_from: alloc21 has size 4, so pointer to 10 bytes starting at offset 0 is out-of-bounds
48+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds offset_from: alloc20 has size 4, so pointer to 10 bytes starting at offset 0 is out-of-bounds
4949

5050
error[E0080]: evaluation of constant value failed
5151
--> $DIR/offset_from_ub.rs:70:14
5252
|
5353
LL | unsafe { ptr_offset_from(end_ptr, end_ptr) }
54-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds offset_from: alloc24 has size 4, so pointer at offset 10 is out-of-bounds
54+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds offset_from: alloc23 has size 4, so pointer at offset 10 is out-of-bounds
5555

5656
error[E0080]: evaluation of constant value failed
5757
--> $DIR/offset_from_ub.rs:79:14

0 commit comments

Comments
 (0)