1
Fork 0

Auto merge of #135709 - lqd:bring-back-len, r=compiler-errors

Temporarily bring back `Rvalue::Len`

r? `@compiler-errors` as requested in https://github.com/rust-lang/rust/issues/135671#issuecomment-2599580364

> However, in the mean time, I'd rather we not crunch trying to find and more importantly validate the soundness of a solution 🤔

Agreed. To fix the IMO P-critical #135671 for which we somehow didn't have test coverage, this PR temporarily reverts:
- https://github.com/rust-lang/rust/pull/133734
- its bugfix https://github.com/rust-lang/rust/pull/134371
- https://github.com/rust-lang/rust/pull/134330

cc `@scottmcm`

I added the few samples from that issue as a test, but we can add more in the future, in particular it seems `@steffahn` [will work on that](https://github.com/rust-lang/rust/issues/135671#issuecomment-2599714354).

Fixes #135671. And if we want to land this, it should also be nominated for beta backport.
This commit is contained in:
bors 2025-01-19 06:09:51 +00:00
commit c62b732724
136 changed files with 1964 additions and 1542 deletions

View file

@ -408,6 +408,18 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> {
state: &mut State<FlatSet<Scalar>>,
) -> ValueOrPlace<FlatSet<Scalar>> {
let val = match rvalue {
Rvalue::Len(place) => {
let place_ty = place.ty(self.local_decls, self.tcx);
if let ty::Array(_, len) = place_ty.ty.kind() {
Const::Ty(self.tcx.types.usize, *len)
.try_eval_scalar(self.tcx, self.typing_env)
.map_or(FlatSet::Top, FlatSet::Elem)
} else if let [ProjectionElem::Deref] = place.projection[..] {
state.get_len(place.local.into(), &self.map)
} else {
FlatSet::Top
}
}
Rvalue::Cast(CastKind::IntToInt | CastKind::IntToFloat, operand, ty) => {
let Ok(layout) = self.tcx.layout_of(self.typing_env.as_query_input(*ty)) else {
return ValueOrPlace::Value(FlatSet::Top);

View file

@ -575,6 +575,7 @@ impl WriteInfo {
| Rvalue::NullaryOp(_, _)
| Rvalue::Ref(_, _, _)
| Rvalue::RawPtr(_, _)
| Rvalue::Len(_)
| Rvalue::Discriminant(_)
| Rvalue::CopyForDeref(_) => {}
}

View file

@ -223,6 +223,8 @@ enum Value<'tcx> {
Projection(VnIndex, ProjectionElem<VnIndex, Ty<'tcx>>),
/// Discriminant of the given value.
Discriminant(VnIndex),
/// Length of an array or slice.
Len(VnIndex),
// Operations.
NullaryOp(NullOp<'tcx>, Ty<'tcx>),
@ -511,6 +513,13 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
self.ecx.discriminant_for_variant(base.layout.ty, variant).discard_err()?;
discr_value.into()
}
Len(slice) => {
let slice = self.evaluated[slice].as_ref()?;
let usize_layout = self.ecx.layout_of(self.tcx.types.usize).unwrap();
let len = slice.len(&self.ecx).discard_err()?;
let imm = ImmTy::from_uint(len, usize_layout);
imm.into()
}
NullaryOp(null_op, ty) => {
let layout = self.ecx.layout_of(ty).ok()?;
if let NullOp::SizeOf | NullOp::AlignOf = null_op
@ -854,6 +863,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
}
// Operations.
Rvalue::Len(ref mut place) => return self.simplify_len(place, location),
Rvalue::Cast(ref mut kind, ref mut value, to) => {
return self.simplify_cast(kind, value, to, location);
}
@ -1474,6 +1484,47 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
Some(self.insert(Value::Cast { kind: *kind, value, from, to }))
}
fn simplify_len(&mut self, place: &mut Place<'tcx>, location: Location) -> Option<VnIndex> {
// Trivial case: we are fetching a statically known length.
let place_ty = place.ty(self.local_decls, self.tcx).ty;
if let ty::Array(_, len) = place_ty.kind() {
return self.insert_constant(Const::from_ty_const(
*len,
self.tcx.types.usize,
self.tcx,
));
}
let mut inner = self.simplify_place_value(place, location)?;
// The length information is stored in the wide pointer.
// Reborrowing copies length information from one pointer to the other.
while let Value::Address { place: borrowed, .. } = self.get(inner)
&& let [PlaceElem::Deref] = borrowed.projection[..]
&& let Some(borrowed) = self.locals[borrowed.local]
{
inner = borrowed;
}
// We have an unsizing cast, which assigns the length to wide pointer metadata.
if let Value::Cast { kind, from, to, .. } = self.get(inner)
&& let CastKind::PointerCoercion(ty::adjustment::PointerCoercion::Unsize, _) = kind
&& let Some(from) = from.builtin_deref(true)
&& let ty::Array(_, len) = from.kind()
&& let Some(to) = to.builtin_deref(true)
&& let ty::Slice(..) = to.kind()
{
return self.insert_constant(Const::from_ty_const(
*len,
self.tcx.types.usize,
self.tcx,
));
}
// Fallback: a symbolic `Len`.
Some(self.insert(Value::Len(inner)))
}
fn pointers_have_same_metadata(&self, left_ptr_ty: Ty<'tcx>, right_ptr_ty: Ty<'tcx>) -> bool {
let left_meta_ty = left_ptr_ty.pointee_metadata_ty_or_projection(self.tcx);
let right_meta_ty = right_ptr_ty.pointee_metadata_ty_or_projection(self.tcx);

View file

@ -46,6 +46,7 @@ impl<'tcx> crate::MirPass<'tcx> for InstSimplify {
}
ctx.simplify_bool_cmp(rvalue);
ctx.simplify_ref_deref(rvalue);
ctx.simplify_len(rvalue);
ctx.simplify_ptr_aggregate(rvalue);
ctx.simplify_cast(rvalue);
ctx.simplify_repeated_aggregate(rvalue);
@ -161,6 +162,18 @@ impl<'tcx> InstSimplifyContext<'_, 'tcx> {
}
}
/// Transform `Len([_; N])` ==> `N`.
fn simplify_len(&self, rvalue: &mut Rvalue<'tcx>) {
if let Rvalue::Len(ref place) = *rvalue {
let place_ty = place.ty(self.local_decls, self.tcx).ty;
if let ty::Array(_, len) = *place_ty.kind() {
let const_ = Const::from_ty_const(len, self.tcx.types.usize, self.tcx);
let constant = ConstOperand { span: DUMMY_SP, const_, user_ty: None };
*rvalue = Rvalue::Use(Operand::Constant(Box::new(constant)));
}
}
}
/// Transform `Aggregate(RawPtr, [p, ()])` ==> `Cast(PtrToPtr, p)`.
fn simplify_ptr_aggregate(&self, rvalue: &mut Rvalue<'tcx>) {
if let Rvalue::Aggregate(box AggregateKind::RawPtr(pointee_ty, mutability), fields) = rvalue

View file

@ -440,6 +440,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
| Rvalue::Use(..)
| Rvalue::CopyForDeref(..)
| Rvalue::Repeat(..)
| Rvalue::Len(..)
| Rvalue::Cast(..)
| Rvalue::ShallowInitBox(..)
| Rvalue::Discriminant(..)
@ -599,6 +600,20 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
return None;
}
Len(place) => {
let len = if let ty::Array(_, n) = place.ty(self.local_decls(), self.tcx).ty.kind()
{
n.try_to_target_usize(self.tcx)?
} else {
match self.get_const(place)? {
Value::Immediate(src) => src.len(&self.ecx).discard_err()?,
Value::Aggregate { fields, .. } => fields.len() as u64,
Value::Uninit => return None,
}
};
ImmTy::from_scalar(Scalar::from_target_usize(len, self), layout).into()
}
Ref(..) | RawPtr(..) => return None,
NullaryOp(ref null_op, ty) => {

View file

@ -430,7 +430,9 @@ impl<'tcx> Validator<'_, 'tcx> {
self.validate_operand(op)?
}
Rvalue::Discriminant(place) => self.validate_place(place.as_ref())?,
Rvalue::Discriminant(place) | Rvalue::Len(place) => {
self.validate_place(place.as_ref())?
}
Rvalue::ThreadLocalRef(_) => return Err(Unpromotable),

View file

@ -1018,6 +1018,14 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
}
}
Rvalue::Ref(..) => {}
Rvalue::Len(p) => {
let pty = p.ty(&self.body.local_decls, self.tcx).ty;
check_kinds!(
pty,
"Cannot compute length of non-array type {:?}",
ty::Array(..) | ty::Slice(..)
);
}
Rvalue::BinaryOp(op, vals) => {
use BinOp::*;
let a = vals.0.ty(&self.body.local_decls, self.tcx);
@ -1116,6 +1124,14 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
);
}
UnOp::PtrMetadata => {
if !matches!(self.body.phase, MirPhase::Runtime(_)) {
// It would probably be fine to support this in earlier phases, but at
// the time of writing it's only ever introduced from intrinsic
// lowering or other runtime-phase optimization passes, so earlier
// things can just `bug!` on it.
self.fail(location, "PtrMetadata should be in runtime MIR only");
}
check_kinds!(
a,
"Cannot PtrMetadata non-pointer non-reference type {:?}",