Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit ce3263e

Browse files
committedApr 19, 2024
Auto merge of rust-lang#124113 - RalfJung:interpret-scalar-ops, r=oli-obk
interpret: use ScalarInt for bin-ops; avoid PartialOrd for ScalarInt Best reviewed commit-by-commit r? `@oli-obk`
2 parents d1a0fa5 + d3f927d commit ce3263e

File tree

15 files changed

+211
-152
lines changed

15 files changed

+211
-152
lines changed
 

‎compiler/rustc_codegen_cranelift/src/constant.rs

Lines changed: 19 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ pub(crate) fn codegen_const_value<'tcx>(
110110
if fx.clif_type(layout.ty).is_some() {
111111
return CValue::const_val(fx, layout, int);
112112
} else {
113-
let raw_val = int.size().truncate(int.to_bits(int.size()).unwrap());
113+
let raw_val = int.size().truncate(int.assert_bits(int.size()));
114114
let val = match int.size().bytes() {
115115
1 => fx.bcx.ins().iconst(types::I8, raw_val as i64),
116116
2 => fx.bcx.ins().iconst(types::I16, raw_val as i64),
@@ -491,27 +491,24 @@ pub(crate) fn mir_operand_get_const_val<'tcx>(
491491
return None;
492492
}
493493
let scalar_int = mir_operand_get_const_val(fx, operand)?;
494-
let scalar_int = match fx
495-
.layout_of(*ty)
496-
.size
497-
.cmp(&scalar_int.size())
498-
{
499-
Ordering::Equal => scalar_int,
500-
Ordering::Less => match ty.kind() {
501-
ty::Uint(_) => ScalarInt::try_from_uint(
502-
scalar_int.try_to_uint(scalar_int.size()).unwrap(),
503-
fx.layout_of(*ty).size,
504-
)
505-
.unwrap(),
506-
ty::Int(_) => ScalarInt::try_from_int(
507-
scalar_int.try_to_int(scalar_int.size()).unwrap(),
508-
fx.layout_of(*ty).size,
509-
)
510-
.unwrap(),
511-
_ => unreachable!(),
512-
},
513-
Ordering::Greater => return None,
514-
};
494+
let scalar_int =
495+
match fx.layout_of(*ty).size.cmp(&scalar_int.size()) {
496+
Ordering::Equal => scalar_int,
497+
Ordering::Less => match ty.kind() {
498+
ty::Uint(_) => ScalarInt::try_from_uint(
499+
scalar_int.assert_uint(scalar_int.size()),
500+
fx.layout_of(*ty).size,
501+
)
502+
.unwrap(),
503+
ty::Int(_) => ScalarInt::try_from_int(
504+
scalar_int.assert_int(scalar_int.size()),
505+
fx.layout_of(*ty).size,
506+
)
507+
.unwrap(),
508+
_ => unreachable!(),
509+
},
510+
Ordering::Greater => return None,
511+
};
515512
computed_scalar_int = Some(scalar_int);
516513
}
517514
Rvalue::Use(operand) => {

‎compiler/rustc_codegen_cranelift/src/value_and_place.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -326,7 +326,7 @@ impl<'tcx> CValue<'tcx> {
326326

327327
let val = match layout.ty.kind() {
328328
ty::Uint(UintTy::U128) | ty::Int(IntTy::I128) => {
329-
let const_val = const_val.to_bits(layout.size).unwrap();
329+
let const_val = const_val.assert_bits(layout.size);
330330
let lsb = fx.bcx.ins().iconst(types::I64, const_val as u64 as i64);
331331
let msb = fx.bcx.ins().iconst(types::I64, (const_val >> 64) as u64 as i64);
332332
fx.bcx.ins().iconcat(lsb, msb)
@@ -338,7 +338,7 @@ impl<'tcx> CValue<'tcx> {
338338
| ty::Ref(..)
339339
| ty::RawPtr(..)
340340
| ty::FnPtr(..) => {
341-
let raw_val = const_val.size().truncate(const_val.to_bits(layout.size).unwrap());
341+
let raw_val = const_val.size().truncate(const_val.assert_bits(layout.size));
342342
fx.bcx.ins().iconst(clif_ty, raw_val as i64)
343343
}
344344
ty::Float(FloatTy::F32) => {

‎compiler/rustc_const_eval/src/interpret/discriminant.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -295,8 +295,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
295295
&niche_start_val,
296296
)?
297297
.to_scalar()
298-
.try_to_int()
299-
.unwrap();
298+
.assert_int();
300299
Ok(Some((tag, tag_field)))
301300
}
302301
}

‎compiler/rustc_const_eval/src/interpret/operand.rs

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@ use std::assert_matches::assert_matches;
66
use either::{Either, Left, Right};
77

88
use rustc_hir::def::Namespace;
9+
use rustc_middle::mir::interpret::ScalarSizeMismatch;
910
use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
1011
use rustc_middle::ty::print::{FmtPrinter, PrettyPrinter};
11-
use rustc_middle::ty::{ConstInt, Ty, TyCtxt};
12+
use rustc_middle::ty::{ConstInt, ScalarInt, Ty, TyCtxt};
1213
use rustc_middle::{mir, ty};
1314
use rustc_target::abi::{self, Abi, HasDataLayout, Size};
1415

@@ -210,6 +211,12 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> {
210211
ImmTy { imm: Immediate::Uninit, layout }
211212
}
212213

214+
#[inline]
215+
pub fn from_scalar_int(s: ScalarInt, layout: TyAndLayout<'tcx>) -> Self {
216+
assert_eq!(s.size(), layout.size);
217+
Self::from_scalar(Scalar::from(s), layout)
218+
}
219+
213220
#[inline]
214221
pub fn try_from_uint(i: impl Into<u128>, layout: TyAndLayout<'tcx>) -> Option<Self> {
215222
Some(Self::from_scalar(Scalar::try_from_uint(i, layout.size)?, layout))
@@ -223,7 +230,6 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> {
223230
pub fn try_from_int(i: impl Into<i128>, layout: TyAndLayout<'tcx>) -> Option<Self> {
224231
Some(Self::from_scalar(Scalar::try_from_int(i, layout.size)?, layout))
225232
}
226-
227233
#[inline]
228234
pub fn from_int(i: impl Into<i128>, layout: TyAndLayout<'tcx>) -> Self {
229235
Self::from_scalar(Scalar::from_int(i, layout.size), layout)
@@ -242,6 +248,20 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> {
242248
Self::from_scalar(Scalar::from_i8(c as i8), layout)
243249
}
244250

251+
/// Return the immediate as a `ScalarInt`. Ensures that it has the size that the layout of the
252+
/// immediate indicates.
253+
#[inline]
254+
pub fn to_scalar_int(&self) -> InterpResult<'tcx, ScalarInt> {
255+
let s = self.to_scalar().to_scalar_int()?;
256+
if s.size() != self.layout.size {
257+
throw_ub!(ScalarSizeMismatch(ScalarSizeMismatch {
258+
target_size: self.layout.size.bytes(),
259+
data_size: s.size().bytes(),
260+
}));
261+
}
262+
Ok(s)
263+
}
264+
245265
#[inline]
246266
pub fn to_const_int(self) -> ConstInt {
247267
assert!(self.layout.ty.is_integral());

‎compiler/rustc_const_eval/src/interpret/operator.rs

Lines changed: 59 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use rustc_apfloat::{Float, FloatConvert};
22
use rustc_middle::mir;
33
use rustc_middle::mir::interpret::{InterpResult, Scalar};
44
use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
5-
use rustc_middle::ty::{self, FloatTy, Ty};
5+
use rustc_middle::ty::{self, FloatTy, ScalarInt, Ty};
66
use rustc_span::symbol::sym;
77
use rustc_target::abi::Abi;
88

@@ -146,14 +146,20 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
146146
fn binary_int_op(
147147
&self,
148148
bin_op: mir::BinOp,
149-
// passing in raw bits
150-
l: u128,
151-
left_layout: TyAndLayout<'tcx>,
152-
r: u128,
153-
right_layout: TyAndLayout<'tcx>,
149+
left: &ImmTy<'tcx, M::Provenance>,
150+
right: &ImmTy<'tcx, M::Provenance>,
154151
) -> InterpResult<'tcx, (ImmTy<'tcx, M::Provenance>, bool)> {
155152
use rustc_middle::mir::BinOp::*;
156153

154+
// This checks the size, so that we can just assert it below.
155+
let l = left.to_scalar_int()?;
156+
let r = right.to_scalar_int()?;
157+
// Prepare to convert the values to signed or unsigned form.
158+
let l_signed = || l.assert_int(left.layout.size);
159+
let l_unsigned = || l.assert_uint(left.layout.size);
160+
let r_signed = || r.assert_int(right.layout.size);
161+
let r_unsigned = || r.assert_uint(right.layout.size);
162+
157163
let throw_ub_on_overflow = match bin_op {
158164
AddUnchecked => Some(sym::unchecked_add),
159165
SubUnchecked => Some(sym::unchecked_sub),
@@ -165,69 +171,72 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
165171

166172
// Shift ops can have an RHS with a different numeric type.
167173
if matches!(bin_op, Shl | ShlUnchecked | Shr | ShrUnchecked) {
168-
let size = left_layout.size.bits();
174+
let size = left.layout.size.bits();
169175
// The shift offset is implicitly masked to the type size. (This is the one MIR operator
170176
// that does *not* directly map to a single LLVM operation.) Compute how much we
171177
// actually shift and whether there was an overflow due to shifting too much.
172-
let (shift_amount, overflow) = if right_layout.abi.is_signed() {
173-
let shift_amount = self.sign_extend(r, right_layout) as i128;
178+
let (shift_amount, overflow) = if right.layout.abi.is_signed() {
179+
let shift_amount = r_signed();
174180
let overflow = shift_amount < 0 || shift_amount >= i128::from(size);
181+
// Deliberately wrapping `as` casts: shift_amount *can* be negative, but the result
182+
// of the `as` will be equal modulo `size` (since it is a power of two).
175183
let masked_amount = (shift_amount as u128) % u128::from(size);
176-
debug_assert_eq!(overflow, shift_amount != (masked_amount as i128));
184+
assert_eq!(overflow, shift_amount != (masked_amount as i128));
177185
(masked_amount, overflow)
178186
} else {
179-
let shift_amount = r;
187+
let shift_amount = r_unsigned();
180188
let masked_amount = shift_amount % u128::from(size);
181189
(masked_amount, shift_amount != masked_amount)
182190
};
183191
let shift_amount = u32::try_from(shift_amount).unwrap(); // we masked so this will always fit
184192
// Compute the shifted result.
185-
let result = if left_layout.abi.is_signed() {
186-
let l = self.sign_extend(l, left_layout) as i128;
193+
let result = if left.layout.abi.is_signed() {
194+
let l = l_signed();
187195
let result = match bin_op {
188196
Shl | ShlUnchecked => l.checked_shl(shift_amount).unwrap(),
189197
Shr | ShrUnchecked => l.checked_shr(shift_amount).unwrap(),
190198
_ => bug!(),
191199
};
192-
result as u128
200+
ScalarInt::truncate_from_int(result, left.layout.size).0
193201
} else {
194-
match bin_op {
202+
let l = l_unsigned();
203+
let result = match bin_op {
195204
Shl | ShlUnchecked => l.checked_shl(shift_amount).unwrap(),
196205
Shr | ShrUnchecked => l.checked_shr(shift_amount).unwrap(),
197206
_ => bug!(),
198-
}
207+
};
208+
ScalarInt::truncate_from_uint(result, left.layout.size).0
199209
};
200-
let truncated = self.truncate(result, left_layout);
201210

202211
if overflow && let Some(intrinsic_name) = throw_ub_on_overflow {
203212
throw_ub_custom!(
204213
fluent::const_eval_overflow_shift,
205-
val = if right_layout.abi.is_signed() {
206-
(self.sign_extend(r, right_layout) as i128).to_string()
214+
val = if right.layout.abi.is_signed() {
215+
r_signed().to_string()
207216
} else {
208-
r.to_string()
217+
r_unsigned().to_string()
209218
},
210219
name = intrinsic_name
211220
);
212221
}
213222

214-
return Ok((ImmTy::from_uint(truncated, left_layout), overflow));
223+
return Ok((ImmTy::from_scalar_int(result, left.layout), overflow));
215224
}
216225

217226
// For the remaining ops, the types must be the same on both sides
218-
if left_layout.ty != right_layout.ty {
227+
if left.layout.ty != right.layout.ty {
219228
span_bug!(
220229
self.cur_span(),
221230
"invalid asymmetric binary op {bin_op:?}: {l:?} ({l_ty}), {r:?} ({r_ty})",
222-
l_ty = left_layout.ty,
223-
r_ty = right_layout.ty,
231+
l_ty = left.layout.ty,
232+
r_ty = right.layout.ty,
224233
)
225234
}
226235

227-
let size = left_layout.size;
236+
let size = left.layout.size;
228237

229238
// Operations that need special treatment for signed integers
230-
if left_layout.abi.is_signed() {
239+
if left.layout.abi.is_signed() {
231240
let op: Option<fn(&i128, &i128) -> bool> = match bin_op {
232241
Lt => Some(i128::lt),
233242
Le => Some(i128::le),
@@ -236,18 +245,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
236245
_ => None,
237246
};
238247
if let Some(op) = op {
239-
let l = self.sign_extend(l, left_layout) as i128;
240-
let r = self.sign_extend(r, right_layout) as i128;
241-
return Ok((ImmTy::from_bool(op(&l, &r), *self.tcx), false));
248+
return Ok((ImmTy::from_bool(op(&l_signed(), &r_signed()), *self.tcx), false));
242249
}
243250
if bin_op == Cmp {
244-
let l = self.sign_extend(l, left_layout) as i128;
245-
let r = self.sign_extend(r, right_layout) as i128;
246-
return Ok(self.three_way_compare(l, r));
251+
return Ok(self.three_way_compare(l_signed(), r_signed()));
247252
}
248253
let op: Option<fn(i128, i128) -> (i128, bool)> = match bin_op {
249-
Div if r == 0 => throw_ub!(DivisionByZero),
250-
Rem if r == 0 => throw_ub!(RemainderByZero),
254+
Div if r.is_null() => throw_ub!(DivisionByZero),
255+
Rem if r.is_null() => throw_ub!(RemainderByZero),
251256
Div => Some(i128::overflowing_div),
252257
Rem => Some(i128::overflowing_rem),
253258
Add | AddUnchecked => Some(i128::overflowing_add),
@@ -256,8 +261,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
256261
_ => None,
257262
};
258263
if let Some(op) = op {
259-
let l = self.sign_extend(l, left_layout) as i128;
260-
let r = self.sign_extend(r, right_layout) as i128;
264+
let l = l_signed();
265+
let r = r_signed();
261266

262267
// We need a special check for overflowing Rem and Div since they are *UB*
263268
// on overflow, which can happen with "int_min $OP -1".
@@ -272,17 +277,19 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
272277
}
273278

274279
let (result, oflo) = op(l, r);
275-
// This may be out-of-bounds for the result type, so we have to truncate ourselves.
280+
// This may be out-of-bounds for the result type, so we have to truncate.
276281
// If that truncation loses any information, we have an overflow.
277-
let result = result as u128;
278-
let truncated = self.truncate(result, left_layout);
279-
let overflow = oflo || self.sign_extend(truncated, left_layout) != result;
282+
let (result, lossy) = ScalarInt::truncate_from_int(result, left.layout.size);
283+
let overflow = oflo || lossy;
280284
if overflow && let Some(intrinsic_name) = throw_ub_on_overflow {
281285
throw_ub_custom!(fluent::const_eval_overflow, name = intrinsic_name);
282286
}
283-
return Ok((ImmTy::from_uint(truncated, left_layout), overflow));
287+
return Ok((ImmTy::from_scalar_int(result, left.layout), overflow));
284288
}
285289
}
290+
// From here on it's okay to treat everything as unsigned.
291+
let l = l_unsigned();
292+
let r = r_unsigned();
286293

287294
if bin_op == Cmp {
288295
return Ok(self.three_way_compare(l, r));
@@ -297,12 +304,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
297304
Gt => ImmTy::from_bool(l > r, *self.tcx),
298305
Ge => ImmTy::from_bool(l >= r, *self.tcx),
299306

300-
BitOr => ImmTy::from_uint(l | r, left_layout),
301-
BitAnd => ImmTy::from_uint(l & r, left_layout),
302-
BitXor => ImmTy::from_uint(l ^ r, left_layout),
307+
BitOr => ImmTy::from_uint(l | r, left.layout),
308+
BitAnd => ImmTy::from_uint(l & r, left.layout),
309+
BitXor => ImmTy::from_uint(l ^ r, left.layout),
303310

304311
Add | AddUnchecked | Sub | SubUnchecked | Mul | MulUnchecked | Rem | Div => {
305-
assert!(!left_layout.abi.is_signed());
312+
assert!(!left.layout.abi.is_signed());
306313
let op: fn(u128, u128) -> (u128, bool) = match bin_op {
307314
Add | AddUnchecked => u128::overflowing_add,
308315
Sub | SubUnchecked => u128::overflowing_sub,
@@ -316,21 +323,21 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
316323
let (result, oflo) = op(l, r);
317324
// Truncate to target type.
318325
// If that truncation loses any information, we have an overflow.
319-
let truncated = self.truncate(result, left_layout);
320-
let overflow = oflo || truncated != result;
326+
let (result, lossy) = ScalarInt::truncate_from_uint(result, left.layout.size);
327+
let overflow = oflo || lossy;
321328
if overflow && let Some(intrinsic_name) = throw_ub_on_overflow {
322329
throw_ub_custom!(fluent::const_eval_overflow, name = intrinsic_name);
323330
}
324-
return Ok((ImmTy::from_uint(truncated, left_layout), overflow));
331+
return Ok((ImmTy::from_scalar_int(result, left.layout), overflow));
325332
}
326333

327334
_ => span_bug!(
328335
self.cur_span(),
329336
"invalid binary op {:?}: {:?}, {:?} (both {})",
330337
bin_op,
331-
l,
332-
r,
333-
right_layout.ty,
338+
left,
339+
right,
340+
right.layout.ty,
334341
),
335342
};
336343

@@ -427,9 +434,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
427434
right.layout.ty
428435
);
429436

430-
let l = left.to_scalar().to_bits(left.layout.size)?;
431-
let r = right.to_scalar().to_bits(right.layout.size)?;
432-
self.binary_int_op(bin_op, l, left.layout, r, right.layout)
437+
self.binary_int_op(bin_op, left, right)
433438
}
434439
_ if left.layout.ty.is_any_ptr() => {
435440
// The RHS type must be a `pointer` *or an integer type* (for `Offset`).

‎compiler/rustc_middle/src/mir/consts.rs

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ impl<'tcx> ConstValue<'tcx> {
8787
}
8888

8989
pub fn try_to_bits(&self, size: Size) -> Option<u128> {
90-
self.try_to_scalar_int()?.to_bits(size).ok()
90+
self.try_to_scalar_int()?.try_to_bits(size).ok()
9191
}
9292

9393
pub fn try_to_bool(&self) -> Option<bool> {
@@ -244,6 +244,8 @@ impl<'tcx> Const<'tcx> {
244244
Const::Ty(c) => match c.kind() {
245245
ty::ConstKind::Value(valtree) if c.ty().is_primitive() => {
246246
// A valtree of a type where leaves directly represent the scalar const value.
247+
// Just checking whether it is a leaf is insufficient as e.g. references are leafs
248+
// but the leaf value is the value they point to, not the reference itself!
247249
Some(valtree.unwrap_leaf().into())
248250
}
249251
_ => None,
@@ -255,12 +257,22 @@ impl<'tcx> Const<'tcx> {
255257

256258
#[inline]
257259
pub fn try_to_scalar_int(self) -> Option<ScalarInt> {
258-
self.try_to_scalar()?.try_to_int().ok()
260+
// This is equivalent to `self.try_to_scalar()?.try_to_int().ok()`, but measurably faster.
261+
match self {
262+
Const::Val(ConstValue::Scalar(Scalar::Int(x)), _) => Some(x),
263+
Const::Ty(c) => match c.kind() {
264+
ty::ConstKind::Value(valtree) if c.ty().is_primitive() => {
265+
Some(valtree.unwrap_leaf())
266+
}
267+
_ => None,
268+
},
269+
_ => None,
270+
}
259271
}
260272

261273
#[inline]
262274
pub fn try_to_bits(self, size: Size) -> Option<u128> {
263-
self.try_to_scalar_int()?.to_bits(size).ok()
275+
self.try_to_scalar_int()?.try_to_bits(size).ok()
264276
}
265277

266278
#[inline]
@@ -334,7 +346,7 @@ impl<'tcx> Const<'tcx> {
334346
let int = self.try_eval_scalar_int(tcx, param_env)?;
335347
let size =
336348
tcx.layout_of(param_env.with_reveal_all_normalized(tcx).and(self.ty())).ok()?.size;
337-
int.to_bits(size).ok()
349+
int.try_to_bits(size).ok()
338350
}
339351

340352
/// Panics if the value cannot be evaluated or doesn't contain a valid integer of the given type.

‎compiler/rustc_middle/src/mir/interpret/value.rs

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ impl<Prov> Scalar<Prov> {
236236
) -> Result<Either<u128, Pointer<Prov>>, ScalarSizeMismatch> {
237237
assert_ne!(target_size.bytes(), 0, "you should never look at the bits of a ZST");
238238
Ok(match self {
239-
Scalar::Int(int) => Left(int.to_bits(target_size).map_err(|size| {
239+
Scalar::Int(int) => Left(int.try_to_bits(target_size).map_err(|size| {
240240
ScalarSizeMismatch { target_size: target_size.bytes(), data_size: size.bytes() }
241241
})?),
242242
Scalar::Ptr(ptr, sz) => {
@@ -300,6 +300,11 @@ impl<'tcx, Prov: Provenance> Scalar<Prov> {
300300
}
301301
}
302302

303+
#[inline(always)]
304+
pub fn to_scalar_int(self) -> InterpResult<'tcx, ScalarInt> {
305+
self.try_to_int().map_err(|_| err_unsup!(ReadPointerAsInt(None)).into())
306+
}
307+
303308
#[inline(always)]
304309
#[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980)
305310
pub fn assert_int(self) -> ScalarInt {
@@ -311,16 +316,13 @@ impl<'tcx, Prov: Provenance> Scalar<Prov> {
311316
#[inline]
312317
pub fn to_bits(self, target_size: Size) -> InterpResult<'tcx, u128> {
313318
assert_ne!(target_size.bytes(), 0, "you should never look at the bits of a ZST");
314-
self.try_to_int()
315-
.map_err(|_| err_unsup!(ReadPointerAsInt(None)))?
316-
.to_bits(target_size)
317-
.map_err(|size| {
318-
err_ub!(ScalarSizeMismatch(ScalarSizeMismatch {
319-
target_size: target_size.bytes(),
320-
data_size: size.bytes(),
321-
}))
322-
.into()
323-
})
319+
self.to_scalar_int()?.try_to_bits(target_size).map_err(|size| {
320+
err_ub!(ScalarSizeMismatch(ScalarSizeMismatch {
321+
target_size: target_size.bytes(),
322+
data_size: size.bytes(),
323+
}))
324+
.into()
325+
})
324326
}
325327

326328
#[inline(always)]

‎compiler/rustc_middle/src/thir.rs

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use rustc_hir::{BindingMode, ByRef, HirId, MatchSource, RangeEnd};
1616
use rustc_index::newtype_index;
1717
use rustc_index::IndexVec;
1818
use rustc_middle::middle::region;
19-
use rustc_middle::mir::interpret::{AllocId, Scalar};
19+
use rustc_middle::mir::interpret::AllocId;
2020
use rustc_middle::mir::{self, BinOp, BorrowKind, FakeReadCause, UnOp};
2121
use rustc_middle::ty::adjustment::PointerCoercion;
2222
use rustc_middle::ty::layout::IntegerExt;
@@ -1006,18 +1006,17 @@ impl<'tcx> PatRangeBoundary<'tcx> {
10061006

10071007
// This code is hot when compiling matches with many ranges. So we
10081008
// special-case extraction of evaluated scalars for speed, for types where
1009-
// raw data comparisons are appropriate. E.g. `unicode-normalization` has
1009+
// unsigned int comparisons are appropriate. E.g. `unicode-normalization` has
10101010
// many ranges such as '\u{037A}'..='\u{037F}', and chars can be compared
10111011
// in this way.
1012-
(Finite(mir::Const::Ty(a)), Finite(mir::Const::Ty(b)))
1013-
if matches!(ty.kind(), ty::Uint(_) | ty::Char) =>
1014-
{
1015-
return Some(a.to_valtree().cmp(&b.to_valtree()));
1012+
(Finite(a), Finite(b)) if matches!(ty.kind(), ty::Uint(_) | ty::Char) => {
1013+
if let (Some(a), Some(b)) = (a.try_to_scalar_int(), b.try_to_scalar_int()) {
1014+
let sz = ty.primitive_size(tcx);
1015+
let a = a.assert_uint(sz);
1016+
let b = b.assert_uint(sz);
1017+
return Some(a.cmp(&b));
1018+
}
10161019
}
1017-
(
1018-
Finite(mir::Const::Val(mir::ConstValue::Scalar(Scalar::Int(a)), _)),
1019-
Finite(mir::Const::Val(mir::ConstValue::Scalar(Scalar::Int(b)), _)),
1020-
) if matches!(ty.kind(), ty::Uint(_) | ty::Char) => return Some(a.cmp(&b)),
10211020
_ => {}
10221021
}
10231022

‎compiler/rustc_middle/src/ty/consts.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -409,7 +409,7 @@ impl<'tcx> Const<'tcx> {
409409
let size =
410410
tcx.layout_of(param_env.with_reveal_all_normalized(tcx).and(self.ty())).ok()?.size;
411411
// if `ty` does not depend on generic parameters, use an empty param_env
412-
int.to_bits(size).ok()
412+
int.try_to_bits(size).ok()
413413
}
414414

415415
#[inline]

‎compiler/rustc_middle/src/ty/consts/int.rs

Lines changed: 62 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ impl IntoDiagArg for ConstInt {
126126
///
127127
/// This is a packed struct in order to allow this type to be optimally embedded in enums
128128
/// (like Scalar).
129-
#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
129+
#[derive(Clone, Copy, Eq, PartialEq, Hash)]
130130
#[repr(packed)]
131131
pub struct ScalarInt {
132132
/// The first `size` bytes of `data` are the value.
@@ -167,9 +167,12 @@ impl<D: Decoder> Decodable<D> for ScalarInt {
167167

168168
impl ScalarInt {
169169
pub const TRUE: ScalarInt = ScalarInt { data: 1_u128, size: NonZero::new(1).unwrap() };
170-
171170
pub const FALSE: ScalarInt = ScalarInt { data: 0_u128, size: NonZero::new(1).unwrap() };
172171

172+
fn raw(data: u128, size: Size) -> Self {
173+
Self { data, size: NonZero::new(size.bytes() as u8).unwrap() }
174+
}
175+
173176
#[inline]
174177
pub fn size(self) -> Size {
175178
Size::from_bytes(self.size.get())
@@ -196,7 +199,7 @@ impl ScalarInt {
196199

197200
#[inline]
198201
pub fn null(size: Size) -> Self {
199-
Self { data: 0, size: NonZero::new(size.bytes() as u8).unwrap() }
202+
Self::raw(0, size)
200203
}
201204

202205
#[inline]
@@ -207,11 +210,15 @@ impl ScalarInt {
207210
#[inline]
208211
pub fn try_from_uint(i: impl Into<u128>, size: Size) -> Option<Self> {
209212
let data = i.into();
210-
if size.truncate(data) == data {
211-
Some(Self { data, size: NonZero::new(size.bytes() as u8).unwrap() })
212-
} else {
213-
None
214-
}
213+
if size.truncate(data) == data { Some(Self::raw(data, size)) } else { None }
214+
}
215+
216+
/// Returns the truncated result, and whether truncation changed the value.
217+
#[inline]
218+
pub fn truncate_from_uint(i: impl Into<u128>, size: Size) -> (Self, bool) {
219+
let data = i.into();
220+
let r = Self::raw(size.truncate(data), size);
221+
(r, r.data != data)
215222
}
216223

217224
#[inline]
@@ -220,26 +227,27 @@ impl ScalarInt {
220227
// `into` performed sign extension, we have to truncate
221228
let truncated = size.truncate(i as u128);
222229
if size.sign_extend(truncated) as i128 == i {
223-
Some(Self { data: truncated, size: NonZero::new(size.bytes() as u8).unwrap() })
230+
Some(Self::raw(truncated, size))
224231
} else {
225232
None
226233
}
227234
}
228235

236+
/// Returns the truncated result, and whether truncation changed the value.
229237
#[inline]
230-
pub fn try_from_target_usize(i: impl Into<u128>, tcx: TyCtxt<'_>) -> Option<Self> {
231-
Self::try_from_uint(i, tcx.data_layout.pointer_size)
238+
pub fn truncate_from_int(i: impl Into<i128>, size: Size) -> (Self, bool) {
239+
let data = i.into();
240+
let r = Self::raw(size.truncate(data as u128), size);
241+
(r, size.sign_extend(r.data) as i128 != data)
232242
}
233243

234244
#[inline]
235-
pub fn assert_bits(self, target_size: Size) -> u128 {
236-
self.to_bits(target_size).unwrap_or_else(|size| {
237-
bug!("expected int of size {}, but got size {}", target_size.bytes(), size.bytes())
238-
})
245+
pub fn try_from_target_usize(i: impl Into<u128>, tcx: TyCtxt<'_>) -> Option<Self> {
246+
Self::try_from_uint(i, tcx.data_layout.pointer_size)
239247
}
240248

241249
#[inline]
242-
pub fn to_bits(self, target_size: Size) -> Result<u128, Size> {
250+
pub fn try_to_bits(self, target_size: Size) -> Result<u128, Size> {
243251
assert_ne!(target_size.bytes(), 0, "you should never look at the bits of a ZST");
244252
if target_size.bytes() == u64::from(self.size.get()) {
245253
self.check_data();
@@ -249,48 +257,60 @@ impl ScalarInt {
249257
}
250258
}
251259

260+
#[inline]
261+
pub fn assert_bits(self, target_size: Size) -> u128 {
262+
self.try_to_bits(target_size).unwrap_or_else(|size| {
263+
bug!("expected int of size {}, but got size {}", target_size.bytes(), size.bytes())
264+
})
265+
}
266+
252267
/// Tries to convert the `ScalarInt` to an unsigned integer of the given size.
253268
/// Fails if the size of the `ScalarInt` is not equal to `size` and returns the
254269
/// `ScalarInt`s size in that case.
255270
#[inline]
256271
pub fn try_to_uint(self, size: Size) -> Result<u128, Size> {
257-
self.to_bits(size)
272+
self.try_to_bits(size)
273+
}
274+
275+
#[inline]
276+
pub fn assert_uint(self, size: Size) -> u128 {
277+
self.assert_bits(size)
258278
}
259279

260280
// Tries to convert the `ScalarInt` to `u8`. Fails if the `size` of the `ScalarInt`
261-
// in not equal to `Size { raw: 1 }` and returns the `size` value of the `ScalarInt` in
281+
// in not equal to 1 byte and returns the `size` value of the `ScalarInt` in
262282
// that case.
263283
#[inline]
264284
pub fn try_to_u8(self) -> Result<u8, Size> {
265285
self.try_to_uint(Size::from_bits(8)).map(|v| u8::try_from(v).unwrap())
266286
}
267287

268288
/// Tries to convert the `ScalarInt` to `u16`. Fails if the size of the `ScalarInt`
269-
/// in not equal to `Size { raw: 2 }` and returns the `size` value of the `ScalarInt` in
289+
/// in not equal to 2 bytes and returns the `size` value of the `ScalarInt` in
270290
/// that case.
271291
#[inline]
272292
pub fn try_to_u16(self) -> Result<u16, Size> {
273293
self.try_to_uint(Size::from_bits(16)).map(|v| u16::try_from(v).unwrap())
274294
}
275295

276296
/// Tries to convert the `ScalarInt` to `u32`. Fails if the `size` of the `ScalarInt`
277-
/// in not equal to `Size { raw: 4 }` and returns the `size` value of the `ScalarInt` in
297+
/// in not equal to 4 bytes and returns the `size` value of the `ScalarInt` in
278298
/// that case.
279299
#[inline]
280300
pub fn try_to_u32(self) -> Result<u32, Size> {
281301
self.try_to_uint(Size::from_bits(32)).map(|v| u32::try_from(v).unwrap())
282302
}
283303

284304
/// Tries to convert the `ScalarInt` to `u64`. Fails if the `size` of the `ScalarInt`
285-
/// in not equal to `Size { raw: 8 }` and returns the `size` value of the `ScalarInt` in
305+
/// in not equal to 8 bytes and returns the `size` value of the `ScalarInt` in
286306
/// that case.
287307
#[inline]
288308
pub fn try_to_u64(self) -> Result<u64, Size> {
289309
self.try_to_uint(Size::from_bits(64)).map(|v| u64::try_from(v).unwrap())
290310
}
291311

292312
/// Tries to convert the `ScalarInt` to `u128`. Fails if the `size` of the `ScalarInt`
293-
/// in not equal to `Size { raw: 16 }` and returns the `size` value of the `ScalarInt` in
313+
/// in not equal to 16 bytes and returns the `size` value of the `ScalarInt` in
294314
/// that case.
295315
#[inline]
296316
pub fn try_to_u128(self) -> Result<u128, Size> {
@@ -303,7 +323,7 @@ impl ScalarInt {
303323
}
304324

305325
// Tries to convert the `ScalarInt` to `bool`. Fails if the `size` of the `ScalarInt`
306-
// in not equal to `Size { raw: 1 }` or if the value is not 0 or 1 and returns the `size`
326+
// in not equal to 1 byte or if the value is not 0 or 1 and returns the `size`
307327
// value of the `ScalarInt` in that case.
308328
#[inline]
309329
pub fn try_to_bool(self) -> Result<bool, Size> {
@@ -319,40 +339,46 @@ impl ScalarInt {
319339
/// `ScalarInt`s size in that case.
320340
#[inline]
321341
pub fn try_to_int(self, size: Size) -> Result<i128, Size> {
322-
let b = self.to_bits(size)?;
342+
let b = self.try_to_bits(size)?;
323343
Ok(size.sign_extend(b) as i128)
324344
}
325345

346+
#[inline]
347+
pub fn assert_int(self, size: Size) -> i128 {
348+
let b = self.assert_bits(size);
349+
size.sign_extend(b) as i128
350+
}
351+
326352
/// Tries to convert the `ScalarInt` to i8.
327-
/// Fails if the size of the `ScalarInt` is not equal to `Size { raw: 1 }`
353+
/// Fails if the size of the `ScalarInt` is not equal to 1 byte
328354
/// and returns the `ScalarInt`s size in that case.
329355
pub fn try_to_i8(self) -> Result<i8, Size> {
330356
self.try_to_int(Size::from_bits(8)).map(|v| i8::try_from(v).unwrap())
331357
}
332358

333359
/// Tries to convert the `ScalarInt` to i16.
334-
/// Fails if the size of the `ScalarInt` is not equal to `Size { raw: 2 }`
360+
/// Fails if the size of the `ScalarInt` is not equal to 2 bytes
335361
/// and returns the `ScalarInt`s size in that case.
336362
pub fn try_to_i16(self) -> Result<i16, Size> {
337363
self.try_to_int(Size::from_bits(16)).map(|v| i16::try_from(v).unwrap())
338364
}
339365

340366
/// Tries to convert the `ScalarInt` to i32.
341-
/// Fails if the size of the `ScalarInt` is not equal to `Size { raw: 4 }`
367+
/// Fails if the size of the `ScalarInt` is not equal to 4 bytes
342368
/// and returns the `ScalarInt`s size in that case.
343369
pub fn try_to_i32(self) -> Result<i32, Size> {
344370
self.try_to_int(Size::from_bits(32)).map(|v| i32::try_from(v).unwrap())
345371
}
346372

347373
/// Tries to convert the `ScalarInt` to i64.
348-
/// Fails if the size of the `ScalarInt` is not equal to `Size { raw: 8 }`
374+
/// Fails if the size of the `ScalarInt` is not equal to 8 bytes
349375
/// and returns the `ScalarInt`s size in that case.
350376
pub fn try_to_i64(self) -> Result<i64, Size> {
351377
self.try_to_int(Size::from_bits(64)).map(|v| i64::try_from(v).unwrap())
352378
}
353379

354380
/// Tries to convert the `ScalarInt` to i128.
355-
/// Fails if the size of the `ScalarInt` is not equal to `Size { raw: 16 }`
381+
/// Fails if the size of the `ScalarInt` is not equal to 16 bytes
356382
/// and returns the `ScalarInt`s size in that case.
357383
pub fn try_to_i128(self) -> Result<i128, Size> {
358384
self.try_to_int(Size::from_bits(128))
@@ -366,7 +392,7 @@ impl ScalarInt {
366392
#[inline]
367393
pub fn try_to_float<F: Float>(self) -> Result<F, Size> {
368394
// Going through `to_uint` to check size and truncation.
369-
Ok(F::from_bits(self.to_bits(Size::from_bits(F::BITS))?))
395+
Ok(F::from_bits(self.try_to_bits(Size::from_bits(F::BITS))?))
370396
}
371397

372398
#[inline]
@@ -415,7 +441,7 @@ macro_rules! try_from {
415441
fn try_from(int: ScalarInt) -> Result<Self, Size> {
416442
// The `unwrap` cannot fail because to_bits (if it succeeds)
417443
// is guaranteed to return a value that fits into the size.
418-
int.to_bits(Size::from_bytes(std::mem::size_of::<$ty>()))
444+
int.try_to_bits(Size::from_bytes(std::mem::size_of::<$ty>()))
419445
.map(|u| u.try_into().unwrap())
420446
}
421447
}
@@ -450,7 +476,7 @@ impl TryFrom<ScalarInt> for char {
450476

451477
#[inline]
452478
fn try_from(int: ScalarInt) -> Result<Self, Self::Error> {
453-
let Ok(bits) = int.to_bits(Size::from_bytes(std::mem::size_of::<char>())) else {
479+
let Ok(bits) = int.try_to_bits(Size::from_bytes(std::mem::size_of::<char>())) else {
454480
return Err(CharTryFromScalarInt);
455481
};
456482
match char::from_u32(bits.try_into().unwrap()) {
@@ -472,7 +498,7 @@ impl TryFrom<ScalarInt> for Half {
472498
type Error = Size;
473499
#[inline]
474500
fn try_from(int: ScalarInt) -> Result<Self, Size> {
475-
int.to_bits(Size::from_bytes(2)).map(Self::from_bits)
501+
int.try_to_bits(Size::from_bytes(2)).map(Self::from_bits)
476502
}
477503
}
478504

@@ -488,7 +514,7 @@ impl TryFrom<ScalarInt> for Single {
488514
type Error = Size;
489515
#[inline]
490516
fn try_from(int: ScalarInt) -> Result<Self, Size> {
491-
int.to_bits(Size::from_bytes(4)).map(Self::from_bits)
517+
int.try_to_bits(Size::from_bytes(4)).map(Self::from_bits)
492518
}
493519
}
494520

@@ -504,7 +530,7 @@ impl TryFrom<ScalarInt> for Double {
504530
type Error = Size;
505531
#[inline]
506532
fn try_from(int: ScalarInt) -> Result<Self, Size> {
507-
int.to_bits(Size::from_bytes(8)).map(Self::from_bits)
533+
int.try_to_bits(Size::from_bytes(8)).map(Self::from_bits)
508534
}
509535
}
510536

@@ -520,7 +546,7 @@ impl TryFrom<ScalarInt> for Quad {
520546
type Error = Size;
521547
#[inline]
522548
fn try_from(int: ScalarInt) -> Result<Self, Size> {
523-
int.to_bits(Size::from_bytes(16)).map(Self::from_bits)
549+
int.try_to_bits(Size::from_bytes(16)).map(Self::from_bits)
524550
}
525551
}
526552

‎compiler/rustc_middle/src/ty/consts/valtree.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use crate::mir::interpret::Scalar;
33
use crate::ty::{self, Ty, TyCtxt};
44
use rustc_macros::{HashStable, TyDecodable, TyEncodable};
55

6-
#[derive(Copy, Clone, Debug, Hash, TyEncodable, TyDecodable, Eq, PartialEq, Ord, PartialOrd)]
6+
#[derive(Copy, Clone, Debug, Hash, TyEncodable, TyDecodable, Eq, PartialEq)]
77
#[derive(HashStable)]
88
/// This datastructure is used to represent the value of constants used in the type system.
99
///

‎compiler/rustc_mir_transform/src/known_panics_lint.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -796,7 +796,7 @@ impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> {
796796
if let Some(ref value) = self.eval_operand(discr)
797797
&& let Some(value_const) = self.use_ecx(|this| this.ecx.read_scalar(value))
798798
&& let Ok(constant) = value_const.try_to_int()
799-
&& let Ok(constant) = constant.to_bits(constant.size())
799+
&& let Ok(constant) = constant.try_to_bits(constant.size())
800800
{
801801
// We managed to evaluate the discriminant, so we know we only need to visit
802802
// one target.

‎compiler/rustc_mir_transform/src/match_branches.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -369,8 +369,7 @@ impl<'tcx> SimplifyMatch<'tcx> for SimplifyToExp {
369369
}
370370

371371
fn int_equal(l: ScalarInt, r: impl Into<u128>, size: Size) -> bool {
372-
l.try_to_int(l.size()).unwrap()
373-
== ScalarInt::try_from_uint(r, size).unwrap().try_to_int(size).unwrap()
372+
l.assert_int(l.size()) == ScalarInt::try_from_uint(r, size).unwrap().assert_int(size)
374373
}
375374

376375
// We first compare the two branches, and then the other branches need to fulfill the same conditions.

‎compiler/rustc_mir_transform/src/promote_consts.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -490,14 +490,14 @@ impl<'tcx> Validator<'_, 'tcx> {
490490
}
491491
_ => None,
492492
};
493-
match rhs_val.map(|x| x.try_to_uint(sz).unwrap()) {
493+
match rhs_val.map(|x| x.assert_uint(sz)) {
494494
// for the zero test, int vs uint does not matter
495495
Some(x) if x != 0 => {} // okay
496496
_ => return Err(Unpromotable), // value not known or 0 -- not okay
497497
}
498498
// Furthermore, for signed divison, we also have to exclude `int::MIN / -1`.
499499
if lhs_ty.is_signed() {
500-
match rhs_val.map(|x| x.try_to_int(sz).unwrap()) {
500+
match rhs_val.map(|x| x.assert_int(sz)) {
501501
Some(-1) | None => {
502502
// The RHS is -1 or unknown, so we have to be careful.
503503
// But is the LHS int::MIN?
@@ -508,7 +508,7 @@ impl<'tcx> Validator<'_, 'tcx> {
508508
_ => None,
509509
};
510510
let lhs_min = sz.signed_int_min();
511-
match lhs_val.map(|x| x.try_to_int(sz).unwrap()) {
511+
match lhs_val.map(|x| x.assert_int(sz)) {
512512
Some(x) if x != lhs_min => {} // okay
513513
_ => return Err(Unpromotable), // value not known or int::MIN -- not okay
514514
}

‎compiler/rustc_transmute/src/layout/tree.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -420,7 +420,7 @@ pub(crate) mod rustc {
420420
fn from_tag(tag: ScalarInt, tcx: TyCtxt<'tcx>) -> Self {
421421
use rustc_target::abi::Endian;
422422
let size = tag.size();
423-
let bits = tag.to_bits(size).unwrap();
423+
let bits = tag.assert_bits(size);
424424
let bytes: [u8; 16];
425425
let bytes = match tcx.data_layout.endian {
426426
Endian::Little => {

0 commit comments

Comments
 (0)
This repository has been archived.