Skip to content

Commit 057313b

Browse files
Reapply "Auto merge of #133734 - scottmcm:lower-indexing-to-ptrmetadata, r=davidtwco,RalfJung"
This reverts commit 122a55b.
1 parent 1cbb062 commit 057313b

File tree

99 files changed

+1432
-1655
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

99 files changed

+1432
-1655
lines changed

compiler/rustc_const_eval/src/check_consts/check.rs

+20-5
Original file line numberDiff line numberDiff line change
@@ -586,12 +586,27 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
586586
) => {}
587587
Rvalue::ShallowInitBox(_, _) => {}
588588

589-
Rvalue::UnaryOp(_, operand) => {
589+
Rvalue::UnaryOp(op, operand) => {
590590
let ty = operand.ty(self.body, self.tcx);
591-
if is_int_bool_float_or_char(ty) {
592-
// Int, bool, float, and char operations are fine.
593-
} else {
594-
span_bug!(self.span, "non-primitive type in `Rvalue::UnaryOp`: {:?}", ty);
591+
match op {
592+
UnOp::Not | UnOp::Neg => {
593+
if is_int_bool_float_or_char(ty) {
594+
// Int, bool, float, and char operations are fine.
595+
} else {
596+
span_bug!(
597+
self.span,
598+
"non-primitive type in `Rvalue::UnaryOp{op:?}`: {ty:?}",
599+
);
600+
}
601+
}
602+
UnOp::PtrMetadata => {
603+
if !ty.is_ref() && !ty.is_unsafe_ptr() {
604+
span_bug!(
605+
self.span,
606+
"non-pointer type in `Rvalue::UnaryOp({op:?})`: {ty:?}",
607+
);
608+
}
609+
}
595610
}
596611
}
597612

compiler/rustc_const_eval/src/interpret/step.rs

+11-2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use rustc_middle::ty::layout::FnAbiOf;
99
use rustc_middle::ty::{self, Instance, Ty};
1010
use rustc_middle::{bug, mir, span_bug};
1111
use rustc_span::source_map::Spanned;
12+
use rustc_span::{DesugaringKind, Span};
1213
use rustc_target::callconv::FnAbi;
1314
use tracing::{info, instrument, trace};
1415

@@ -80,7 +81,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
8081
use rustc_middle::mir::StatementKind::*;
8182

8283
match &stmt.kind {
83-
Assign(box (place, rvalue)) => self.eval_rvalue_into_place(rvalue, *place)?,
84+
Assign(box (place, rvalue)) => {
85+
self.eval_rvalue_into_place(rvalue, *place, stmt.source_info.span)?
86+
}
8487

8588
SetDiscriminant { place, variant_index } => {
8689
let dest = self.eval_place(**place)?;
@@ -159,6 +162,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
159162
&mut self,
160163
rvalue: &mir::Rvalue<'tcx>,
161164
place: mir::Place<'tcx>,
165+
span: Span,
162166
) -> InterpResult<'tcx> {
163167
let dest = self.eval_place(place)?;
164168
// FIXME: ensure some kind of non-aliasing between LHS and RHS?
@@ -250,8 +254,13 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
250254
let src = self.eval_place(place)?;
251255
let place = self.force_allocation(&src)?;
252256
let mut val = ImmTy::from_immediate(place.to_ref(self), dest.layout);
253-
if !place_base_raw {
257+
if !place_base_raw
258+
&& span.desugaring_kind() != Some(DesugaringKind::IndexBoundsCheckReborrow)
259+
{
254260
// If this was not already raw, it needs retagging.
261+
// As a special hack, we exclude the desugared `PtrMetadata(&raw const *_n)`
262+
// from indexing. (Really we should not do any retag on `&raw` but that does not
263+
// currently work with Stacked Borrows.)
255264
val = M::retag_ptr_value(self, mir::RetagKind::Raw, &val)?;
256265
}
257266
self.write_immediate(*val, &dest)?;

compiler/rustc_mir_build/src/builder/expr/as_place.rs

+83-9
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use rustc_middle::mir::*;
1111
use rustc_middle::thir::*;
1212
use rustc_middle::ty::{self, AdtDef, CanonicalUserTypeAnnotation, Ty, Variance};
1313
use rustc_middle::{bug, span_bug};
14-
use rustc_span::Span;
14+
use rustc_span::{DesugaringKind, Span};
1515
use tracing::{debug, instrument, trace};
1616

1717
use crate::builder::ForGuard::{OutsideGuard, RefWithinGuard};
@@ -630,6 +630,80 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
630630
block.and(base_place.index(idx))
631631
}
632632

633+
/// Given a place that's either an array or a slice, returns an operand
634+
/// with the length of the array/slice.
635+
///
636+
/// For arrays it'll be `Operand::Constant` with the actual length;
637+
/// For slices it'll be `Operand::Move` of a local using `PtrMetadata`.
638+
fn len_of_slice_or_array(
639+
&mut self,
640+
block: BasicBlock,
641+
place: Place<'tcx>,
642+
span: Span,
643+
source_info: SourceInfo,
644+
) -> Operand<'tcx> {
645+
let place_ty = place.ty(&self.local_decls, self.tcx).ty;
646+
let usize_ty = self.tcx.types.usize;
647+
648+
match place_ty.kind() {
649+
ty::Array(_elem_ty, len_const) => {
650+
// We know how long an array is, so just use that as a constant
651+
// directly -- no locals needed. We do need one statement so
652+
// that borrow- and initialization-checking consider it used,
653+
// though. FIXME: Do we really *need* to count this as a use?
654+
// Could partial array tracking work off something else instead?
655+
self.cfg.push_fake_read(block, source_info, FakeReadCause::ForIndex, place);
656+
let const_ = Const::Ty(self.tcx.types.usize, *len_const);
657+
Operand::Constant(Box::new(ConstOperand { span, user_ty: None, const_ }))
658+
}
659+
ty::Slice(_elem_ty) => {
660+
let ptr_or_ref = if let [PlaceElem::Deref] = place.projection[..]
661+
&& let local_ty = self.local_decls[place.local].ty
662+
&& local_ty.is_trivially_pure_clone_copy()
663+
{
664+
// It's extremely common that we have something that can be
665+
// directly passed to `PtrMetadata`, so avoid an unnecessary
666+
// temporary and statement in those cases. Note that we can
667+
// only do that for `Copy` types -- not `&mut [_]` -- because
668+
// the MIR we're building here needs to pass NLL later.
669+
Operand::Copy(Place::from(place.local))
670+
} else {
671+
let len_span = self.tcx.with_stable_hashing_context(|hcx| {
672+
let span = source_info.span;
673+
span.mark_with_reason(
674+
None,
675+
DesugaringKind::IndexBoundsCheckReborrow,
676+
span.edition(),
677+
hcx,
678+
)
679+
});
680+
let ptr_ty = Ty::new_imm_ptr(self.tcx, place_ty);
681+
let slice_ptr = self.temp(ptr_ty, span);
682+
self.cfg.push_assign(
683+
block,
684+
SourceInfo { span: len_span, ..source_info },
685+
slice_ptr,
686+
Rvalue::RawPtr(Mutability::Not, place),
687+
);
688+
Operand::Move(slice_ptr)
689+
};
690+
691+
let len = self.temp(usize_ty, span);
692+
self.cfg.push_assign(
693+
block,
694+
source_info,
695+
len,
696+
Rvalue::UnaryOp(UnOp::PtrMetadata, ptr_or_ref),
697+
);
698+
699+
Operand::Move(len)
700+
}
701+
_ => {
702+
span_bug!(span, "len called on place of type {place_ty:?}")
703+
}
704+
}
705+
}
706+
633707
fn bounds_check(
634708
&mut self,
635709
block: BasicBlock,
@@ -638,25 +712,25 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
638712
expr_span: Span,
639713
source_info: SourceInfo,
640714
) -> BasicBlock {
641-
let usize_ty = self.tcx.types.usize;
642-
let bool_ty = self.tcx.types.bool;
643-
// bounds check:
644-
let len = self.temp(usize_ty, expr_span);
645-
let lt = self.temp(bool_ty, expr_span);
715+
let slice = slice.to_place(self);
646716

647717
// len = len(slice)
648-
self.cfg.push_assign(block, source_info, len, Rvalue::Len(slice.to_place(self)));
718+
let len = self.len_of_slice_or_array(block, slice, expr_span, source_info);
719+
649720
// lt = idx < len
721+
let bool_ty = self.tcx.types.bool;
722+
let lt = self.temp(bool_ty, expr_span);
650723
self.cfg.push_assign(
651724
block,
652725
source_info,
653726
lt,
654727
Rvalue::BinaryOp(
655728
BinOp::Lt,
656-
Box::new((Operand::Copy(Place::from(index)), Operand::Copy(len))),
729+
Box::new((Operand::Copy(Place::from(index)), len.to_copy())),
657730
),
658731
);
659-
let msg = BoundsCheck { len: Operand::Move(len), index: Operand::Copy(Place::from(index)) };
732+
let msg = BoundsCheck { len, index: Operand::Copy(Place::from(index)) };
733+
660734
// assert!(lt, "...")
661735
self.assert(block, Operand::Move(lt), true, msg, expr_span)
662736
}

compiler/rustc_mir_transform/src/instsimplify.rs

-13
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ impl<'tcx> crate::MirPass<'tcx> for InstSimplify {
4646
}
4747
ctx.simplify_bool_cmp(rvalue);
4848
ctx.simplify_ref_deref(rvalue);
49-
ctx.simplify_len(rvalue);
5049
ctx.simplify_ptr_aggregate(rvalue);
5150
ctx.simplify_cast(rvalue);
5251
ctx.simplify_repeated_aggregate(rvalue);
@@ -166,18 +165,6 @@ impl<'tcx> InstSimplifyContext<'_, 'tcx> {
166165
}
167166
}
168167

169-
/// Transform `Len([_; N])` ==> `N`.
170-
fn simplify_len(&self, rvalue: &mut Rvalue<'tcx>) {
171-
if let Rvalue::Len(ref place) = *rvalue {
172-
let place_ty = place.ty(self.local_decls, self.tcx).ty;
173-
if let ty::Array(_, len) = *place_ty.kind() {
174-
let const_ = Const::Ty(self.tcx.types.usize, len);
175-
let constant = ConstOperand { span: DUMMY_SP, const_, user_ty: None };
176-
*rvalue = Rvalue::Use(Operand::Constant(Box::new(constant)));
177-
}
178-
}
179-
}
180-
181168
/// Transform `Aggregate(RawPtr, [p, ()])` ==> `Cast(PtrToPtr, p)`.
182169
fn simplify_ptr_aggregate(&self, rvalue: &mut Rvalue<'tcx>) {
183170
if let Rvalue::Aggregate(box AggregateKind::RawPtr(pointee_ty, mutability), fields) = rvalue

compiler/rustc_mir_transform/src/validate.rs

-8
Original file line numberDiff line numberDiff line change
@@ -1128,14 +1128,6 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
11281128
);
11291129
}
11301130
UnOp::PtrMetadata => {
1131-
if !matches!(self.body.phase, MirPhase::Runtime(_)) {
1132-
// It would probably be fine to support this in earlier phases, but at
1133-
// the time of writing it's only ever introduced from intrinsic
1134-
// lowering or other runtime-phase optimization passes, so earlier
1135-
// things can just `bug!` on it.
1136-
self.fail(location, "PtrMetadata should be in runtime MIR only");
1137-
}
1138-
11391131
check_kinds!(
11401132
a,
11411133
"Cannot PtrMetadata non-pointer non-reference type {:?}",

compiler/rustc_span/src/hygiene.rs

+4
Original file line numberDiff line numberDiff line change
@@ -1163,6 +1163,9 @@ pub enum DesugaringKind {
11631163
WhileLoop,
11641164
/// `async Fn()` bound modifier
11651165
BoundModifier,
1166+
/// Marks a `&raw const *_1` needed as part of getting the length of a mutable
1167+
/// slice for the bounds check, so that MIRI's retag handling can recognize it.
1168+
IndexBoundsCheckReborrow,
11661169
}
11671170

11681171
impl DesugaringKind {
@@ -1179,6 +1182,7 @@ impl DesugaringKind {
11791182
DesugaringKind::ForLoop => "`for` loop",
11801183
DesugaringKind::WhileLoop => "`while` loop",
11811184
DesugaringKind::BoundModifier => "trait bound modifier",
1185+
DesugaringKind::IndexBoundsCheckReborrow => "slice indexing",
11821186
}
11831187
}
11841188
}

tests/mir-opt/array_index_is_temporary.main.SimplifyCfg-pre-optimizations.after.panic-abort.mir

+3-5
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,7 @@ fn main() -> () {
77
let mut _5: u32;
88
let mut _6: *mut usize;
99
let _7: usize;
10-
let mut _8: usize;
11-
let mut _9: bool;
10+
let mut _8: bool;
1211
scope 1 {
1312
debug x => _1;
1413
let mut _2: usize;
@@ -41,9 +40,8 @@ fn main() -> () {
4140
StorageDead(_6);
4241
StorageLive(_7);
4342
_7 = copy _2;
44-
_8 = Len(_1);
45-
_9 = Lt(copy _7, copy _8);
46-
assert(move _9, "index out of bounds: the length is {} but the index is {}", move _8, copy _7) -> [success: bb2, unwind unreachable];
43+
_8 = Lt(copy _7, const 3_usize);
44+
assert(move _8, "index out of bounds: the length is {} but the index is {}", const 3_usize, copy _7) -> [success: bb2, unwind unreachable];
4745
}
4846

4947
bb2: {

tests/mir-opt/array_index_is_temporary.main.SimplifyCfg-pre-optimizations.after.panic-unwind.mir

+3-5
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,7 @@ fn main() -> () {
77
let mut _5: u32;
88
let mut _6: *mut usize;
99
let _7: usize;
10-
let mut _8: usize;
11-
let mut _9: bool;
10+
let mut _8: bool;
1211
scope 1 {
1312
debug x => _1;
1413
let mut _2: usize;
@@ -41,9 +40,8 @@ fn main() -> () {
4140
StorageDead(_6);
4241
StorageLive(_7);
4342
_7 = copy _2;
44-
_8 = Len(_1);
45-
_9 = Lt(copy _7, copy _8);
46-
assert(move _9, "index out of bounds: the length is {} but the index is {}", move _8, copy _7) -> [success: bb2, unwind continue];
43+
_8 = Lt(copy _7, const 3_usize);
44+
assert(move _8, "index out of bounds: the length is {} but the index is {}", const 3_usize, copy _7) -> [success: bb2, unwind continue];
4745
}
4846

4947
bb2: {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// MIR for `index_array` after built
2+
3+
fn index_array(_1: &[i32; 7], _2: usize) -> &i32 {
4+
debug array => _1;
5+
debug index => _2;
6+
let mut _0: &i32;
7+
let _3: &i32;
8+
let _4: usize;
9+
let mut _5: bool;
10+
11+
bb0: {
12+
StorageLive(_3);
13+
StorageLive(_4);
14+
_4 = copy _2;
15+
FakeRead(ForIndex, (*_1));
16+
_5 = Lt(copy _4, const 7_usize);
17+
assert(move _5, "index out of bounds: the length is {} but the index is {}", const 7_usize, copy _4) -> [success: bb1, unwind: bb2];
18+
}
19+
20+
bb1: {
21+
_3 = &(*_1)[_4];
22+
_0 = &(*_3);
23+
StorageDead(_4);
24+
StorageDead(_3);
25+
return;
26+
}
27+
28+
bb2 (cleanup): {
29+
resume;
30+
}
31+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// MIR for `index_const_generic_array` after built
2+
3+
fn index_const_generic_array(_1: &[i32; N], _2: usize) -> &i32 {
4+
debug array => _1;
5+
debug index => _2;
6+
let mut _0: &i32;
7+
let _3: &i32;
8+
let _4: usize;
9+
let mut _5: bool;
10+
11+
bb0: {
12+
StorageLive(_3);
13+
StorageLive(_4);
14+
_4 = copy _2;
15+
FakeRead(ForIndex, (*_1));
16+
_5 = Lt(copy _4, const N);
17+
assert(move _5, "index out of bounds: the length is {} but the index is {}", const N, copy _4) -> [success: bb1, unwind: bb2];
18+
}
19+
20+
bb1: {
21+
_3 = &(*_1)[_4];
22+
_0 = &(*_3);
23+
StorageDead(_4);
24+
StorageDead(_3);
25+
return;
26+
}
27+
28+
bb2 (cleanup): {
29+
resume;
30+
}
31+
}

0 commit comments

Comments
 (0)