diff options
| author | Scott McMurray <scottmcm@users.noreply.github.com> | 2024-12-01 20:14:04 -0800 |
|---|---|---|
| committer | Scott McMurray <scottmcm@users.noreply.github.com> | 2024-12-03 11:05:45 -0800 |
| commit | 612adbb6bf88ecf549fb93cb1f4edcf793bca88e (patch) | |
| tree | 3f37a1552ccefe3df0da67281b5e060bcc2d3d21 /compiler/rustc_mir_build/src | |
| parent | 490b2cc09860dd62a7595bb07364d71c12ce4e60 (diff) | |
| download | rust-612adbb6bf88ecf549fb93cb1f4edcf793bca88e.tar.gz rust-612adbb6bf88ecf549fb93cb1f4edcf793bca88e.zip | |
Bounds-check with PtrMetadata instead of Len in MIR
Diffstat (limited to 'compiler/rustc_mir_build/src')
| -rw-r--r-- | compiler/rustc_mir_build/src/build/expr/as_place.rs | 81 |
1 files changed, 73 insertions, 8 deletions
diff --git a/compiler/rustc_mir_build/src/build/expr/as_place.rs b/compiler/rustc_mir_build/src/build/expr/as_place.rs index 6ce88cdc39d..9f377f792ef 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_place.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_place.rs @@ -630,6 +630,71 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { block.and(base_place.index(idx)) } + /// Given a place that's either an array or a slice, returns an operand + /// with the length of the array/slice. + /// + /// For arrays it'll be `Operand::Constant` with the actual length; + /// For slices it'll be `Operand::Move` of a local using `PtrMetadata`. + fn len_of_slice_or_array( + &mut self, + block: BasicBlock, + place: Place<'tcx>, + span: Span, + source_info: SourceInfo, + ) -> Operand<'tcx> { + let place_ty = place.ty(&self.local_decls, self.tcx).ty; + let usize_ty = self.tcx.types.usize; + + match place_ty.kind() { + ty::Array(_elem_ty, len_const) => { + // We know how long an array is, so just use that as a constant + // directly -- no locals needed. We do need one statement so + // that borrow- and initialization-checking consider it used, + // though. FIXME: Do we really *need* to count this as a use? + // Could partial array tracking work off something else instead? + self.cfg.push_fake_read(block, source_info, FakeReadCause::ForIndex, place); + let const_ = Const::from_ty_const(*len_const, usize_ty, self.tcx); + Operand::Constant(Box::new(ConstOperand { span, user_ty: None, const_ })) + } + ty::Slice(_elem_ty) => { + let ptr_or_ref = if let [PlaceElem::Deref] = place.projection[..] + && let local_ty = self.local_decls[place.local].ty + && local_ty.is_trivially_pure_clone_copy() + { + // It's extremely common that we have something that can be + // directly passed to `PtrMetadata`, so avoid an unnecessary + // temporary and statement in those cases. Note that we can + // only do that for `Copy` types -- not `&mut [_]` -- because + // the MIR we're building here needs to pass NLL later. + Operand::Copy(Place::from(place.local)) + } else { + let ptr_ty = Ty::new_imm_ptr(self.tcx, place_ty); + let slice_ptr = self.temp(ptr_ty, span); + self.cfg.push_assign( + block, + source_info, + slice_ptr, + Rvalue::RawPtr(Mutability::Not, place), + ); + Operand::Move(slice_ptr) + }; + + let len = self.temp(usize_ty, span); + self.cfg.push_assign( + block, + source_info, + len, + Rvalue::UnaryOp(UnOp::PtrMetadata, ptr_or_ref), + ); + + Operand::Move(len) + } + _ => { + span_bug!(span, "len called on place of type {place_ty:?}") + } + } + } + fn bounds_check( &mut self, block: BasicBlock, @@ -638,25 +703,25 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { expr_span: Span, source_info: SourceInfo, ) -> BasicBlock { - let usize_ty = self.tcx.types.usize; - let bool_ty = self.tcx.types.bool; - // bounds check: - let len = self.temp(usize_ty, expr_span); - let lt = self.temp(bool_ty, expr_span); + let slice = slice.to_place(self); // len = len(slice) - self.cfg.push_assign(block, source_info, len, Rvalue::Len(slice.to_place(self))); + let len = self.len_of_slice_or_array(block, slice, expr_span, source_info); + // lt = idx < len + let bool_ty = self.tcx.types.bool; + let lt = self.temp(bool_ty, expr_span); self.cfg.push_assign( block, source_info, lt, Rvalue::BinaryOp( BinOp::Lt, - Box::new((Operand::Copy(Place::from(index)), Operand::Copy(len))), + Box::new((Operand::Copy(Place::from(index)), len.to_copy())), ), ); - let msg = BoundsCheck { len: Operand::Move(len), index: Operand::Copy(Place::from(index)) }; + let msg = BoundsCheck { len, index: Operand::Copy(Place::from(index)) }; + // assert!(lt, "...") self.assert(block, Operand::Move(lt), true, msg, expr_span) } |
