Represent the raw pointer for a array length check as a new kind of fake borrow

This commit is contained in:
Michael Goulet 2025-01-19 22:45:03 +00:00
parent 057313b7a6
commit eeecb56b73
26 changed files with 199 additions and 89 deletions

View file

@ -1284,15 +1284,18 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
); );
} }
&Rvalue::RawPtr(mutability, place) => { &Rvalue::RawPtr(kind, place) => {
let access_kind = match mutability { let access_kind = match kind {
Mutability::Mut => ( RawPtrKind::Mut => (
Deep, Deep,
Write(WriteKind::MutableBorrow(BorrowKind::Mut { Write(WriteKind::MutableBorrow(BorrowKind::Mut {
kind: MutBorrowKind::Default, kind: MutBorrowKind::Default,
})), })),
), ),
Mutability::Not => (Deep, Read(ReadKind::Borrow(BorrowKind::Shared))), RawPtrKind::Const => (Deep, Read(ReadKind::Borrow(BorrowKind::Shared))),
RawPtrKind::FakeForPtrMetadata => {
(Shallow(Some(ArtificialField::ArrayLength)), Read(ReadKind::Copy))
}
}; };
self.access_place( self.access_place(

View file

@ -3,11 +3,7 @@ use std::ops::ControlFlow;
use rustc_data_structures::graph::dominators::Dominators; use rustc_data_structures::graph::dominators::Dominators;
use rustc_middle::bug; use rustc_middle::bug;
use rustc_middle::mir::visit::Visitor; use rustc_middle::mir::visit::Visitor;
use rustc_middle::mir::{ use rustc_middle::mir::*;
self, BasicBlock, Body, BorrowKind, FakeBorrowKind, InlineAsmOperand, Location, Mutability,
NonDivergingIntrinsic, Operand, Place, Rvalue, Statement, StatementKind, Terminator,
TerminatorKind,
};
use rustc_middle::ty::TyCtxt; use rustc_middle::ty::TyCtxt;
use tracing::debug; use tracing::debug;
@ -60,7 +56,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LoanInvalidationsGenerator<'a, 'tcx> {
StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume(op)) => { StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume(op)) => {
self.consume_operand(location, op); self.consume_operand(location, op);
} }
StatementKind::Intrinsic(box NonDivergingIntrinsic::CopyNonOverlapping(mir::CopyNonOverlapping { StatementKind::Intrinsic(box NonDivergingIntrinsic::CopyNonOverlapping(CopyNonOverlapping {
src, src,
dst, dst,
count, count,
@ -273,15 +269,18 @@ impl<'a, 'tcx> LoanInvalidationsGenerator<'a, 'tcx> {
self.access_place(location, place, access_kind, LocalMutationIsAllowed::No); self.access_place(location, place, access_kind, LocalMutationIsAllowed::No);
} }
&Rvalue::RawPtr(mutability, place) => { &Rvalue::RawPtr(kind, place) => {
let access_kind = match mutability { let access_kind = match kind {
Mutability::Mut => ( RawPtrKind::Mut => (
Deep, Deep,
Write(WriteKind::MutableBorrow(BorrowKind::Mut { Write(WriteKind::MutableBorrow(BorrowKind::Mut {
kind: mir::MutBorrowKind::Default, kind: MutBorrowKind::Default,
})), })),
), ),
Mutability::Not => (Deep, Read(ReadKind::Borrow(BorrowKind::Shared))), RawPtrKind::Const => (Deep, Read(ReadKind::Borrow(BorrowKind::Shared))),
RawPtrKind::FakeForPtrMetadata => {
(Shallow(Some(ArtificialField::ArrayLength)), Read(ReadKind::Copy))
}
}; };
self.access_place(location, place, access_kind, LocalMutationIsAllowed::No); self.access_place(location, place, access_kind, LocalMutationIsAllowed::No);

View file

@ -612,9 +612,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
mir::Rvalue::CopyForDeref(place) => { mir::Rvalue::CopyForDeref(place) => {
self.codegen_operand(bx, &mir::Operand::Copy(place)) self.codegen_operand(bx, &mir::Operand::Copy(place))
} }
mir::Rvalue::RawPtr(mutability, place) => { mir::Rvalue::RawPtr(kind, place) => {
let mk_ptr = let mk_ptr = move |tcx: TyCtxt<'tcx>, ty: Ty<'tcx>| {
move |tcx: TyCtxt<'tcx>, ty: Ty<'tcx>| Ty::new_ptr(tcx, ty, mutability); Ty::new_ptr(tcx, ty, kind.to_mutbl_lossy())
};
self.codegen_place_to_pointer(bx, place, mk_ptr) self.codegen_place_to_pointer(bx, place, mk_ptr)
} }

View file

@ -518,7 +518,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
} }
Rvalue::Ref(_, BorrowKind::Mut { .. }, place) Rvalue::Ref(_, BorrowKind::Mut { .. }, place)
| Rvalue::RawPtr(Mutability::Mut, place) => { | Rvalue::RawPtr(RawPtrKind::Mut, place) => {
// Inside mutable statics, we allow arbitrary mutable references. // Inside mutable statics, we allow arbitrary mutable references.
// We've allowed `static mut FOO = &mut [elements];` for a long time (the exact // We've allowed `static mut FOO = &mut [elements];` for a long time (the exact
// reasons why are lost to history), and there is no reason to restrict that to // reasons why are lost to history), and there is no reason to restrict that to
@ -536,7 +536,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
} }
Rvalue::Ref(_, BorrowKind::Shared | BorrowKind::Fake(_), place) Rvalue::Ref(_, BorrowKind::Shared | BorrowKind::Fake(_), place)
| Rvalue::RawPtr(Mutability::Not, place) => { | Rvalue::RawPtr(RawPtrKind::Const, place) => {
let borrowed_place_has_mut_interior = qualifs::in_place::<HasMutInterior, _>( let borrowed_place_has_mut_interior = qualifs::in_place::<HasMutInterior, _>(
self.ccx, self.ccx,
&mut |local| self.qualifs.has_mut_interior(self.ccx, local, location), &mut |local| self.qualifs.has_mut_interior(self.ccx, local, location),
@ -548,6 +548,12 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
} }
} }
Rvalue::RawPtr(RawPtrKind::FakeForPtrMetadata, place) => {
// These are only inserted for slice length, so the place must already be indirect.
// This implies we do not have to worry about whether the borrow escapes.
assert!(place.is_indirect(), "fake borrows are always indirect");
}
Rvalue::Cast( Rvalue::Cast(
CastKind::PointerCoercion( CastKind::PointerCoercion(
PointerCoercion::MutToConstPointer PointerCoercion::MutToConstPointer
@ -600,12 +606,8 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
} }
} }
UnOp::PtrMetadata => { UnOp::PtrMetadata => {
if !ty.is_ref() && !ty.is_unsafe_ptr() { // Getting the metadata from a pointer is always const.
span_bug!( // We already validated the type is valid in the validator.
self.span,
"non-pointer type in `Rvalue::UnaryOp({op:?})`: {ty:?}",
);
}
} }
} }
} }

View file

@ -9,7 +9,6 @@ use rustc_middle::ty::layout::FnAbiOf;
use rustc_middle::ty::{self, Instance, Ty}; use rustc_middle::ty::{self, Instance, Ty};
use rustc_middle::{bug, mir, span_bug}; use rustc_middle::{bug, mir, span_bug};
use rustc_span::source_map::Spanned; use rustc_span::source_map::Spanned;
use rustc_span::{DesugaringKind, Span};
use rustc_target::callconv::FnAbi; use rustc_target::callconv::FnAbi;
use tracing::{info, instrument, trace}; use tracing::{info, instrument, trace};
@ -81,9 +80,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
use rustc_middle::mir::StatementKind::*; use rustc_middle::mir::StatementKind::*;
match &stmt.kind { match &stmt.kind {
Assign(box (place, rvalue)) => { Assign(box (place, rvalue)) => self.eval_rvalue_into_place(rvalue, *place)?,
self.eval_rvalue_into_place(rvalue, *place, stmt.source_info.span)?
}
SetDiscriminant { place, variant_index } => { SetDiscriminant { place, variant_index } => {
let dest = self.eval_place(**place)?; let dest = self.eval_place(**place)?;
@ -162,7 +159,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
&mut self, &mut self,
rvalue: &mir::Rvalue<'tcx>, rvalue: &mir::Rvalue<'tcx>,
place: mir::Place<'tcx>, place: mir::Place<'tcx>,
span: Span,
) -> InterpResult<'tcx> { ) -> InterpResult<'tcx> {
let dest = self.eval_place(place)?; let dest = self.eval_place(place)?;
// FIXME: ensure some kind of non-aliasing between LHS and RHS? // FIXME: ensure some kind of non-aliasing between LHS and RHS?
@ -241,7 +237,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
self.write_immediate(*val, &dest)?; self.write_immediate(*val, &dest)?;
} }
RawPtr(_, place) => { RawPtr(kind, place) => {
// Figure out whether this is an addr_of of an already raw place. // Figure out whether this is an addr_of of an already raw place.
let place_base_raw = if place.is_indirect_first_projection() { let place_base_raw = if place.is_indirect_first_projection() {
let ty = self.frame().body.local_decls[place.local].ty; let ty = self.frame().body.local_decls[place.local].ty;
@ -254,13 +250,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
let src = self.eval_place(place)?; let src = self.eval_place(place)?;
let place = self.force_allocation(&src)?; let place = self.force_allocation(&src)?;
let mut val = ImmTy::from_immediate(place.to_ref(self), dest.layout); let mut val = ImmTy::from_immediate(place.to_ref(self), dest.layout);
if !place_base_raw if !place_base_raw && !kind.is_fake() {
&& span.desugaring_kind() != Some(DesugaringKind::IndexBoundsCheckReborrow) // If this was not already raw, it needs retagging -- except for "fake"
{ // raw borrows whose defining property is that they do not get retagged.
// If this was not already raw, it needs retagging.
// As a special hack, we exclude the desugared `PtrMetadata(&raw const *_n)`
// from indexing. (Really we should not do any retag on `&raw` but that does not
// currently work with Stacked Borrows.)
val = M::retag_ptr_value(self, mir::RetagKind::Raw, &val)?; val = M::retag_ptr_value(self, mir::RetagKind::Raw, &val)?;
} }
self.write_immediate(*val, &dest)?; self.write_immediate(*val, &dest)?;

View file

@ -180,6 +180,59 @@ pub enum BorrowKind {
Mut { kind: MutBorrowKind }, Mut { kind: MutBorrowKind },
} }
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, TyEncodable, TyDecodable)]
#[derive(Hash, HashStable)]
pub enum RawPtrKind {
Mut,
Const,
/// Creates a raw pointer to a place that will only be used to access its metadata,
/// not the data behind the pointer. Note that this limitation is *not* enforced
/// by the validator.
///
/// The borrow checker allows overlap of these raw pointers with references to the
/// data. This is sound even if the pointer is "misused" since any such use is anyway
/// unsafe. In terms of the operational semantics (i.e., Miri), this is equivalent
/// to `RawPtrKind::Mut`, but will never incur a retag.
FakeForPtrMetadata,
}
impl From<Mutability> for RawPtrKind {
fn from(other: Mutability) -> Self {
match other {
Mutability::Mut => RawPtrKind::Mut,
Mutability::Not => RawPtrKind::Const,
}
}
}
impl RawPtrKind {
pub fn is_fake(self) -> bool {
match self {
RawPtrKind::Mut | RawPtrKind::Const => false,
RawPtrKind::FakeForPtrMetadata => true,
}
}
pub fn to_mutbl_lossy(self) -> Mutability {
match self {
RawPtrKind::Mut => Mutability::Mut,
RawPtrKind::Const => Mutability::Not,
// We have no type corresponding to a fake borrow, so use
// `*const` as an approximation.
RawPtrKind::FakeForPtrMetadata => Mutability::Not,
}
}
pub fn ptr_str(self) -> &'static str {
match self {
RawPtrKind::Mut => "mut",
RawPtrKind::Const => "const",
RawPtrKind::FakeForPtrMetadata => "const (fake)",
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, TyEncodable, TyDecodable)] #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, TyEncodable, TyDecodable)]
#[derive(Hash, HashStable)] #[derive(Hash, HashStable)]
pub enum MutBorrowKind { pub enum MutBorrowKind {
@ -1356,7 +1409,7 @@ pub enum Rvalue<'tcx> {
/// ///
/// Like with references, the semantics of this operation are heavily dependent on the aliasing /// Like with references, the semantics of this operation are heavily dependent on the aliasing
/// model. /// model.
RawPtr(Mutability, Place<'tcx>), RawPtr(RawPtrKind, Place<'tcx>),
/// Yields the length of the place, as a `usize`. /// Yields the length of the place, as a `usize`.
/// ///

View file

@ -206,9 +206,9 @@ impl<'tcx> Rvalue<'tcx> {
let place_ty = place.ty(local_decls, tcx).ty; let place_ty = place.ty(local_decls, tcx).ty;
Ty::new_ref(tcx, reg, place_ty, bk.to_mutbl_lossy()) Ty::new_ref(tcx, reg, place_ty, bk.to_mutbl_lossy())
} }
Rvalue::RawPtr(mutability, ref place) => { Rvalue::RawPtr(kind, ref place) => {
let place_ty = place.ty(local_decls, tcx).ty; let place_ty = place.ty(local_decls, tcx).ty;
Ty::new_ptr(tcx, place_ty, mutability) Ty::new_ptr(tcx, place_ty, kind.to_mutbl_lossy())
} }
Rvalue::Len(..) => tcx.types.usize, Rvalue::Len(..) => tcx.types.usize,
Rvalue::Cast(.., ty) => ty, Rvalue::Cast(.., ty) => ty,

View file

@ -15,6 +15,7 @@ TrivialTypeTraversalImpls! {
SourceScopeLocalData, SourceScopeLocalData,
UserTypeAnnotationIndex, UserTypeAnnotationIndex,
BorrowKind, BorrowKind,
RawPtrKind,
CastKind, CastKind,
BasicBlock, BasicBlock,
SwitchTargets, SwitchTargets,

View file

@ -685,12 +685,15 @@ macro_rules! make_mir_visitor {
Rvalue::RawPtr(m, path) => { Rvalue::RawPtr(m, path) => {
let ctx = match m { let ctx = match m {
Mutability::Mut => PlaceContext::MutatingUse( RawPtrKind::Mut => PlaceContext::MutatingUse(
MutatingUseContext::RawBorrow MutatingUseContext::RawBorrow
), ),
Mutability::Not => PlaceContext::NonMutatingUse( RawPtrKind::Const => PlaceContext::NonMutatingUse(
NonMutatingUseContext::RawBorrow NonMutatingUseContext::RawBorrow
), ),
RawPtrKind::FakeForPtrMetadata => PlaceContext::NonMutatingUse(
NonMutatingUseContext::Inspect
),
}; };
self.visit_place(path, ctx, location); self.visit_place(path, ctx, location);
} }

View file

@ -253,7 +253,7 @@ impl<'a, 'tcx> ParseCtxt<'a, 'tcx> {
Rvalue::Ref(self.tcx.lifetimes.re_erased, *borrow_kind, self.parse_place(*arg)?) Rvalue::Ref(self.tcx.lifetimes.re_erased, *borrow_kind, self.parse_place(*arg)?)
), ),
ExprKind::RawBorrow { mutability, arg } => Ok( ExprKind::RawBorrow { mutability, arg } => Ok(
Rvalue::RawPtr(*mutability, self.parse_place(*arg)?) Rvalue::RawPtr((*mutability).into(), self.parse_place(*arg)?)
), ),
ExprKind::Binary { op, lhs, rhs } => Ok( ExprKind::Binary { op, lhs, rhs } => Ok(
Rvalue::BinaryOp(*op, Box::new((self.parse_operand(*lhs)?, self.parse_operand(*rhs)?))) Rvalue::BinaryOp(*op, Box::new((self.parse_operand(*lhs)?, self.parse_operand(*rhs)?)))

View file

@ -11,7 +11,7 @@ use rustc_middle::mir::*;
use rustc_middle::thir::*; use rustc_middle::thir::*;
use rustc_middle::ty::{self, AdtDef, CanonicalUserTypeAnnotation, Ty, Variance}; use rustc_middle::ty::{self, AdtDef, CanonicalUserTypeAnnotation, Ty, Variance};
use rustc_middle::{bug, span_bug}; use rustc_middle::{bug, span_bug};
use rustc_span::{DesugaringKind, Span}; use rustc_span::Span;
use tracing::{debug, instrument, trace}; use tracing::{debug, instrument, trace};
use crate::builder::ForGuard::{OutsideGuard, RefWithinGuard}; use crate::builder::ForGuard::{OutsideGuard, RefWithinGuard};
@ -643,8 +643,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
source_info: SourceInfo, source_info: SourceInfo,
) -> Operand<'tcx> { ) -> Operand<'tcx> {
let place_ty = place.ty(&self.local_decls, self.tcx).ty; let place_ty = place.ty(&self.local_decls, self.tcx).ty;
let usize_ty = self.tcx.types.usize;
match place_ty.kind() { match place_ty.kind() {
ty::Array(_elem_ty, len_const) => { ty::Array(_elem_ty, len_const) => {
// We know how long an array is, so just use that as a constant // We know how long an array is, so just use that as a constant
@ -668,27 +666,18 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// the MIR we're building here needs to pass NLL later. // the MIR we're building here needs to pass NLL later.
Operand::Copy(Place::from(place.local)) Operand::Copy(Place::from(place.local))
} else { } else {
let len_span = self.tcx.with_stable_hashing_context(|hcx| {
let span = source_info.span;
span.mark_with_reason(
None,
DesugaringKind::IndexBoundsCheckReborrow,
span.edition(),
hcx,
)
});
let ptr_ty = Ty::new_imm_ptr(self.tcx, place_ty); let ptr_ty = Ty::new_imm_ptr(self.tcx, place_ty);
let slice_ptr = self.temp(ptr_ty, span); let slice_ptr = self.temp(ptr_ty, span);
self.cfg.push_assign( self.cfg.push_assign(
block, block,
SourceInfo { span: len_span, ..source_info }, source_info,
slice_ptr, slice_ptr,
Rvalue::RawPtr(Mutability::Not, place), Rvalue::RawPtr(RawPtrKind::FakeForPtrMetadata, place),
); );
Operand::Move(slice_ptr) Operand::Move(slice_ptr)
}; };
let len = self.temp(usize_ty, span); let len = self.temp(self.tcx.types.usize, span);
self.cfg.push_assign( self.cfg.push_assign(
block, block,
source_info, source_info,

View file

@ -303,7 +303,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
hir::Mutability::Not => this.as_read_only_place(block, arg), hir::Mutability::Not => this.as_read_only_place(block, arg),
hir::Mutability::Mut => this.as_place(block, arg), hir::Mutability::Mut => this.as_place(block, arg),
}; };
let address_of = Rvalue::RawPtr(mutability, unpack!(block = place)); let address_of = Rvalue::RawPtr(mutability.into(), unpack!(block = place));
this.cfg.push_assign(block, source_info, destination, address_of); this.cfg.push_assign(block, source_info, destination, address_of);
block.unit() block.unit()
} }

View file

@ -700,7 +700,7 @@ where
statements: vec![ statements: vec![
self.assign( self.assign(
ptr, ptr,
Rvalue::RawPtr(Mutability::Mut, tcx.mk_place_index(self.place, cur)), Rvalue::RawPtr(RawPtrKind::Mut, tcx.mk_place_index(self.place, cur)),
), ),
self.assign( self.assign(
cur.into(), cur.into(),
@ -816,7 +816,7 @@ where
let mut delegate_block = BasicBlockData { let mut delegate_block = BasicBlockData {
statements: vec![ statements: vec![
self.assign(Place::from(array_ptr), Rvalue::RawPtr(Mutability::Mut, self.place)), self.assign(Place::from(array_ptr), Rvalue::RawPtr(RawPtrKind::Mut, self.place)),
self.assign( self.assign(
Place::from(slice_ptr), Place::from(slice_ptr),
Rvalue::Cast( Rvalue::Cast(

View file

@ -192,7 +192,7 @@ enum AggregateTy<'tcx> {
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
enum AddressKind { enum AddressKind {
Ref(BorrowKind), Ref(BorrowKind),
Address(Mutability), Address(RawPtrKind),
} }
#[derive(Debug, PartialEq, Eq, Hash)] #[derive(Debug, PartialEq, Eq, Hash)]
@ -504,7 +504,9 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
mplace.layout.ty, mplace.layout.ty,
bk.to_mutbl_lossy(), bk.to_mutbl_lossy(),
), ),
AddressKind::Address(mutbl) => Ty::new_ptr(self.tcx, mplace.layout.ty, mutbl), AddressKind::Address(mutbl) => {
Ty::new_ptr(self.tcx, mplace.layout.ty, mutbl.to_mutbl_lossy())
}
}; };
let layout = self.ecx.layout_of(ty).ok()?; let layout = self.ecx.layout_of(ty).ok()?;
ImmTy::from_immediate(pointer, layout).into() ImmTy::from_immediate(pointer, layout).into()

View file

@ -125,7 +125,7 @@ impl<'tcx> crate::MirPass<'tcx> for EnumSizeOpt {
source_info, source_info,
kind: StatementKind::Assign(Box::new(( kind: StatementKind::Assign(Box::new((
dst, dst,
Rvalue::RawPtr(Mutability::Mut, *lhs), Rvalue::RawPtr(RawPtrKind::Mut, *lhs),
))), ))),
}; };
@ -146,7 +146,7 @@ impl<'tcx> crate::MirPass<'tcx> for EnumSizeOpt {
source_info, source_info,
kind: StatementKind::Assign(Box::new(( kind: StatementKind::Assign(Box::new((
src, src,
Rvalue::RawPtr(Mutability::Not, *rhs), Rvalue::RawPtr(RawPtrKind::Const, *rhs),
))), ))),
}; };

View file

@ -2,17 +2,11 @@ use std::iter;
use itertools::Itertools; use itertools::Itertools;
use rustc_abi::{FieldIdx, VariantIdx}; use rustc_abi::{FieldIdx, VariantIdx};
use rustc_ast::Mutability;
use rustc_const_eval::interpret; use rustc_const_eval::interpret;
use rustc_hir::def_id::DefId; use rustc_hir::def_id::DefId;
use rustc_hir::lang_items::LangItem; use rustc_hir::lang_items::LangItem;
use rustc_index::{Idx, IndexVec}; use rustc_index::{Idx, IndexVec};
use rustc_middle::mir::{ use rustc_middle::mir::*;
BasicBlock, BasicBlockData, Body, CallSource, CastKind, CoercionSource, Const, ConstOperand,
ConstValue, Local, LocalDecl, MirSource, Operand, Place, PlaceElem, RETURN_PLACE, Rvalue,
SourceInfo, Statement, StatementKind, Terminator, TerminatorKind, UnwindAction,
UnwindTerminateReason,
};
use rustc_middle::ty::adjustment::PointerCoercion; use rustc_middle::ty::adjustment::PointerCoercion;
use rustc_middle::ty::util::{AsyncDropGlueMorphology, Discr}; use rustc_middle::ty::util::{AsyncDropGlueMorphology, Discr};
use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::ty::{self, Ty, TyCtxt};
@ -345,7 +339,7 @@ impl<'tcx> AsyncDestructorCtorShimBuilder<'tcx> {
.tcx .tcx
.mk_place_elems(&[PlaceElem::Deref, PlaceElem::Field(field, field_ty)]), .mk_place_elems(&[PlaceElem::Deref, PlaceElem::Field(field, field_ty)]),
}; };
self.put_temp_rvalue(Rvalue::RawPtr(Mutability::Mut, place)) self.put_temp_rvalue(Rvalue::RawPtr(RawPtrKind::Mut, place))
} }
/// If given Self is an enum puts `to_drop: *mut FieldTy` on top of /// If given Self is an enum puts `to_drop: *mut FieldTy` on top of
@ -365,7 +359,7 @@ impl<'tcx> AsyncDestructorCtorShimBuilder<'tcx> {
PlaceElem::Field(field, field_ty), PlaceElem::Field(field, field_ty),
]), ]),
}; };
self.put_temp_rvalue(Rvalue::RawPtr(Mutability::Mut, place)) self.put_temp_rvalue(Rvalue::RawPtr(RawPtrKind::Mut, place))
} }
/// If given Self is an enum puts `to_drop: *mut FieldTy` on top of /// If given Self is an enum puts `to_drop: *mut FieldTy` on top of

View file

@ -232,6 +232,18 @@ impl<'tcx> Stable<'tcx> for mir::Mutability {
} }
} }
impl<'tcx> Stable<'tcx> for mir::RawPtrKind {
type T = stable_mir::mir::RawPtrKind;
fn stable(&self, _: &mut Tables<'_>) -> Self::T {
use mir::RawPtrKind::*;
match *self {
Const => stable_mir::mir::RawPtrKind::Const,
Mut => stable_mir::mir::RawPtrKind::Mut,
FakeForPtrMetadata => stable_mir::mir::RawPtrKind::FakeForPtrMetadata,
}
}
}
impl<'tcx> Stable<'tcx> for mir::BorrowKind { impl<'tcx> Stable<'tcx> for mir::BorrowKind {
type T = stable_mir::mir::BorrowKind; type T = stable_mir::mir::BorrowKind;
fn stable(&self, tables: &mut Tables<'_>) -> Self::T { fn stable(&self, tables: &mut Tables<'_>) -> Self::T {

View file

@ -1163,9 +1163,6 @@ pub enum DesugaringKind {
WhileLoop, WhileLoop,
/// `async Fn()` bound modifier /// `async Fn()` bound modifier
BoundModifier, BoundModifier,
/// Marks a `&raw const *_1` needed as part of getting the length of a mutable
/// slice for the bounds check, so that MIRI's retag handling can recognize it.
IndexBoundsCheckReborrow,
} }
impl DesugaringKind { impl DesugaringKind {
@ -1182,7 +1179,6 @@ impl DesugaringKind {
DesugaringKind::ForLoop => "`for` loop", DesugaringKind::ForLoop => "`for` loop",
DesugaringKind::WhileLoop => "`while` loop", DesugaringKind::WhileLoop => "`while` loop",
DesugaringKind::BoundModifier => "trait bound modifier", DesugaringKind::BoundModifier => "trait bound modifier",
DesugaringKind::IndexBoundsCheckReborrow => "slice indexing",
} }
} }
} }

View file

@ -457,7 +457,7 @@ pub enum Rvalue {
/// ///
/// This is generated by pointer casts like `&v as *const _` or raw address of expressions like /// This is generated by pointer casts like `&v as *const _` or raw address of expressions like
/// `&raw v` or `addr_of!(v)`. /// `&raw v` or `addr_of!(v)`.
AddressOf(Mutability, Place), AddressOf(RawPtrKind, Place),
/// Creates an aggregate value, like a tuple or struct. /// Creates an aggregate value, like a tuple or struct.
/// ///
@ -577,7 +577,7 @@ impl Rvalue {
} }
Rvalue::AddressOf(mutability, place) => { Rvalue::AddressOf(mutability, place) => {
let place_ty = place.ty(locals)?; let place_ty = place.ty(locals)?;
Ok(Ty::new_ptr(place_ty, *mutability)) Ok(Ty::new_ptr(place_ty, mutability.to_mutable_lossy()))
} }
Rvalue::Len(..) => Ok(Ty::usize_ty()), Rvalue::Len(..) => Ok(Ty::usize_ty()),
Rvalue::Cast(.., ty) => Ok(*ty), Rvalue::Cast(.., ty) => Ok(*ty),
@ -903,6 +903,24 @@ impl BorrowKind {
} }
} }
#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)]
pub enum RawPtrKind {
Mut,
Const,
FakeForPtrMetadata,
}
impl RawPtrKind {
pub fn to_mutable_lossy(self) -> Mutability {
match self {
RawPtrKind::Mut { .. } => Mutability::Mut,
RawPtrKind::Const => Mutability::Not,
// FIXME: There's no type corresponding to a shallow borrow, so use `&` as an approximation.
RawPtrKind::FakeForPtrMetadata => Mutability::Not,
}
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] #[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)]
pub enum MutBorrowKind { pub enum MutBorrowKind {
Default, Default,

View file

@ -6,7 +6,9 @@ use std::{fmt, io, iter};
use fmt::{Display, Formatter}; use fmt::{Display, Formatter};
use super::{AggregateKind, AssertMessage, BinOp, BorrowKind, FakeBorrowKind, TerminatorKind}; use super::{AggregateKind, AssertMessage, BinOp, BorrowKind, FakeBorrowKind, TerminatorKind};
use crate::mir::{Operand, Place, Rvalue, StatementKind, UnwindAction, VarDebugInfoContents}; use crate::mir::{
Operand, Place, RawPtrKind, Rvalue, StatementKind, UnwindAction, VarDebugInfoContents,
};
use crate::ty::{AdtKind, IndexedVal, MirConst, Ty, TyConst}; use crate::ty::{AdtKind, IndexedVal, MirConst, Ty, TyConst};
use crate::{Body, CrateDef, Mutability, with}; use crate::{Body, CrateDef, Mutability, with};
@ -325,7 +327,7 @@ fn pretty_ty_const(ct: &TyConst) -> String {
fn pretty_rvalue<W: Write>(writer: &mut W, rval: &Rvalue) -> io::Result<()> { fn pretty_rvalue<W: Write>(writer: &mut W, rval: &Rvalue) -> io::Result<()> {
match rval { match rval {
Rvalue::AddressOf(mutability, place) => { Rvalue::AddressOf(mutability, place) => {
write!(writer, "&raw {} {:?}", pretty_mut(*mutability), place) write!(writer, "&raw {} {:?}", pretty_raw_ptr_kind(*mutability), place)
} }
Rvalue::Aggregate(aggregate_kind, operands) => { Rvalue::Aggregate(aggregate_kind, operands) => {
// FIXME: Add pretty_aggregate function that returns a pretty string // FIXME: Add pretty_aggregate function that returns a pretty string
@ -437,3 +439,11 @@ fn pretty_mut(mutability: Mutability) -> &'static str {
Mutability::Mut => "mut ", Mutability::Mut => "mut ",
} }
} }
fn pretty_raw_ptr_kind(kind: RawPtrKind) -> &'static str {
match kind {
RawPtrKind::Const => "const",
RawPtrKind::Mut => "mut",
RawPtrKind::FakeForPtrMetadata => "const (fake)",
}
}

View file

@ -308,7 +308,7 @@ pub trait MirVisitor {
fn super_rvalue(&mut self, rvalue: &Rvalue, location: Location) { fn super_rvalue(&mut self, rvalue: &Rvalue, location: Location) {
match rvalue { match rvalue {
Rvalue::AddressOf(mutability, place) => { Rvalue::AddressOf(mutability, place) => {
let pcx = PlaceContext { is_mut: *mutability == Mutability::Mut }; let pcx = PlaceContext { is_mut: *mutability == RawPtrKind::Mut };
self.visit_place(place, pcx, location); self.visit_place(place, pcx, location);
} }
Rvalue::Aggregate(_, operands) => { Rvalue::Aggregate(_, operands) => {

View file

@ -0,0 +1,35 @@
// This is a regression test for issue #135671 where a MIR refactor about arrays and their lengths
// unexpectedly caused borrowck errors for disjoint borrows of array elements, for which we had no
// tests. This is a collection of a few code samples from that issue.
//@revisions: stack tree
//@[tree]compile-flags: -Zmiri-tree-borrows
struct Test {
a: i32,
b: i32,
}
fn one() {
let inputs: &mut [_] = &mut [Test { a: 0, b: 0 }];
let a = &mut inputs[0].a;
let b = &mut inputs[0].b;
*a = 0;
*b = 1;
}
fn two() {
let slice = &mut [(0, 0)][..];
std::mem::swap(&mut slice[0].0, &mut slice[0].1);
}
fn three(a: &mut [(i32, i32)], i: usize, j: usize) -> (&mut i32, &mut i32) {
(&mut a[i].0, &mut a[j].1)
}
fn main() {
one();
two();
three(&mut [(1, 2), (3, 4)], 0, 1);
}

View file

@ -14,7 +14,7 @@ fn index_custom(_1: &WithSliceTail, _2: usize) -> &i32 {
StorageLive(_3); StorageLive(_3);
StorageLive(_4); StorageLive(_4);
_4 = copy _2; _4 = copy _2;
_5 = &raw const ((*_1).1: [i32]); _5 = &raw const (fake) ((*_1).1: [i32]);
_6 = PtrMetadata(move _5); _6 = PtrMetadata(move _5);
_7 = Lt(copy _4, copy _6); _7 = Lt(copy _4, copy _6);
assert(move _7, "index out of bounds: the length is {} but the index is {}", move _6, copy _4) -> [success: bb1, unwind: bb2]; assert(move _7, "index out of bounds: the length is {} but the index is {}", move _6, copy _4) -> [success: bb1, unwind: bb2];

View file

@ -14,7 +14,7 @@ fn index_mut_slice(_1: &mut [i32], _2: usize) -> &i32 {
StorageLive(_3); StorageLive(_3);
StorageLive(_4); StorageLive(_4);
_4 = copy _2; _4 = copy _2;
_5 = &raw const (*_1); _5 = &raw const (fake) (*_1);
_6 = PtrMetadata(move _5); _6 = PtrMetadata(move _5);
_7 = Lt(copy _4, copy _6); _7 = Lt(copy _4, copy _6);
assert(move _7, "index out of bounds: the length is {} but the index is {}", move _6, copy _4) -> [success: bb1, unwind: bb2]; assert(move _7, "index out of bounds: the length is {} but the index is {}", move _6, copy _4) -> [success: bb1, unwind: bb2];

View file

@ -54,7 +54,7 @@ struct WithSliceTail(f64, [i32]);
// EMIT_MIR index_array_and_slice.index_custom.built.after.mir // EMIT_MIR index_array_and_slice.index_custom.built.after.mir
fn index_custom(custom: &WithSliceTail, index: usize) -> &i32 { fn index_custom(custom: &WithSliceTail, index: usize) -> &i32 {
// CHECK: bb0: // CHECK: bb0:
// CHECK: [[PTR:_.+]] = &raw const ((*_1).1: [i32]); // CHECK: [[PTR:_.+]] = &raw const (fake) ((*_1).1: [i32]);
// CHECK: [[LEN:_.+]] = PtrMetadata(move [[PTR]]); // CHECK: [[LEN:_.+]] = PtrMetadata(move [[PTR]]);
// CHECK: [[LT:_.+]] = Lt(copy _2, copy [[LEN]]); // CHECK: [[LT:_.+]] = Lt(copy _2, copy [[LEN]]);
// CHECK: assert(move [[LT]], "index out of bounds{{.+}}", move [[LEN]], copy _2) -> [success: bb1, // CHECK: assert(move [[LT]], "index out of bounds{{.+}}", move [[LEN]], copy _2) -> [success: bb1,

View file

@ -18,7 +18,7 @@ fn foo(_1: Box<[T]>) -> T {
StorageLive(_3); StorageLive(_3);
StorageLive(_4); StorageLive(_4);
_4 = const 0_usize; _4 = const 0_usize;
_5 = &raw const (*_1); _5 = &raw const (fake) (*_1);
_6 = PtrMetadata(move _5); _6 = PtrMetadata(move _5);
_7 = Lt(copy _4, copy _6); _7 = Lt(copy _4, copy _6);
assert(move _7, "index out of bounds: the length is {} but the index is {}", move _6, copy _4) -> [success: bb1, unwind: bb5]; assert(move _7, "index out of bounds: the length is {} but the index is {}", move _6, copy _4) -> [success: bb1, unwind: bb5];