Rollup merge of #122598 - Nadrieril:full-derefpats, r=matthewjasper
deref patterns: lower deref patterns to MIR This lowers deref patterns to MIR. This is a bit tricky because this is the first kind of pattern that requires storing a value in a temporary. Thanks to https://github.com/rust-lang/rust/pull/123324 false edges are no longer a problem. The thing I'm not confident about is the handling of fake borrows. This PR ignores any fake borrows inside a deref pattern. We are guaranteed to at least fake borrow the place of the first pointer value, which could be enough, but I'm not certain.
This commit is contained in:
commit
332cac2c6d
@ -69,7 +69,8 @@ impl<'tcx> fmt::Display for BorrowData<'tcx> {
|
||||
fn fmt(&self, w: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let kind = match self.kind {
|
||||
mir::BorrowKind::Shared => "",
|
||||
mir::BorrowKind::Fake => "fake ",
|
||||
mir::BorrowKind::Fake(mir::FakeBorrowKind::Deep) => "fake ",
|
||||
mir::BorrowKind::Fake(mir::FakeBorrowKind::Shallow) => "fake shallow ",
|
||||
mir::BorrowKind::Mut { kind: mir::MutBorrowKind::ClosureCapture } => "uniq ",
|
||||
// FIXME: differentiate `TwoPhaseBorrow`
|
||||
mir::BorrowKind::Mut {
|
||||
|
@ -17,9 +17,9 @@ use rustc_middle::hir::nested_filter::OnlyBodies;
|
||||
use rustc_middle::mir::tcx::PlaceTy;
|
||||
use rustc_middle::mir::{
|
||||
self, AggregateKind, BindingForm, BorrowKind, CallSource, ClearCrossCrate, ConstraintCategory,
|
||||
FakeReadCause, LocalDecl, LocalInfo, LocalKind, Location, MutBorrowKind, Operand, Place,
|
||||
PlaceRef, ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind,
|
||||
VarBindingForm,
|
||||
FakeBorrowKind, FakeReadCause, LocalDecl, LocalInfo, LocalKind, Location, MutBorrowKind,
|
||||
Operand, Place, PlaceRef, ProjectionElem, Rvalue, Statement, StatementKind, Terminator,
|
||||
TerminatorKind, VarBindingForm,
|
||||
};
|
||||
use rustc_middle::ty::{
|
||||
self, suggest_constraining_type_params, PredicateKind, ToPredicate, Ty, TyCtxt,
|
||||
@ -1486,7 +1486,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
let first_borrow_desc;
|
||||
let mut err = match (gen_borrow_kind, issued_borrow.kind) {
|
||||
(
|
||||
BorrowKind::Shared,
|
||||
BorrowKind::Shared | BorrowKind::Fake(FakeBorrowKind::Deep),
|
||||
BorrowKind::Mut { kind: MutBorrowKind::Default | MutBorrowKind::TwoPhaseBorrow },
|
||||
) => {
|
||||
first_borrow_desc = "mutable ";
|
||||
@ -1504,7 +1504,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
}
|
||||
(
|
||||
BorrowKind::Mut { kind: MutBorrowKind::Default | MutBorrowKind::TwoPhaseBorrow },
|
||||
BorrowKind::Shared,
|
||||
BorrowKind::Shared | BorrowKind::Fake(FakeBorrowKind::Deep),
|
||||
) => {
|
||||
first_borrow_desc = "immutable ";
|
||||
let mut err = self.cannot_reborrow_already_borrowed(
|
||||
@ -1566,7 +1566,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
self.cannot_uniquely_borrow_by_two_closures(span, &desc_place, issued_span, None)
|
||||
}
|
||||
|
||||
(BorrowKind::Mut { .. }, BorrowKind::Fake) => {
|
||||
(BorrowKind::Mut { .. }, BorrowKind::Fake(FakeBorrowKind::Shallow)) => {
|
||||
if let Some(immutable_section_description) =
|
||||
self.classify_immutable_section(issued_borrow.assigned_place)
|
||||
{
|
||||
@ -1629,7 +1629,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
)
|
||||
}
|
||||
|
||||
(BorrowKind::Shared, BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture }) => {
|
||||
(
|
||||
BorrowKind::Shared | BorrowKind::Fake(FakeBorrowKind::Deep),
|
||||
BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture },
|
||||
) => {
|
||||
first_borrow_desc = "first ";
|
||||
self.cannot_reborrow_already_uniquely_borrowed(
|
||||
span,
|
||||
@ -1659,8 +1662,14 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
)
|
||||
}
|
||||
|
||||
(BorrowKind::Shared, BorrowKind::Shared | BorrowKind::Fake)
|
||||
| (BorrowKind::Fake, BorrowKind::Mut { .. } | BorrowKind::Shared | BorrowKind::Fake) => {
|
||||
(
|
||||
BorrowKind::Shared | BorrowKind::Fake(FakeBorrowKind::Deep),
|
||||
BorrowKind::Shared | BorrowKind::Fake(_),
|
||||
)
|
||||
| (
|
||||
BorrowKind::Fake(FakeBorrowKind::Shallow),
|
||||
BorrowKind::Mut { .. } | BorrowKind::Shared | BorrowKind::Fake(_),
|
||||
) => {
|
||||
unreachable!()
|
||||
}
|
||||
};
|
||||
@ -3572,7 +3581,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
let loan_span = loan_spans.args_or_use();
|
||||
|
||||
let descr_place = self.describe_any_place(place.as_ref());
|
||||
if loan.kind == BorrowKind::Fake {
|
||||
if let BorrowKind::Fake(_) = loan.kind {
|
||||
if let Some(section) = self.classify_immutable_section(loan.assigned_place) {
|
||||
let mut err = self.cannot_mutate_in_immutable_section(
|
||||
span,
|
||||
|
@ -654,7 +654,7 @@ impl UseSpans<'_> {
|
||||
match kind {
|
||||
Some(kd) => match kd {
|
||||
rustc_middle::mir::BorrowKind::Shared
|
||||
| rustc_middle::mir::BorrowKind::Fake => {
|
||||
| rustc_middle::mir::BorrowKind::Fake(_) => {
|
||||
CaptureVarKind::Immut { kind_span: capture_kind_span }
|
||||
}
|
||||
|
||||
|
@ -1056,18 +1056,19 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
Control::Continue
|
||||
}
|
||||
|
||||
(Read(_), BorrowKind::Shared | BorrowKind::Fake)
|
||||
| (Read(ReadKind::Borrow(BorrowKind::Fake)), BorrowKind::Mut { .. }) => {
|
||||
Control::Continue
|
||||
}
|
||||
(Read(_), BorrowKind::Shared | BorrowKind::Fake(_))
|
||||
| (
|
||||
Read(ReadKind::Borrow(BorrowKind::Fake(FakeBorrowKind::Shallow))),
|
||||
BorrowKind::Mut { .. },
|
||||
) => Control::Continue,
|
||||
|
||||
(Reservation(_), BorrowKind::Fake | BorrowKind::Shared) => {
|
||||
(Reservation(_), BorrowKind::Fake(_) | BorrowKind::Shared) => {
|
||||
// This used to be a future compatibility warning (to be
|
||||
// disallowed on NLL). See rust-lang/rust#56254
|
||||
Control::Continue
|
||||
}
|
||||
|
||||
(Write(WriteKind::Move), BorrowKind::Fake) => {
|
||||
(Write(WriteKind::Move), BorrowKind::Fake(FakeBorrowKind::Shallow)) => {
|
||||
// Handled by initialization checks.
|
||||
Control::Continue
|
||||
}
|
||||
@ -1175,10 +1176,12 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
match rvalue {
|
||||
&Rvalue::Ref(_ /*rgn*/, bk, place) => {
|
||||
let access_kind = match bk {
|
||||
BorrowKind::Fake => {
|
||||
BorrowKind::Fake(FakeBorrowKind::Shallow) => {
|
||||
(Shallow(Some(ArtificialField::FakeBorrow)), Read(ReadKind::Borrow(bk)))
|
||||
}
|
||||
BorrowKind::Shared => (Deep, Read(ReadKind::Borrow(bk))),
|
||||
BorrowKind::Shared | BorrowKind::Fake(FakeBorrowKind::Deep) => {
|
||||
(Deep, Read(ReadKind::Borrow(bk)))
|
||||
}
|
||||
BorrowKind::Mut { .. } => {
|
||||
let wk = WriteKind::MutableBorrow(bk);
|
||||
if allow_two_phase_borrow(bk) {
|
||||
@ -1197,7 +1200,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
flow_state,
|
||||
);
|
||||
|
||||
let action = if bk == BorrowKind::Fake {
|
||||
let action = if bk == BorrowKind::Fake(FakeBorrowKind::Shallow) {
|
||||
InitializationRequiringAction::MatchOn
|
||||
} else {
|
||||
InitializationRequiringAction::Borrow
|
||||
@ -1557,7 +1560,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
|
||||
// only mutable borrows should be 2-phase
|
||||
assert!(match borrow.kind {
|
||||
BorrowKind::Shared | BorrowKind::Fake => false,
|
||||
BorrowKind::Shared | BorrowKind::Fake(_) => false,
|
||||
BorrowKind::Mut { .. } => true,
|
||||
});
|
||||
|
||||
@ -2122,14 +2125,14 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
| WriteKind::Replace
|
||||
| WriteKind::StorageDeadOrDrop
|
||||
| WriteKind::MutableBorrow(BorrowKind::Shared)
|
||||
| WriteKind::MutableBorrow(BorrowKind::Fake),
|
||||
| WriteKind::MutableBorrow(BorrowKind::Fake(_)),
|
||||
)
|
||||
| Write(
|
||||
WriteKind::Move
|
||||
| WriteKind::Replace
|
||||
| WriteKind::StorageDeadOrDrop
|
||||
| WriteKind::MutableBorrow(BorrowKind::Shared)
|
||||
| WriteKind::MutableBorrow(BorrowKind::Fake),
|
||||
| WriteKind::MutableBorrow(BorrowKind::Fake(_)),
|
||||
) => {
|
||||
if self.is_mutable(place.as_ref(), is_local_mutation_allowed).is_err()
|
||||
&& !self.has_buffered_diags()
|
||||
@ -2153,7 +2156,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
return false;
|
||||
}
|
||||
Read(
|
||||
ReadKind::Borrow(BorrowKind::Mut { .. } | BorrowKind::Shared | BorrowKind::Fake)
|
||||
ReadKind::Borrow(BorrowKind::Mut { .. } | BorrowKind::Shared | BorrowKind::Fake(_))
|
||||
| ReadKind::Copy,
|
||||
) => {
|
||||
// Access authorized
|
||||
|
@ -55,7 +55,7 @@ use crate::Overlap;
|
||||
use crate::{AccessDepth, Deep, Shallow};
|
||||
use rustc_hir as hir;
|
||||
use rustc_middle::mir::{
|
||||
Body, BorrowKind, MutBorrowKind, Place, PlaceElem, PlaceRef, ProjectionElem,
|
||||
Body, BorrowKind, FakeBorrowKind, MutBorrowKind, Place, PlaceElem, PlaceRef, ProjectionElem,
|
||||
};
|
||||
use rustc_middle::ty::{self, TyCtxt};
|
||||
use std::cmp::max;
|
||||
@ -271,10 +271,10 @@ fn place_components_conflict<'tcx>(
|
||||
// If the second example, where we did, then we still know
|
||||
// that the borrow can access a *part* of our place that
|
||||
// our access cares about, so we still have a conflict.
|
||||
if borrow_kind == BorrowKind::Fake
|
||||
if borrow_kind == BorrowKind::Fake(FakeBorrowKind::Shallow)
|
||||
&& borrow_place.projection.len() < access_place.projection.len()
|
||||
{
|
||||
debug!("borrow_conflicts_with_place: fake borrow");
|
||||
debug!("borrow_conflicts_with_place: shallow borrow");
|
||||
false
|
||||
} else {
|
||||
debug!("borrow_conflicts_with_place: full borrow, CONFLICT");
|
||||
|
@ -1,6 +1,8 @@
|
||||
use rustc_data_structures::graph::dominators::Dominators;
|
||||
use rustc_middle::mir::visit::Visitor;
|
||||
use rustc_middle::mir::{self, BasicBlock, Body, Location, NonDivergingIntrinsic, Place, Rvalue};
|
||||
use rustc_middle::mir::{
|
||||
self, BasicBlock, Body, FakeBorrowKind, Location, NonDivergingIntrinsic, Place, Rvalue,
|
||||
};
|
||||
use rustc_middle::mir::{BorrowKind, Mutability, Operand};
|
||||
use rustc_middle::mir::{InlineAsmOperand, Terminator, TerminatorKind};
|
||||
use rustc_middle::mir::{Statement, StatementKind};
|
||||
@ -239,10 +241,12 @@ impl<'cx, 'tcx> LoanInvalidationsGenerator<'cx, 'tcx> {
|
||||
match rvalue {
|
||||
&Rvalue::Ref(_ /*rgn*/, bk, place) => {
|
||||
let access_kind = match bk {
|
||||
BorrowKind::Fake => {
|
||||
BorrowKind::Fake(FakeBorrowKind::Shallow) => {
|
||||
(Shallow(Some(ArtificialField::FakeBorrow)), Read(ReadKind::Borrow(bk)))
|
||||
}
|
||||
BorrowKind::Shared => (Deep, Read(ReadKind::Borrow(bk))),
|
||||
BorrowKind::Shared | BorrowKind::Fake(FakeBorrowKind::Deep) => {
|
||||
(Deep, Read(ReadKind::Borrow(bk)))
|
||||
}
|
||||
BorrowKind::Mut { .. } => {
|
||||
let wk = WriteKind::MutableBorrow(bk);
|
||||
if allow_two_phase_borrow(bk) {
|
||||
@ -357,8 +361,11 @@ impl<'cx, 'tcx> LoanInvalidationsGenerator<'cx, 'tcx> {
|
||||
// have already taken the reservation
|
||||
}
|
||||
|
||||
(Read(_), BorrowKind::Fake | BorrowKind::Shared)
|
||||
| (Read(ReadKind::Borrow(BorrowKind::Fake)), BorrowKind::Mut { .. }) => {
|
||||
(Read(_), BorrowKind::Fake(_) | BorrowKind::Shared)
|
||||
| (
|
||||
Read(ReadKind::Borrow(BorrowKind::Fake(FakeBorrowKind::Shallow))),
|
||||
BorrowKind::Mut { .. },
|
||||
) => {
|
||||
// Reads don't invalidate shared or shallow borrows
|
||||
}
|
||||
|
||||
@ -403,7 +410,7 @@ impl<'cx, 'tcx> LoanInvalidationsGenerator<'cx, 'tcx> {
|
||||
|
||||
// only mutable borrows should be 2-phase
|
||||
assert!(match borrow.kind {
|
||||
BorrowKind::Shared | BorrowKind::Fake => false,
|
||||
BorrowKind::Shared | BorrowKind::Fake(_) => false,
|
||||
BorrowKind::Mut { .. } => true,
|
||||
});
|
||||
|
||||
|
@ -414,7 +414,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
|
||||
BorrowKind::Shared => {
|
||||
PlaceContext::NonMutatingUse(NonMutatingUseContext::SharedBorrow)
|
||||
}
|
||||
BorrowKind::Fake => {
|
||||
BorrowKind::Fake(_) => {
|
||||
PlaceContext::NonMutatingUse(NonMutatingUseContext::FakeBorrow)
|
||||
}
|
||||
BorrowKind::Mut { .. } => {
|
||||
@ -487,7 +487,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
Rvalue::Ref(_, BorrowKind::Shared | BorrowKind::Fake, place)
|
||||
Rvalue::Ref(_, BorrowKind::Shared | BorrowKind::Fake(_), place)
|
||||
| Rvalue::AddressOf(Mutability::Not, place) => {
|
||||
let borrowed_place_has_mut_interior = qualifs::in_place::<HasMutInterior, _>(
|
||||
self.ccx,
|
||||
|
@ -105,7 +105,7 @@ where
|
||||
fn ref_allows_mutation(&self, kind: mir::BorrowKind, place: mir::Place<'tcx>) -> bool {
|
||||
match kind {
|
||||
mir::BorrowKind::Mut { .. } => true,
|
||||
mir::BorrowKind::Shared | mir::BorrowKind::Fake => {
|
||||
mir::BorrowKind::Shared | mir::BorrowKind::Fake(_) => {
|
||||
self.shared_borrow_allows_mutation(place)
|
||||
}
|
||||
}
|
||||
|
@ -965,7 +965,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
},
|
||||
Rvalue::Ref(_, BorrowKind::Fake, _) => {
|
||||
Rvalue::Ref(_, BorrowKind::Fake(_), _) => {
|
||||
if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
|
||||
self.fail(
|
||||
location,
|
||||
|
@ -750,6 +750,15 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if let PatKind::Deref(subpattern) = pat.kind {
|
||||
// A deref pattern is a bit special: the binding mode of its inner bindings
|
||||
// determines whether to borrow *at the level of the deref pattern* rather than
|
||||
// borrowing the bound place (since that inner place is inside the temporary that
|
||||
// stores the result of calling `deref()`/`deref_mut()` so can't be captured).
|
||||
let mutable = mc.typeck_results.pat_has_ref_mut_binding(subpattern);
|
||||
let mutability = if mutable { hir::Mutability::Mut } else { hir::Mutability::Not };
|
||||
let bk = ty::BorrowKind::from_mutbl(mutability);
|
||||
delegate.borrow(place, discr_place.hir_id, bk);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
@ -711,13 +711,23 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
|
||||
self.cat_pattern_(place_with_id, subpat, op)?;
|
||||
}
|
||||
|
||||
PatKind::Box(subpat) | PatKind::Ref(subpat, _) | PatKind::Deref(subpat) => {
|
||||
PatKind::Box(subpat) | PatKind::Ref(subpat, _) => {
|
||||
// box p1, &p1, &mut p1. we can ignore the mutability of
|
||||
// PatKind::Ref since that information is already contained
|
||||
// in the type.
|
||||
let subplace = self.cat_deref(pat, place_with_id)?;
|
||||
self.cat_pattern_(subplace, subpat, op)?;
|
||||
}
|
||||
PatKind::Deref(subpat) => {
|
||||
let mutable = self.typeck_results.pat_has_ref_mut_binding(subpat);
|
||||
let mutability = if mutable { hir::Mutability::Mut } else { hir::Mutability::Not };
|
||||
let re_erased = self.tcx().lifetimes.re_erased;
|
||||
let ty = self.pat_ty_adjusted(subpat)?;
|
||||
let ty = Ty::new_ref(self.tcx(), re_erased, ty, mutability);
|
||||
// A deref pattern generates a temporary.
|
||||
let place = self.cat_rvalue(pat.hir_id, ty);
|
||||
self.cat_pattern_(place, subpat, op)?;
|
||||
}
|
||||
|
||||
PatKind::Slice(before, ref slice, after) => {
|
||||
let Some(element_ty) = place_with_id.place.ty().builtin_index() else {
|
||||
|
@ -985,7 +985,8 @@ impl<'tcx> Debug for Rvalue<'tcx> {
|
||||
Ref(region, borrow_kind, ref place) => {
|
||||
let kind_str = match borrow_kind {
|
||||
BorrowKind::Shared => "",
|
||||
BorrowKind::Fake => "fake ",
|
||||
BorrowKind::Fake(FakeBorrowKind::Deep) => "fake ",
|
||||
BorrowKind::Fake(FakeBorrowKind::Shallow) => "fake shallow ",
|
||||
BorrowKind::Mut { .. } => "mut ",
|
||||
};
|
||||
|
||||
|
@ -236,6 +236,11 @@ impl<'tcx> PlaceRef<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_place(&self, tcx: TyCtxt<'tcx>) -> Place<'tcx> {
|
||||
Place { local: self.local, projection: tcx.mk_place_elems(self.projection) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn last_projection(&self) -> Option<(PlaceRef<'tcx>, PlaceElem<'tcx>)> {
|
||||
if let &[ref proj_base @ .., elem] = self.projection {
|
||||
@ -446,7 +451,7 @@ impl<'tcx> Rvalue<'tcx> {
|
||||
impl BorrowKind {
|
||||
pub fn mutability(&self) -> Mutability {
|
||||
match *self {
|
||||
BorrowKind::Shared | BorrowKind::Fake => Mutability::Not,
|
||||
BorrowKind::Shared | BorrowKind::Fake(_) => Mutability::Not,
|
||||
BorrowKind::Mut { .. } => Mutability::Mut,
|
||||
}
|
||||
}
|
||||
@ -454,7 +459,7 @@ impl BorrowKind {
|
||||
pub fn allows_two_phase_borrow(&self) -> bool {
|
||||
match *self {
|
||||
BorrowKind::Shared
|
||||
| BorrowKind::Fake
|
||||
| BorrowKind::Fake(_)
|
||||
| BorrowKind::Mut { kind: MutBorrowKind::Default | MutBorrowKind::ClosureCapture } => {
|
||||
false
|
||||
}
|
||||
|
@ -165,13 +165,16 @@ pub enum BorrowKind {
|
||||
/// Data must be immutable and is aliasable.
|
||||
Shared,
|
||||
|
||||
/// The immediately borrowed place must be immutable, but projections from
|
||||
/// it don't need to be. For example, a shallow borrow of `a.b` doesn't
|
||||
/// conflict with a mutable borrow of `a.b.c`.
|
||||
/// An immutable, aliasable borrow that is discarded after borrow-checking. Can behave either
|
||||
/// like a normal shared borrow or like a special shallow borrow (see [`FakeBorrowKind`]).
|
||||
///
|
||||
/// This is used when lowering matches: when matching on a place we want to
|
||||
/// ensure that place have the same value from the start of the match until
|
||||
/// an arm is selected. This prevents this code from compiling:
|
||||
/// This is used when lowering index expressions and matches. This is used to prevent code like
|
||||
/// the following from compiling:
|
||||
/// ```compile_fail,E0510
|
||||
/// let mut x: &[_] = &[[0, 1]];
|
||||
/// let y: &[_] = &[];
|
||||
/// let _ = x[0][{x = y; 1}];
|
||||
/// ```
|
||||
/// ```compile_fail,E0510
|
||||
/// let mut x = &Some(0);
|
||||
/// match *x {
|
||||
@ -180,11 +183,8 @@ pub enum BorrowKind {
|
||||
/// Some(_) => (),
|
||||
/// }
|
||||
/// ```
|
||||
/// This can't be a shared borrow because mutably borrowing (*x as Some).0
|
||||
/// should not prevent `if let None = x { ... }`, for example, because the
|
||||
/// mutating `(*x as Some).0` can't affect the discriminant of `x`.
|
||||
/// We can also report errors with this kind of borrow differently.
|
||||
Fake,
|
||||
Fake(FakeBorrowKind),
|
||||
|
||||
/// Data is mutable and not aliasable.
|
||||
Mut { kind: MutBorrowKind },
|
||||
@ -240,6 +240,57 @@ pub enum MutBorrowKind {
|
||||
ClosureCapture,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, TyEncodable, TyDecodable)]
|
||||
#[derive(Hash, HashStable)]
|
||||
pub enum FakeBorrowKind {
|
||||
/// A shared shallow borrow. The immediately borrowed place must be immutable, but projections
|
||||
/// from it don't need to be. For example, a shallow borrow of `a.b` doesn't conflict with a
|
||||
/// mutable borrow of `a.b.c`.
|
||||
///
|
||||
/// This is used when lowering matches: when matching on a place we want to ensure that place
|
||||
/// have the same value from the start of the match until an arm is selected. This prevents this
|
||||
/// code from compiling:
|
||||
/// ```compile_fail,E0510
|
||||
/// let mut x = &Some(0);
|
||||
/// match *x {
|
||||
/// None => (),
|
||||
/// Some(_) if { x = &None; false } => (),
|
||||
/// Some(_) => (),
|
||||
/// }
|
||||
/// ```
|
||||
/// This can't be a shared borrow because mutably borrowing `(*x as Some).0` should not checking
|
||||
/// the discriminant or accessing other variants, because the mutating `(*x as Some).0` can't
|
||||
/// affect the discriminant of `x`. E.g. the following is allowed:
|
||||
/// ```rust
|
||||
/// let mut x = Some(0);
|
||||
/// match x {
|
||||
/// Some(_)
|
||||
/// if {
|
||||
/// if let Some(ref mut y) = x {
|
||||
/// *y += 1;
|
||||
/// };
|
||||
/// true
|
||||
/// } => {}
|
||||
/// _ => {}
|
||||
/// }
|
||||
/// ```
|
||||
Shallow,
|
||||
/// A shared (deep) borrow. Data must be immutable and is aliasable.
|
||||
///
|
||||
/// This is used when lowering deref patterns, where shallow borrows wouldn't prevent something
|
||||
/// like:
|
||||
// ```compile_fail
|
||||
// let mut b = Box::new(false);
|
||||
// match b {
|
||||
// deref!(true) => {} // not reached because `*b == false`
|
||||
// _ if { *b = true; false } => {} // not reached because the guard is `false`
|
||||
// deref!(false) => {} // not reached because the guard changed it
|
||||
// // UB because we reached the unreachable.
|
||||
// }
|
||||
// ```
|
||||
Deep,
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Statements
|
||||
|
||||
|
@ -294,7 +294,7 @@ impl BorrowKind {
|
||||
|
||||
// We have no type corresponding to a shallow borrow, so use
|
||||
// `&` as an approximation.
|
||||
BorrowKind::Fake => hir::Mutability::Not,
|
||||
BorrowKind::Fake(_) => hir::Mutability::Not,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -655,7 +655,7 @@ macro_rules! make_mir_visitor {
|
||||
BorrowKind::Shared => PlaceContext::NonMutatingUse(
|
||||
NonMutatingUseContext::SharedBorrow
|
||||
),
|
||||
BorrowKind::Fake => PlaceContext::NonMutatingUse(
|
||||
BorrowKind::Fake(_) => PlaceContext::NonMutatingUse(
|
||||
NonMutatingUseContext::FakeBorrow
|
||||
),
|
||||
BorrowKind::Mut { .. } =>
|
||||
@ -1284,6 +1284,8 @@ pub enum NonMutatingUseContext {
|
||||
/// Shared borrow.
|
||||
SharedBorrow,
|
||||
/// A fake borrow.
|
||||
/// FIXME: do we need to distinguish shallow and deep fake borrows? In fact, do we need to
|
||||
/// distinguish fake and normal deep borrows?
|
||||
FakeBorrow,
|
||||
/// AddressOf for *const pointer.
|
||||
AddressOf,
|
||||
|
@ -642,7 +642,7 @@ impl<'tcx> Pat<'tcx> {
|
||||
AscribeUserType { subpattern, .. }
|
||||
| Binding { subpattern: Some(subpattern), .. }
|
||||
| Deref { subpattern }
|
||||
| DerefPattern { subpattern }
|
||||
| DerefPattern { subpattern, .. }
|
||||
| InlineConstant { subpattern, .. } => subpattern.walk_(it),
|
||||
Leaf { subpatterns } | Variant { subpatterns, .. } => {
|
||||
subpatterns.iter().for_each(|field| field.pattern.walk_(it))
|
||||
@ -760,6 +760,7 @@ pub enum PatKind<'tcx> {
|
||||
/// Deref pattern, written `box P` for now.
|
||||
DerefPattern {
|
||||
subpattern: Box<Pat<'tcx>>,
|
||||
mutability: hir::Mutability,
|
||||
},
|
||||
|
||||
/// One of the following:
|
||||
@ -1166,7 +1167,7 @@ impl<'tcx> fmt::Display for Pat<'tcx> {
|
||||
}
|
||||
write!(f, "{subpattern}")
|
||||
}
|
||||
PatKind::DerefPattern { ref subpattern } => {
|
||||
PatKind::DerefPattern { ref subpattern, .. } => {
|
||||
write!(f, "deref!({subpattern})")
|
||||
}
|
||||
PatKind::Constant { value } => write!(f, "{value}"),
|
||||
|
@ -229,7 +229,7 @@ pub fn walk_pat<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>(
|
||||
match &pat.kind {
|
||||
AscribeUserType { subpattern, ascription: _ }
|
||||
| Deref { subpattern }
|
||||
| DerefPattern { subpattern }
|
||||
| DerefPattern { subpattern, .. }
|
||||
| Binding { subpattern: Some(subpattern), .. } => visitor.visit_pat(subpattern),
|
||||
Binding { .. } | Wild | Never | Error(_) => {}
|
||||
Variant { subpatterns, adt_def: _, args: _, variant_index: _ } | Leaf { subpatterns } => {
|
||||
|
@ -451,7 +451,7 @@ impl<'tcx> TypeckResults<'tcx> {
|
||||
/// This is computed from the typeck results since we want to make
|
||||
/// sure to apply any match-ergonomics adjustments, which we cannot
|
||||
/// determine from the HIR alone.
|
||||
pub fn pat_has_ref_mut_binding(&self, pat: &'tcx hir::Pat<'tcx>) -> bool {
|
||||
pub fn pat_has_ref_mut_binding(&self, pat: &hir::Pat<'_>) -> bool {
|
||||
let mut has_ref_mut = false;
|
||||
pat.walk(|pat| {
|
||||
if let hir::PatKind::Binding(_, id, _, _) = pat.kind
|
||||
|
@ -685,7 +685,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
fake_borrow_temp.into(),
|
||||
Rvalue::Ref(
|
||||
tcx.lifetimes.re_erased,
|
||||
BorrowKind::Fake,
|
||||
BorrowKind::Fake(FakeBorrowKind::Shallow),
|
||||
Place { local: base_place.local, projection },
|
||||
),
|
||||
);
|
||||
|
@ -10,10 +10,7 @@ use crate::build::scope::DropKind;
|
||||
use crate::build::ForGuard::{self, OutsideGuard, RefWithinGuard};
|
||||
use crate::build::{BlockAnd, BlockAndExtension, Builder};
|
||||
use crate::build::{GuardFrame, GuardFrameLocal, LocalsForNode};
|
||||
use rustc_data_structures::{
|
||||
fx::{FxHashSet, FxIndexMap, FxIndexSet},
|
||||
stack::ensure_sufficient_stack,
|
||||
};
|
||||
use rustc_data_structures::{fx::FxIndexMap, stack::ensure_sufficient_stack};
|
||||
use rustc_hir::{BindingMode, ByRef};
|
||||
use rustc_middle::middle::region;
|
||||
use rustc_middle::mir::{self, *};
|
||||
@ -211,7 +208,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
/// 2. Create the decision tree ([Builder::lower_match_tree]).
|
||||
/// 3. Determine the fake borrows that are needed from the places that were
|
||||
/// matched against and create the required temporaries for them
|
||||
/// ([Builder::calculate_fake_borrows]).
|
||||
/// ([util::collect_fake_borrows]).
|
||||
/// 4. Create everything else: the guards and the arms ([Builder::lower_match_arms]).
|
||||
///
|
||||
/// ## False edges
|
||||
@ -380,12 +377,19 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
match_start_span: Span,
|
||||
match_has_guard: bool,
|
||||
candidates: &mut [&mut Candidate<'pat, 'tcx>],
|
||||
) -> Vec<(Place<'tcx>, Local)> {
|
||||
// The set of places that we are creating fake borrows of. If there are
|
||||
// no match guards then we don't need any fake borrows, so don't track
|
||||
// them.
|
||||
let fake_borrows = match_has_guard
|
||||
.then(|| util::FakeBorrowCollector::collect_fake_borrows(self, candidates));
|
||||
) -> Vec<(Place<'tcx>, Local, FakeBorrowKind)> {
|
||||
// The set of places that we are creating fake borrows of. If there are no match guards then
|
||||
// we don't need any fake borrows, so don't track them.
|
||||
let fake_borrows: Vec<(Place<'tcx>, Local, FakeBorrowKind)> = if match_has_guard {
|
||||
util::collect_fake_borrows(
|
||||
self,
|
||||
candidates,
|
||||
scrutinee_span,
|
||||
scrutinee_place_builder.base(),
|
||||
)
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
|
||||
// See the doc comment on `match_candidates` for why we have an
|
||||
// otherwise block. Match checking will ensure this is actually
|
||||
@ -439,11 +443,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
});
|
||||
}
|
||||
|
||||
if let Some(ref borrows) = fake_borrows {
|
||||
self.calculate_fake_borrows(borrows, scrutinee_span)
|
||||
} else {
|
||||
Vec::new()
|
||||
}
|
||||
fake_borrows
|
||||
}
|
||||
|
||||
/// Lower the bindings, guards and arm bodies of a `match` expression.
|
||||
@ -459,7 +459,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
scrutinee_span: Span,
|
||||
arm_candidates: Vec<(&'_ Arm<'tcx>, Candidate<'_, 'tcx>)>,
|
||||
outer_source_info: SourceInfo,
|
||||
fake_borrow_temps: Vec<(Place<'tcx>, Local)>,
|
||||
fake_borrow_temps: Vec<(Place<'tcx>, Local, FakeBorrowKind)>,
|
||||
) -> BlockAnd<()> {
|
||||
let arm_end_blocks: Vec<_> = arm_candidates
|
||||
.into_iter()
|
||||
@ -543,7 +543,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
&mut self,
|
||||
outer_source_info: SourceInfo,
|
||||
candidate: Candidate<'_, 'tcx>,
|
||||
fake_borrow_temps: &[(Place<'tcx>, Local)],
|
||||
fake_borrow_temps: &[(Place<'tcx>, Local, FakeBorrowKind)],
|
||||
scrutinee_span: Span,
|
||||
arm_match_scope: Option<(&Arm<'tcx>, region::Scope)>,
|
||||
storages_alive: bool,
|
||||
@ -940,7 +940,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
self.visit_primary_bindings(subpattern, pattern_user_ty.deref(), f);
|
||||
}
|
||||
|
||||
PatKind::DerefPattern { ref subpattern } => {
|
||||
PatKind::DerefPattern { ref subpattern, .. } => {
|
||||
self.visit_primary_bindings(subpattern, UserTypeProjections::none(), f);
|
||||
}
|
||||
|
||||
@ -1165,6 +1165,7 @@ enum TestCase<'pat, 'tcx> {
|
||||
Constant { value: mir::Const<'tcx> },
|
||||
Range(&'pat PatRange<'tcx>),
|
||||
Slice { len: usize, variable_length: bool },
|
||||
Deref { temp: Place<'tcx>, mutability: Mutability },
|
||||
Or { pats: Box<[FlatPat<'pat, 'tcx>]> },
|
||||
}
|
||||
|
||||
@ -1224,6 +1225,13 @@ enum TestKind<'tcx> {
|
||||
|
||||
/// Test that the length of the slice is equal to `len`.
|
||||
Len { len: u64, op: BinOp },
|
||||
|
||||
/// Call `Deref::deref[_mut]` on the value.
|
||||
Deref {
|
||||
/// Temporary to store the result of `deref()`/`deref_mut()`.
|
||||
temp: Place<'tcx>,
|
||||
mutability: Mutability,
|
||||
},
|
||||
}
|
||||
|
||||
/// A test to perform to determine which [`Candidate`] matches a value.
|
||||
@ -1905,81 +1913,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
target_blocks,
|
||||
);
|
||||
}
|
||||
|
||||
/// Determine the fake borrows that are needed from a set of places that
|
||||
/// have to be stable across match guards.
|
||||
///
|
||||
/// Returns a list of places that need a fake borrow and the temporary
|
||||
/// that's used to store the fake borrow.
|
||||
///
|
||||
/// Match exhaustiveness checking is not able to handle the case where the
|
||||
/// place being matched on is mutated in the guards. We add "fake borrows"
|
||||
/// to the guards that prevent any mutation of the place being matched.
|
||||
/// There are a some subtleties:
|
||||
///
|
||||
/// 1. Borrowing `*x` doesn't prevent assigning to `x`. If `x` is a shared
|
||||
/// reference, the borrow isn't even tracked. As such we have to add fake
|
||||
/// borrows of any prefixes of a place
|
||||
/// 2. We don't want `match x { _ => (), }` to conflict with mutable
|
||||
/// borrows of `x`, so we only add fake borrows for places which are
|
||||
/// bound or tested by the match.
|
||||
/// 3. We don't want the fake borrows to conflict with `ref mut` bindings,
|
||||
/// so we use a special BorrowKind for them.
|
||||
/// 4. The fake borrows may be of places in inactive variants, so it would
|
||||
/// be UB to generate code for them. They therefore have to be removed
|
||||
/// by a MIR pass run after borrow checking.
|
||||
fn calculate_fake_borrows<'b>(
|
||||
&mut self,
|
||||
fake_borrows: &'b FxIndexSet<Place<'tcx>>,
|
||||
temp_span: Span,
|
||||
) -> Vec<(Place<'tcx>, Local)> {
|
||||
let tcx = self.tcx;
|
||||
|
||||
debug!("add_fake_borrows fake_borrows = {:?}", fake_borrows);
|
||||
|
||||
let mut all_fake_borrows = Vec::with_capacity(fake_borrows.len());
|
||||
|
||||
// Insert a Shallow borrow of the prefixes of any fake borrows.
|
||||
for place in fake_borrows {
|
||||
let mut cursor = place.projection.as_ref();
|
||||
while let [proj_base @ .., elem] = cursor {
|
||||
cursor = proj_base;
|
||||
|
||||
if let ProjectionElem::Deref = elem {
|
||||
// Insert a shallow borrow after a deref. For other
|
||||
// projections the borrow of prefix_cursor will
|
||||
// conflict with any mutation of base.
|
||||
all_fake_borrows.push(PlaceRef { local: place.local, projection: proj_base });
|
||||
}
|
||||
}
|
||||
|
||||
all_fake_borrows.push(place.as_ref());
|
||||
}
|
||||
|
||||
// Deduplicate
|
||||
let mut dedup = FxHashSet::default();
|
||||
all_fake_borrows.retain(|b| dedup.insert(*b));
|
||||
|
||||
debug!("add_fake_borrows all_fake_borrows = {:?}", all_fake_borrows);
|
||||
|
||||
all_fake_borrows
|
||||
.into_iter()
|
||||
.map(|matched_place_ref| {
|
||||
let matched_place = Place {
|
||||
local: matched_place_ref.local,
|
||||
projection: tcx.mk_place_elems(matched_place_ref.projection),
|
||||
};
|
||||
let fake_borrow_deref_ty = matched_place.ty(&self.local_decls, tcx).ty;
|
||||
let fake_borrow_ty =
|
||||
Ty::new_imm_ref(tcx, tcx.lifetimes.re_erased, fake_borrow_deref_ty);
|
||||
let mut fake_borrow_temp = LocalDecl::new(fake_borrow_ty, temp_span);
|
||||
fake_borrow_temp.local_info = ClearCrossCrate::Set(Box::new(LocalInfo::FakeBorrow));
|
||||
let fake_borrow_temp = self.local_decls.push(fake_borrow_temp);
|
||||
|
||||
(matched_place, fake_borrow_temp)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
@ -2044,7 +1977,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
&mut self,
|
||||
candidate: Candidate<'pat, 'tcx>,
|
||||
parent_data: &[PatternExtraData<'tcx>],
|
||||
fake_borrows: &[(Place<'tcx>, Local)],
|
||||
fake_borrows: &[(Place<'tcx>, Local, FakeBorrowKind)],
|
||||
scrutinee_span: Span,
|
||||
arm_match_scope: Option<(&Arm<'tcx>, region::Scope)>,
|
||||
schedule_drops: bool,
|
||||
@ -2174,8 +2107,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
|
||||
let re_erased = tcx.lifetimes.re_erased;
|
||||
let scrutinee_source_info = self.source_info(scrutinee_span);
|
||||
for &(place, temp) in fake_borrows {
|
||||
let borrow = Rvalue::Ref(re_erased, BorrowKind::Fake, place);
|
||||
for &(place, temp, kind) in fake_borrows {
|
||||
let borrow = Rvalue::Ref(re_erased, BorrowKind::Fake(kind), place);
|
||||
self.cfg.push_assign(block, scrutinee_source_info, Place::from(temp), borrow);
|
||||
}
|
||||
|
||||
@ -2198,7 +2131,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
let guard_frame = self.guard_context.pop().unwrap();
|
||||
debug!("Exiting guard building context with locals: {:?}", guard_frame);
|
||||
|
||||
for &(_, temp) in fake_borrows {
|
||||
for &(_, temp, _) in fake_borrows {
|
||||
let cause = FakeReadCause::ForMatchGuard;
|
||||
self.cfg.push_fake_read(post_guard_block, guard_end, cause, Place::from(temp));
|
||||
}
|
||||
|
@ -42,6 +42,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
TestKind::Len { len: len as u64, op }
|
||||
}
|
||||
|
||||
TestCase::Deref { temp, mutability } => TestKind::Deref { temp, mutability },
|
||||
|
||||
TestCase::Or { .. } => bug!("or-patterns should have already been handled"),
|
||||
|
||||
TestCase::Irrefutable { .. } => span_bug!(
|
||||
@ -143,34 +145,18 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
);
|
||||
}
|
||||
let re_erased = tcx.lifetimes.re_erased;
|
||||
let ref_string = self.temp(Ty::new_imm_ref(tcx, re_erased, ty), test.span);
|
||||
let ref_str_ty = Ty::new_imm_ref(tcx, re_erased, tcx.types.str_);
|
||||
let ref_str = self.temp(ref_str_ty, test.span);
|
||||
let deref = tcx.require_lang_item(LangItem::Deref, None);
|
||||
let method = trait_method(tcx, deref, sym::deref, [ty]);
|
||||
let eq_block = self.cfg.start_new_block();
|
||||
self.cfg.push_assign(
|
||||
// `let ref_str: &str = <String as Deref>::deref(&place);`
|
||||
self.call_deref(
|
||||
block,
|
||||
source_info,
|
||||
ref_string,
|
||||
Rvalue::Ref(re_erased, BorrowKind::Shared, place),
|
||||
);
|
||||
self.cfg.terminate(
|
||||
block,
|
||||
source_info,
|
||||
TerminatorKind::Call {
|
||||
func: Operand::Constant(Box::new(ConstOperand {
|
||||
span: test.span,
|
||||
user_ty: None,
|
||||
const_: method,
|
||||
})),
|
||||
args: vec![Spanned { node: Operand::Move(ref_string), span: DUMMY_SP }],
|
||||
destination: ref_str,
|
||||
target: Some(eq_block),
|
||||
unwind: UnwindAction::Continue,
|
||||
call_source: CallSource::Misc,
|
||||
fn_span: source_info.span,
|
||||
},
|
||||
eq_block,
|
||||
place,
|
||||
Mutability::Not,
|
||||
ty,
|
||||
ref_str,
|
||||
test.span,
|
||||
);
|
||||
self.non_scalar_compare(
|
||||
eq_block,
|
||||
@ -270,9 +256,66 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
Operand::Move(expected),
|
||||
);
|
||||
}
|
||||
|
||||
TestKind::Deref { temp, mutability } => {
|
||||
let ty = place_ty.ty;
|
||||
let target = target_block(TestBranch::Success);
|
||||
self.call_deref(block, target, place, mutability, ty, temp, test.span);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Perform `let temp = <ty as Deref>::deref(&place)`.
|
||||
/// or `let temp = <ty as DerefMut>::deref_mut(&mut place)`.
|
||||
pub(super) fn call_deref(
|
||||
&mut self,
|
||||
block: BasicBlock,
|
||||
target_block: BasicBlock,
|
||||
place: Place<'tcx>,
|
||||
mutability: Mutability,
|
||||
ty: Ty<'tcx>,
|
||||
temp: Place<'tcx>,
|
||||
span: Span,
|
||||
) {
|
||||
let (trait_item, method) = match mutability {
|
||||
Mutability::Not => (LangItem::Deref, sym::deref),
|
||||
Mutability::Mut => (LangItem::DerefMut, sym::deref_mut),
|
||||
};
|
||||
let borrow_kind = super::util::ref_pat_borrow_kind(mutability);
|
||||
let source_info = self.source_info(span);
|
||||
let re_erased = self.tcx.lifetimes.re_erased;
|
||||
let trait_item = self.tcx.require_lang_item(trait_item, None);
|
||||
let method = trait_method(self.tcx, trait_item, method, [ty]);
|
||||
let ref_src = self.temp(Ty::new_ref(self.tcx, re_erased, ty, mutability), span);
|
||||
// `let ref_src = &src_place;`
|
||||
// or `let ref_src = &mut src_place;`
|
||||
self.cfg.push_assign(
|
||||
block,
|
||||
source_info,
|
||||
ref_src,
|
||||
Rvalue::Ref(re_erased, borrow_kind, place),
|
||||
);
|
||||
// `let temp = <Ty as Deref>::deref(ref_src);`
|
||||
// or `let temp = <Ty as DerefMut>::deref_mut(ref_src);`
|
||||
self.cfg.terminate(
|
||||
block,
|
||||
source_info,
|
||||
TerminatorKind::Call {
|
||||
func: Operand::Constant(Box::new(ConstOperand {
|
||||
span,
|
||||
user_ty: None,
|
||||
const_: method,
|
||||
})),
|
||||
args: vec![Spanned { node: Operand::Move(ref_src), span }],
|
||||
destination: temp,
|
||||
target: Some(target_block),
|
||||
unwind: UnwindAction::Continue,
|
||||
call_source: CallSource::Misc,
|
||||
fn_span: source_info.span,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// Compare using the provided built-in comparison operator
|
||||
fn compare(
|
||||
&mut self,
|
||||
@ -660,13 +703,21 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
(TestKind::Deref { temp: test_temp, .. }, TestCase::Deref { temp, .. })
|
||||
if test_temp == temp =>
|
||||
{
|
||||
fully_matched = true;
|
||||
Some(TestBranch::Success)
|
||||
}
|
||||
|
||||
(
|
||||
TestKind::Switch { .. }
|
||||
| TestKind::SwitchInt { .. }
|
||||
| TestKind::If
|
||||
| TestKind::Len { .. }
|
||||
| TestKind::Range { .. }
|
||||
| TestKind::Eq { .. },
|
||||
| TestKind::Eq { .. }
|
||||
| TestKind::Deref { .. },
|
||||
_,
|
||||
) => {
|
||||
fully_matched = false;
|
||||
|
@ -1,12 +1,13 @@
|
||||
use crate::build::expr::as_place::{PlaceBase, PlaceBuilder};
|
||||
use crate::build::matches::{Binding, Candidate, FlatPat, MatchPair, TestCase};
|
||||
use crate::build::Builder;
|
||||
use rustc_data_structures::fx::FxIndexSet;
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_infer::infer::type_variable::TypeVariableOrigin;
|
||||
use rustc_middle::mir::*;
|
||||
use rustc_middle::thir::{self, *};
|
||||
use rustc_middle::ty;
|
||||
use rustc_middle::ty::TypeVisitableExt;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_span::Span;
|
||||
|
||||
impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
pub(crate) fn field_match_pairs<'pat>(
|
||||
@ -249,10 +250,15 @@ impl<'pat, 'tcx> MatchPair<'pat, 'tcx> {
|
||||
default_irrefutable()
|
||||
}
|
||||
|
||||
PatKind::DerefPattern { .. } => {
|
||||
// FIXME(deref_patterns)
|
||||
// Treat it like a wildcard for now.
|
||||
default_irrefutable()
|
||||
PatKind::DerefPattern { ref subpattern, mutability } => {
|
||||
// Create a new temporary for each deref pattern.
|
||||
// FIXME(deref_patterns): dedup temporaries to avoid multiple `deref()` calls?
|
||||
let temp = cx.temp(
|
||||
Ty::new_ref(cx.tcx, cx.tcx.lifetimes.re_erased, subpattern.ty, mutability),
|
||||
pattern.span,
|
||||
);
|
||||
subpairs.push(MatchPair::new(PlaceBuilder::from(temp).deref(), subpattern, cx));
|
||||
TestCase::Deref { temp, mutability }
|
||||
}
|
||||
};
|
||||
|
||||
@ -262,19 +268,103 @@ impl<'pat, 'tcx> MatchPair<'pat, 'tcx> {
|
||||
|
||||
pub(super) struct FakeBorrowCollector<'a, 'b, 'tcx> {
|
||||
cx: &'a mut Builder<'b, 'tcx>,
|
||||
fake_borrows: FxIndexSet<Place<'tcx>>,
|
||||
/// Base of the scrutinee place. Used to distinguish bindings inside the scrutinee place from
|
||||
/// bindings inside deref patterns.
|
||||
scrutinee_base: PlaceBase,
|
||||
/// Store for each place the kind of borrow to take. In case of conflicts, we take the strongest
|
||||
/// borrow (i.e. Deep > Shallow).
|
||||
/// Invariant: for any place in `fake_borrows`, all the prefixes of this place that are
|
||||
/// dereferences are also borrowed with the same of stronger borrow kind.
|
||||
fake_borrows: FxIndexMap<Place<'tcx>, FakeBorrowKind>,
|
||||
}
|
||||
|
||||
/// Determine the set of places that have to be stable across match guards.
|
||||
///
|
||||
/// Returns a list of places that need a fake borrow along with a local to store it.
|
||||
///
|
||||
/// Match exhaustiveness checking is not able to handle the case where the place being matched on is
|
||||
/// mutated in the guards. We add "fake borrows" to the guards that prevent any mutation of the
|
||||
/// place being matched. There are a some subtleties:
|
||||
///
|
||||
/// 1. Borrowing `*x` doesn't prevent assigning to `x`. If `x` is a shared reference, the borrow
|
||||
/// isn't even tracked. As such we have to add fake borrows of any prefixes of a place.
|
||||
/// 2. We don't want `match x { (Some(_), _) => (), .. }` to conflict with mutable borrows of `x.1`, so we
|
||||
/// only add fake borrows for places which are bound or tested by the match.
|
||||
/// 3. We don't want `match x { Some(_) => (), .. }` to conflict with mutable borrows of `(x as
|
||||
/// Some).0`, so the borrows are a special shallow borrow that only affects the place and not its
|
||||
/// projections.
|
||||
/// ```rust
|
||||
/// let mut x = (Some(0), true);
|
||||
/// match x {
|
||||
/// (Some(_), false) => {}
|
||||
/// _ if { if let Some(ref mut y) = x.0 { *y += 1 }; true } => {}
|
||||
/// _ => {}
|
||||
/// }
|
||||
/// ```
|
||||
/// 4. The fake borrows may be of places in inactive variants, e.g. here we need to fake borrow `x`
|
||||
/// and `(x as Some).0`, but when we reach the guard `x` may not be `Some`.
|
||||
/// ```rust
|
||||
/// let mut x = (Some(Some(0)), true);
|
||||
/// match x {
|
||||
/// (Some(Some(_)), false) => {}
|
||||
/// _ if { if let Some(Some(ref mut y)) = x.0 { *y += 1 }; true } => {}
|
||||
/// _ => {}
|
||||
/// }
|
||||
/// ```
|
||||
/// So it would be UB to generate code for the fake borrows. They therefore have to be removed by
|
||||
/// a MIR pass run after borrow checking.
|
||||
pub(super) fn collect_fake_borrows<'tcx>(
|
||||
cx: &mut Builder<'_, 'tcx>,
|
||||
candidates: &[&mut Candidate<'_, 'tcx>],
|
||||
temp_span: Span,
|
||||
scrutinee_base: PlaceBase,
|
||||
) -> Vec<(Place<'tcx>, Local, FakeBorrowKind)> {
|
||||
let mut collector =
|
||||
FakeBorrowCollector { cx, scrutinee_base, fake_borrows: FxIndexMap::default() };
|
||||
for candidate in candidates.iter() {
|
||||
collector.visit_candidate(candidate);
|
||||
}
|
||||
let fake_borrows = collector.fake_borrows;
|
||||
debug!("add_fake_borrows fake_borrows = {:?}", fake_borrows);
|
||||
let tcx = cx.tcx;
|
||||
fake_borrows
|
||||
.iter()
|
||||
.map(|(matched_place, borrow_kind)| {
|
||||
let fake_borrow_deref_ty = matched_place.ty(&cx.local_decls, tcx).ty;
|
||||
let fake_borrow_ty =
|
||||
Ty::new_imm_ref(tcx, tcx.lifetimes.re_erased, fake_borrow_deref_ty);
|
||||
let mut fake_borrow_temp = LocalDecl::new(fake_borrow_ty, temp_span);
|
||||
fake_borrow_temp.local_info = ClearCrossCrate::Set(Box::new(LocalInfo::FakeBorrow));
|
||||
let fake_borrow_temp = cx.local_decls.push(fake_borrow_temp);
|
||||
(*matched_place, fake_borrow_temp, *borrow_kind)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
impl<'a, 'b, 'tcx> FakeBorrowCollector<'a, 'b, 'tcx> {
|
||||
pub(super) fn collect_fake_borrows(
|
||||
cx: &'a mut Builder<'b, 'tcx>,
|
||||
candidates: &[&mut Candidate<'_, 'tcx>],
|
||||
) -> FxIndexSet<Place<'tcx>> {
|
||||
let mut collector = Self { cx, fake_borrows: FxIndexSet::default() };
|
||||
for candidate in candidates.iter() {
|
||||
collector.visit_candidate(candidate);
|
||||
// Fake borrow this place and its dereference prefixes.
|
||||
fn fake_borrow(&mut self, place: Place<'tcx>, kind: FakeBorrowKind) {
|
||||
if self.fake_borrows.get(&place).is_some_and(|k| *k >= kind) {
|
||||
return;
|
||||
}
|
||||
self.fake_borrows.insert(place, kind);
|
||||
// Also fake borrow the prefixes of any fake borrow.
|
||||
self.fake_borrow_deref_prefixes(place, kind);
|
||||
}
|
||||
|
||||
// Fake borrow the prefixes of this place that are dereferences.
|
||||
fn fake_borrow_deref_prefixes(&mut self, place: Place<'tcx>, kind: FakeBorrowKind) {
|
||||
for (place_ref, elem) in place.as_ref().iter_projections().rev() {
|
||||
if let ProjectionElem::Deref = elem {
|
||||
// Insert a shallow borrow after a deref. For other projections the borrow of
|
||||
// `place_ref` will conflict with any mutation of `place.base`.
|
||||
let place = place_ref.to_place(self.cx.tcx);
|
||||
if self.fake_borrows.get(&place).is_some_and(|k| *k >= kind) {
|
||||
return;
|
||||
}
|
||||
self.fake_borrows.insert(place, kind);
|
||||
}
|
||||
}
|
||||
collector.fake_borrows
|
||||
}
|
||||
|
||||
fn visit_candidate(&mut self, candidate: &Candidate<'_, 'tcx>) {
|
||||
@ -300,10 +390,27 @@ impl<'a, 'b, 'tcx> FakeBorrowCollector<'a, 'b, 'tcx> {
|
||||
for flat_pat in pats.iter() {
|
||||
self.visit_flat_pat(flat_pat)
|
||||
}
|
||||
} else if matches!(match_pair.test_case, TestCase::Deref { .. }) {
|
||||
// The subpairs of a deref pattern are all places relative to the deref temporary, so we
|
||||
// don't fake borrow them. Problem is, if we only shallowly fake-borrowed
|
||||
// `match_pair.place`, this would allow:
|
||||
// ```
|
||||
// let mut b = Box::new(false);
|
||||
// match b {
|
||||
// deref!(true) => {} // not reached because `*b == false`
|
||||
// _ if { *b = true; false } => {} // not reached because the guard is `false`
|
||||
// deref!(false) => {} // not reached because the guard changed it
|
||||
// // UB because we reached the unreachable.
|
||||
// }
|
||||
// ```
|
||||
// Hence we fake borrow using a deep borrow.
|
||||
if let Some(place) = match_pair.place {
|
||||
self.fake_borrow(place, FakeBorrowKind::Deep);
|
||||
}
|
||||
} else {
|
||||
// Insert a Shallow borrow of any place that is switched on.
|
||||
if let Some(place) = match_pair.place {
|
||||
self.fake_borrows.insert(place);
|
||||
self.fake_borrow(place, FakeBorrowKind::Shallow);
|
||||
}
|
||||
|
||||
for subpair in &match_pair.subpairs {
|
||||
@ -313,6 +420,14 @@ impl<'a, 'b, 'tcx> FakeBorrowCollector<'a, 'b, 'tcx> {
|
||||
}
|
||||
|
||||
fn visit_binding(&mut self, Binding { source, .. }: &Binding<'tcx>) {
|
||||
if let PlaceBase::Local(l) = self.scrutinee_base
|
||||
&& l != source.local
|
||||
{
|
||||
// The base of this place is a temporary created for deref patterns. We don't emit fake
|
||||
// borrows for these as they are not initialized in all branches.
|
||||
return;
|
||||
}
|
||||
|
||||
// Insert a borrows of prefixes of places that are bound and are
|
||||
// behind a dereference projection.
|
||||
//
|
||||
@ -329,13 +444,13 @@ impl<'a, 'b, 'tcx> FakeBorrowCollector<'a, 'b, 'tcx> {
|
||||
// y if { y == 1 && (x = &2) == () } => y,
|
||||
// _ => 3,
|
||||
// }
|
||||
if let Some(i) = source.projection.iter().rposition(|elem| elem == ProjectionElem::Deref) {
|
||||
let proj_base = &source.projection[..i];
|
||||
self.fake_borrows.insert(Place {
|
||||
local: source.local,
|
||||
projection: self.cx.tcx.mk_place_elems(proj_base),
|
||||
});
|
||||
}
|
||||
//
|
||||
// We don't just fake borrow the whole place because this is allowed:
|
||||
// match u {
|
||||
// _ if { u = true; false } => (),
|
||||
// x => (),
|
||||
// }
|
||||
self.fake_borrow_deref_prefixes(*source, FakeBorrowKind::Shallow);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -513,7 +513,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
|
||||
visit::walk_expr(&mut visitor, expr);
|
||||
if visitor.found {
|
||||
match borrow_kind {
|
||||
BorrowKind::Fake | BorrowKind::Shared
|
||||
BorrowKind::Fake(_) | BorrowKind::Shared
|
||||
if !self.thir[arg].ty.is_freeze(self.tcx, self.param_env) =>
|
||||
{
|
||||
self.requires_unsafe(expr.span, BorrowOfLayoutConstrainedField)
|
||||
@ -521,7 +521,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
|
||||
BorrowKind::Mut { .. } => {
|
||||
self.requires_unsafe(expr.span, MutationOfLayoutConstrainedField)
|
||||
}
|
||||
BorrowKind::Fake | BorrowKind::Shared => {}
|
||||
BorrowKind::Fake(_) | BorrowKind::Shared => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -264,7 +264,9 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
|
||||
}
|
||||
|
||||
hir::PatKind::Deref(subpattern) => {
|
||||
PatKind::DerefPattern { subpattern: self.lower_pattern(subpattern) }
|
||||
let mutable = self.typeck_results.pat_has_ref_mut_binding(subpattern);
|
||||
let mutability = if mutable { hir::Mutability::Mut } else { hir::Mutability::Not };
|
||||
PatKind::DerefPattern { subpattern: self.lower_pattern(subpattern), mutability }
|
||||
}
|
||||
hir::PatKind::Ref(subpattern, _) | hir::PatKind::Box(subpattern) => {
|
||||
PatKind::Deref { subpattern: self.lower_pattern(subpattern) }
|
||||
|
@ -688,7 +688,7 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> {
|
||||
self.print_pat(subpattern, depth_lvl + 2);
|
||||
print_indented!(self, "}", depth_lvl + 1);
|
||||
}
|
||||
PatKind::DerefPattern { subpattern } => {
|
||||
PatKind::DerefPattern { subpattern, .. } => {
|
||||
print_indented!(self, "DerefPattern { ", depth_lvl + 1);
|
||||
print_indented!(self, "subpattern:", depth_lvl + 2);
|
||||
self.print_pat(subpattern, depth_lvl + 2);
|
||||
|
@ -102,7 +102,7 @@ where
|
||||
}
|
||||
|
||||
Rvalue::Cast(..)
|
||||
| Rvalue::Ref(_, BorrowKind::Fake, _)
|
||||
| Rvalue::Ref(_, BorrowKind::Fake(_), _)
|
||||
| Rvalue::ShallowInitBox(..)
|
||||
| Rvalue::Use(..)
|
||||
| Rvalue::ThreadLocalRef(..)
|
||||
|
@ -29,7 +29,7 @@ impl<'tcx> MirPass<'tcx> for CleanupPostBorrowck {
|
||||
for statement in basic_block.statements.iter_mut() {
|
||||
match statement.kind {
|
||||
StatementKind::AscribeUserType(..)
|
||||
| StatementKind::Assign(box (_, Rvalue::Ref(_, BorrowKind::Fake, _)))
|
||||
| StatementKind::Assign(box (_, Rvalue::Ref(_, BorrowKind::Fake(_), _)))
|
||||
| StatementKind::Coverage(
|
||||
// These kinds of coverage statements are markers inserted during
|
||||
// MIR building, and are not needed after InstrumentCoverage.
|
||||
|
@ -384,7 +384,7 @@ impl<'tcx> Validator<'_, 'tcx> {
|
||||
match kind {
|
||||
// Reject these borrow types just to be safe.
|
||||
// FIXME(RalfJung): could we allow them? Should we? No point in it until we have a usecase.
|
||||
BorrowKind::Fake | BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture } => {
|
||||
BorrowKind::Fake(_) | BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture } => {
|
||||
return Err(Unpromotable);
|
||||
}
|
||||
|
||||
|
@ -229,7 +229,7 @@ impl<'tcx> Stable<'tcx> for mir::BorrowKind {
|
||||
use rustc_middle::mir::BorrowKind::*;
|
||||
match *self {
|
||||
Shared => stable_mir::mir::BorrowKind::Shared,
|
||||
Fake => stable_mir::mir::BorrowKind::Fake,
|
||||
Fake(kind) => stable_mir::mir::BorrowKind::Fake(kind.stable(tables)),
|
||||
Mut { kind } => stable_mir::mir::BorrowKind::Mut { kind: kind.stable(tables) },
|
||||
}
|
||||
}
|
||||
@ -247,6 +247,17 @@ impl<'tcx> Stable<'tcx> for mir::MutBorrowKind {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Stable<'tcx> for mir::FakeBorrowKind {
|
||||
type T = stable_mir::mir::FakeBorrowKind;
|
||||
fn stable(&self, _: &mut Tables<'_>) -> Self::T {
|
||||
use rustc_middle::mir::FakeBorrowKind::*;
|
||||
match *self {
|
||||
Deep => stable_mir::mir::FakeBorrowKind::Deep,
|
||||
Shallow => stable_mir::mir::FakeBorrowKind::Shallow,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Stable<'tcx> for mir::NullOp<'tcx> {
|
||||
type T = stable_mir::mir::NullOp;
|
||||
fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
|
||||
|
@ -867,11 +867,9 @@ pub enum BorrowKind {
|
||||
/// Data must be immutable and is aliasable.
|
||||
Shared,
|
||||
|
||||
/// The immediately borrowed place must be immutable, but projections from
|
||||
/// it don't need to be. This is used to prevent match guards from replacing
|
||||
/// the scrutinee. For example, a fake borrow of `a.b` doesn't
|
||||
/// conflict with a mutable borrow of `a.b.c`.
|
||||
Fake,
|
||||
/// An immutable, aliasable borrow that is discarded after borrow-checking. Can behave either
|
||||
/// like a normal shared borrow or like a special shallow borrow (see [`FakeBorrowKind`]).
|
||||
Fake(FakeBorrowKind),
|
||||
|
||||
/// Data is mutable and not aliasable.
|
||||
Mut {
|
||||
@ -886,7 +884,7 @@ impl BorrowKind {
|
||||
BorrowKind::Mut { .. } => Mutability::Mut,
|
||||
BorrowKind::Shared => Mutability::Not,
|
||||
// FIXME: There's no type corresponding to a shallow borrow, so use `&` as an approximation.
|
||||
BorrowKind::Fake => Mutability::Not,
|
||||
BorrowKind::Fake(_) => Mutability::Not,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -898,6 +896,17 @@ pub enum MutBorrowKind {
|
||||
ClosureCapture,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum FakeBorrowKind {
|
||||
/// A shared (deep) borrow. Data must be immutable and is aliasable.
|
||||
Deep,
|
||||
/// The immediately borrowed place must be immutable, but projections from
|
||||
/// it don't need to be. This is used to prevent match guards from replacing
|
||||
/// the scrutinee. For example, a fake borrow of `a.b` doesn't
|
||||
/// conflict with a mutable borrow of `a.b.c`.
|
||||
Shallow,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum Mutability {
|
||||
Not,
|
||||
|
@ -8,7 +8,7 @@ use std::{fmt, io, iter};
|
||||
|
||||
use super::{AssertMessage, BinOp, TerminatorKind};
|
||||
|
||||
use super::BorrowKind;
|
||||
use super::{BorrowKind, FakeBorrowKind};
|
||||
|
||||
impl Display for Ty {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
@ -352,7 +352,8 @@ fn pretty_rvalue<W: Write>(writer: &mut W, rval: &Rvalue) -> io::Result<()> {
|
||||
Rvalue::Ref(_, borrowkind, place) => {
|
||||
let kind = match borrowkind {
|
||||
BorrowKind::Shared => "&",
|
||||
BorrowKind::Fake => "&fake ",
|
||||
BorrowKind::Fake(FakeBorrowKind::Deep) => "&fake ",
|
||||
BorrowKind::Fake(FakeBorrowKind::Shallow) => "&fake shallow ",
|
||||
BorrowKind::Mut { .. } => "&mut ",
|
||||
};
|
||||
write!(writer, "{kind}{:?}", place)
|
||||
|
@ -4,8 +4,8 @@ fn full_tested_match() -> () {
|
||||
let mut _0: ();
|
||||
let mut _1: (i32, i32);
|
||||
let mut _2: std::option::Option<i32>;
|
||||
let mut _3: isize;
|
||||
let mut _4: &std::option::Option<i32>;
|
||||
let mut _3: &std::option::Option<i32>;
|
||||
let mut _4: isize;
|
||||
let _5: i32;
|
||||
let _6: &i32;
|
||||
let mut _7: bool;
|
||||
@ -27,8 +27,8 @@ fn full_tested_match() -> () {
|
||||
StorageLive(_2);
|
||||
_2 = Option::<i32>::Some(const 42_i32);
|
||||
PlaceMention(_2);
|
||||
_3 = discriminant(_2);
|
||||
switchInt(move _3) -> [0: bb5, 1: bb2, otherwise: bb1];
|
||||
_4 = discriminant(_2);
|
||||
switchInt(move _4) -> [0: bb5, 1: bb2, otherwise: bb1];
|
||||
}
|
||||
|
||||
bb1: {
|
||||
@ -60,7 +60,7 @@ fn full_tested_match() -> () {
|
||||
bb7: {
|
||||
StorageLive(_6);
|
||||
_6 = &((_2 as Some).0: i32);
|
||||
_4 = &fake _2;
|
||||
_3 = &fake shallow _2;
|
||||
StorageLive(_7);
|
||||
_7 = guard() -> [return: bb8, unwind: bb16];
|
||||
}
|
||||
@ -71,7 +71,7 @@ fn full_tested_match() -> () {
|
||||
|
||||
bb9: {
|
||||
StorageDead(_7);
|
||||
FakeRead(ForMatchGuard, _4);
|
||||
FakeRead(ForMatchGuard, _3);
|
||||
FakeRead(ForGuardBinding, _6);
|
||||
StorageLive(_5);
|
||||
_5 = ((_2 as Some).0: i32);
|
||||
|
@ -4,8 +4,8 @@ fn full_tested_match2() -> () {
|
||||
let mut _0: ();
|
||||
let mut _1: (i32, i32);
|
||||
let mut _2: std::option::Option<i32>;
|
||||
let mut _3: isize;
|
||||
let mut _4: &std::option::Option<i32>;
|
||||
let mut _3: &std::option::Option<i32>;
|
||||
let mut _4: isize;
|
||||
let _5: i32;
|
||||
let _6: &i32;
|
||||
let mut _7: bool;
|
||||
@ -27,8 +27,8 @@ fn full_tested_match2() -> () {
|
||||
StorageLive(_2);
|
||||
_2 = Option::<i32>::Some(const 42_i32);
|
||||
PlaceMention(_2);
|
||||
_3 = discriminant(_2);
|
||||
switchInt(move _3) -> [0: bb5, 1: bb2, otherwise: bb1];
|
||||
_4 = discriminant(_2);
|
||||
switchInt(move _4) -> [0: bb5, 1: bb2, otherwise: bb1];
|
||||
}
|
||||
|
||||
bb1: {
|
||||
@ -66,7 +66,7 @@ fn full_tested_match2() -> () {
|
||||
bb7: {
|
||||
StorageLive(_6);
|
||||
_6 = &((_2 as Some).0: i32);
|
||||
_4 = &fake _2;
|
||||
_3 = &fake shallow _2;
|
||||
StorageLive(_7);
|
||||
_7 = guard() -> [return: bb8, unwind: bb16];
|
||||
}
|
||||
@ -77,7 +77,7 @@ fn full_tested_match2() -> () {
|
||||
|
||||
bb9: {
|
||||
StorageDead(_7);
|
||||
FakeRead(ForMatchGuard, _4);
|
||||
FakeRead(ForMatchGuard, _3);
|
||||
FakeRead(ForGuardBinding, _6);
|
||||
StorageLive(_5);
|
||||
_5 = ((_2 as Some).0: i32);
|
||||
|
@ -4,9 +4,9 @@ fn main() -> () {
|
||||
let mut _0: ();
|
||||
let mut _1: i32;
|
||||
let mut _2: std::option::Option<i32>;
|
||||
let mut _3: isize;
|
||||
let mut _3: &std::option::Option<i32>;
|
||||
let mut _4: isize;
|
||||
let mut _5: &std::option::Option<i32>;
|
||||
let mut _5: isize;
|
||||
let _6: i32;
|
||||
let _7: &i32;
|
||||
let mut _8: bool;
|
||||
@ -38,8 +38,8 @@ fn main() -> () {
|
||||
StorageLive(_2);
|
||||
_2 = Option::<i32>::Some(const 1_i32);
|
||||
PlaceMention(_2);
|
||||
_4 = discriminant(_2);
|
||||
switchInt(move _4) -> [1: bb8, otherwise: bb2];
|
||||
_5 = discriminant(_2);
|
||||
switchInt(move _5) -> [1: bb8, otherwise: bb2];
|
||||
}
|
||||
|
||||
bb1: {
|
||||
@ -52,8 +52,8 @@ fn main() -> () {
|
||||
}
|
||||
|
||||
bb3: {
|
||||
_3 = discriminant(_2);
|
||||
switchInt(move _3) -> [1: bb6, otherwise: bb4];
|
||||
_4 = discriminant(_2);
|
||||
switchInt(move _4) -> [1: bb6, otherwise: bb4];
|
||||
}
|
||||
|
||||
bb4: {
|
||||
@ -87,7 +87,7 @@ fn main() -> () {
|
||||
bb10: {
|
||||
StorageLive(_7);
|
||||
_7 = &((_2 as Some).0: i32);
|
||||
_5 = &fake _2;
|
||||
_3 = &fake shallow _2;
|
||||
StorageLive(_8);
|
||||
_8 = guard() -> [return: bb11, unwind: bb24];
|
||||
}
|
||||
@ -98,7 +98,7 @@ fn main() -> () {
|
||||
|
||||
bb12: {
|
||||
StorageDead(_8);
|
||||
FakeRead(ForMatchGuard, _5);
|
||||
FakeRead(ForMatchGuard, _3);
|
||||
FakeRead(ForGuardBinding, _7);
|
||||
StorageLive(_6);
|
||||
_6 = ((_2 as Some).0: i32);
|
||||
@ -129,7 +129,7 @@ fn main() -> () {
|
||||
bb16: {
|
||||
StorageLive(_11);
|
||||
_11 = &((_2 as Some).0: i32);
|
||||
_5 = &fake _2;
|
||||
_3 = &fake shallow _2;
|
||||
StorageLive(_12);
|
||||
StorageLive(_13);
|
||||
_13 = (*_11);
|
||||
@ -143,7 +143,7 @@ fn main() -> () {
|
||||
bb18: {
|
||||
StorageDead(_13);
|
||||
StorageDead(_12);
|
||||
FakeRead(ForMatchGuard, _5);
|
||||
FakeRead(ForMatchGuard, _3);
|
||||
FakeRead(ForGuardBinding, _11);
|
||||
StorageLive(_10);
|
||||
_10 = ((_2 as Some).0: i32);
|
||||
|
@ -7,10 +7,10 @@ fn constant_eq(_1: &str, _2: bool) -> u32 {
|
||||
let mut _3: (&str, bool);
|
||||
let mut _4: &str;
|
||||
let mut _5: bool;
|
||||
let mut _6: bool;
|
||||
let mut _7: bool;
|
||||
let mut _8: &&str;
|
||||
let mut _9: &bool;
|
||||
let mut _6: &&str;
|
||||
let mut _7: &bool;
|
||||
let mut _8: bool;
|
||||
let mut _9: bool;
|
||||
let mut _10: bool;
|
||||
|
||||
bb0: {
|
||||
@ -23,7 +23,7 @@ fn constant_eq(_1: &str, _2: bool) -> u32 {
|
||||
StorageDead(_5);
|
||||
StorageDead(_4);
|
||||
PlaceMention(_3);
|
||||
_7 = <str as PartialEq>::eq((_3.0: &str), const "a") -> [return: bb11, unwind: bb19];
|
||||
_9 = <str as PartialEq>::eq((_3.0: &str), const "a") -> [return: bb11, unwind: bb19];
|
||||
}
|
||||
|
||||
bb1: {
|
||||
@ -52,7 +52,7 @@ fn constant_eq(_1: &str, _2: bool) -> u32 {
|
||||
}
|
||||
|
||||
bb7: {
|
||||
_6 = <str as PartialEq>::eq((_3.0: &str), const "b") -> [return: bb10, unwind: bb19];
|
||||
_8 = <str as PartialEq>::eq((_3.0: &str), const "b") -> [return: bb10, unwind: bb19];
|
||||
}
|
||||
|
||||
bb8: {
|
||||
@ -64,16 +64,16 @@ fn constant_eq(_1: &str, _2: bool) -> u32 {
|
||||
}
|
||||
|
||||
bb10: {
|
||||
switchInt(move _6) -> [0: bb1, otherwise: bb8];
|
||||
switchInt(move _8) -> [0: bb1, otherwise: bb8];
|
||||
}
|
||||
|
||||
bb11: {
|
||||
switchInt(move _7) -> [0: bb7, otherwise: bb4];
|
||||
switchInt(move _9) -> [0: bb7, otherwise: bb4];
|
||||
}
|
||||
|
||||
bb12: {
|
||||
_8 = &fake (_3.0: &str);
|
||||
_9 = &fake (_3.1: bool);
|
||||
_6 = &fake shallow (_3.0: &str);
|
||||
_7 = &fake shallow (_3.1: bool);
|
||||
StorageLive(_10);
|
||||
_10 = const true;
|
||||
switchInt(move _10) -> [0: bb14, otherwise: bb13];
|
||||
@ -81,8 +81,8 @@ fn constant_eq(_1: &str, _2: bool) -> u32 {
|
||||
|
||||
bb13: {
|
||||
StorageDead(_10);
|
||||
FakeRead(ForMatchGuard, _8);
|
||||
FakeRead(ForMatchGuard, _9);
|
||||
FakeRead(ForMatchGuard, _6);
|
||||
FakeRead(ForMatchGuard, _7);
|
||||
_0 = const 1_u32;
|
||||
goto -> bb18;
|
||||
}
|
||||
|
@ -4,17 +4,17 @@ fn disjoint_ranges(_1: i32, _2: bool) -> u32 {
|
||||
debug x => _1;
|
||||
debug b => _2;
|
||||
let mut _0: u32;
|
||||
let mut _3: bool;
|
||||
let mut _3: &i32;
|
||||
let mut _4: bool;
|
||||
let mut _5: bool;
|
||||
let mut _6: bool;
|
||||
let mut _7: &i32;
|
||||
let mut _7: bool;
|
||||
let mut _8: bool;
|
||||
|
||||
bb0: {
|
||||
PlaceMention(_1);
|
||||
_5 = Le(const 0_i32, _1);
|
||||
switchInt(move _5) -> [0: bb3, otherwise: bb8];
|
||||
_6 = Le(const 0_i32, _1);
|
||||
switchInt(move _6) -> [0: bb3, otherwise: bb8];
|
||||
}
|
||||
|
||||
bb1: {
|
||||
@ -27,8 +27,8 @@ fn disjoint_ranges(_1: i32, _2: bool) -> u32 {
|
||||
}
|
||||
|
||||
bb3: {
|
||||
_3 = Le(const 10_i32, _1);
|
||||
switchInt(move _3) -> [0: bb5, otherwise: bb7];
|
||||
_4 = Le(const 10_i32, _1);
|
||||
switchInt(move _4) -> [0: bb5, otherwise: bb7];
|
||||
}
|
||||
|
||||
bb4: {
|
||||
@ -44,17 +44,17 @@ fn disjoint_ranges(_1: i32, _2: bool) -> u32 {
|
||||
}
|
||||
|
||||
bb7: {
|
||||
_4 = Le(_1, const 20_i32);
|
||||
switchInt(move _4) -> [0: bb5, otherwise: bb4];
|
||||
_5 = Le(_1, const 20_i32);
|
||||
switchInt(move _5) -> [0: bb5, otherwise: bb4];
|
||||
}
|
||||
|
||||
bb8: {
|
||||
_6 = Lt(_1, const 10_i32);
|
||||
switchInt(move _6) -> [0: bb3, otherwise: bb2];
|
||||
_7 = Lt(_1, const 10_i32);
|
||||
switchInt(move _7) -> [0: bb3, otherwise: bb2];
|
||||
}
|
||||
|
||||
bb9: {
|
||||
_7 = &fake _1;
|
||||
_3 = &fake shallow _1;
|
||||
StorageLive(_8);
|
||||
_8 = _2;
|
||||
switchInt(move _8) -> [0: bb11, otherwise: bb10];
|
||||
@ -62,7 +62,7 @@ fn disjoint_ranges(_1: i32, _2: bool) -> u32 {
|
||||
|
||||
bb10: {
|
||||
StorageDead(_8);
|
||||
FakeRead(ForMatchGuard, _7);
|
||||
FakeRead(ForMatchGuard, _3);
|
||||
_0 = const 0_u32;
|
||||
goto -> bb14;
|
||||
}
|
||||
|
@ -80,8 +80,8 @@
|
||||
_6 = &(_2.1: bool);
|
||||
StorageLive(_8);
|
||||
_8 = &(_2.2: std::string::String);
|
||||
- _3 = &fake (_2.0: bool);
|
||||
- _4 = &fake (_2.1: bool);
|
||||
- _3 = &fake shallow (_2.0: bool);
|
||||
- _4 = &fake shallow (_2.1: bool);
|
||||
StorageLive(_9);
|
||||
StorageLive(_10);
|
||||
_10 = _1;
|
||||
@ -137,8 +137,8 @@
|
||||
_6 = &(_2.0: bool);
|
||||
StorageLive(_8);
|
||||
_8 = &(_2.2: std::string::String);
|
||||
- _3 = &fake (_2.0: bool);
|
||||
- _4 = &fake (_2.1: bool);
|
||||
- _3 = &fake shallow (_2.0: bool);
|
||||
- _4 = &fake shallow (_2.1: bool);
|
||||
StorageLive(_12);
|
||||
StorageLive(_13);
|
||||
_13 = _1;
|
||||
|
@ -80,8 +80,8 @@
|
||||
_6 = &(_2.1: bool);
|
||||
StorageLive(_8);
|
||||
_8 = &(_2.2: std::string::String);
|
||||
- _3 = &fake (_2.0: bool);
|
||||
- _4 = &fake (_2.1: bool);
|
||||
- _3 = &fake shallow (_2.0: bool);
|
||||
- _4 = &fake shallow (_2.1: bool);
|
||||
StorageLive(_9);
|
||||
StorageLive(_10);
|
||||
_10 = _1;
|
||||
@ -137,8 +137,8 @@
|
||||
_6 = &(_2.0: bool);
|
||||
StorageLive(_8);
|
||||
_8 = &(_2.2: std::string::String);
|
||||
- _3 = &fake (_2.0: bool);
|
||||
- _4 = &fake (_2.1: bool);
|
||||
- _3 = &fake shallow (_2.0: bool);
|
||||
- _4 = &fake shallow (_2.1: bool);
|
||||
StorageLive(_12);
|
||||
StorageLive(_13);
|
||||
_13 = _1;
|
||||
|
@ -5,17 +5,17 @@
|
||||
debug x => _1;
|
||||
debug c => _2;
|
||||
let mut _0: i32;
|
||||
let mut _3: isize;
|
||||
let mut _4: &std::option::Option<&&i32>;
|
||||
let mut _3: &std::option::Option<&&i32>;
|
||||
let mut _4: &i32;
|
||||
let mut _5: &&i32;
|
||||
let mut _6: &&&i32;
|
||||
let mut _7: &i32;
|
||||
let mut _7: isize;
|
||||
let mut _8: bool;
|
||||
|
||||
bb0: {
|
||||
PlaceMention(_1);
|
||||
_3 = discriminant(_1);
|
||||
switchInt(move _3) -> [1: bb2, otherwise: bb1];
|
||||
_7 = discriminant(_1);
|
||||
switchInt(move _7) -> [1: bb2, otherwise: bb1];
|
||||
}
|
||||
|
||||
bb1: {
|
||||
@ -33,10 +33,10 @@
|
||||
}
|
||||
|
||||
bb4: {
|
||||
- _4 = &fake _1;
|
||||
- _5 = &fake (*((_1 as Some).0: &&i32));
|
||||
- _6 = &fake ((_1 as Some).0: &&i32);
|
||||
- _7 = &fake (*(*((_1 as Some).0: &&i32)));
|
||||
- _3 = &fake shallow _1;
|
||||
- _4 = &fake shallow (*(*((_1 as Some).0: &&i32)));
|
||||
- _5 = &fake shallow (*((_1 as Some).0: &&i32));
|
||||
- _6 = &fake shallow ((_1 as Some).0: &&i32);
|
||||
+ nop;
|
||||
+ nop;
|
||||
+ nop;
|
||||
@ -48,10 +48,10 @@
|
||||
|
||||
bb5: {
|
||||
StorageDead(_8);
|
||||
- FakeRead(ForMatchGuard, _3);
|
||||
- FakeRead(ForMatchGuard, _4);
|
||||
- FakeRead(ForMatchGuard, _5);
|
||||
- FakeRead(ForMatchGuard, _6);
|
||||
- FakeRead(ForMatchGuard, _7);
|
||||
+ nop;
|
||||
+ nop;
|
||||
+ nop;
|
||||
|
@ -5,17 +5,17 @@
|
||||
debug x => _1;
|
||||
debug c => _2;
|
||||
let mut _0: i32;
|
||||
let mut _3: isize;
|
||||
let mut _4: &std::option::Option<&&i32>;
|
||||
let mut _3: &std::option::Option<&&i32>;
|
||||
let mut _4: &i32;
|
||||
let mut _5: &&i32;
|
||||
let mut _6: &&&i32;
|
||||
let mut _7: &i32;
|
||||
let mut _7: isize;
|
||||
let mut _8: bool;
|
||||
|
||||
bb0: {
|
||||
PlaceMention(_1);
|
||||
_3 = discriminant(_1);
|
||||
switchInt(move _3) -> [1: bb2, otherwise: bb1];
|
||||
_7 = discriminant(_1);
|
||||
switchInt(move _7) -> [1: bb2, otherwise: bb1];
|
||||
}
|
||||
|
||||
bb1: {
|
||||
@ -33,10 +33,10 @@
|
||||
}
|
||||
|
||||
bb4: {
|
||||
- _4 = &fake _1;
|
||||
- _5 = &fake (*((_1 as Some).0: &&i32));
|
||||
- _6 = &fake ((_1 as Some).0: &&i32);
|
||||
- _7 = &fake (*(*((_1 as Some).0: &&i32)));
|
||||
- _3 = &fake shallow _1;
|
||||
- _4 = &fake shallow (*(*((_1 as Some).0: &&i32)));
|
||||
- _5 = &fake shallow (*((_1 as Some).0: &&i32));
|
||||
- _6 = &fake shallow ((_1 as Some).0: &&i32);
|
||||
+ nop;
|
||||
+ nop;
|
||||
+ nop;
|
||||
@ -48,10 +48,10 @@
|
||||
|
||||
bb5: {
|
||||
StorageDead(_8);
|
||||
- FakeRead(ForMatchGuard, _3);
|
||||
- FakeRead(ForMatchGuard, _4);
|
||||
- FakeRead(ForMatchGuard, _5);
|
||||
- FakeRead(ForMatchGuard, _6);
|
||||
- FakeRead(ForMatchGuard, _7);
|
||||
+ nop;
|
||||
+ nop;
|
||||
+ nop;
|
||||
|
64
tests/ui/pattern/deref-patterns/bindings.rs
Normal file
64
tests/ui/pattern/deref-patterns/bindings.rs
Normal file
@ -0,0 +1,64 @@
|
||||
//@ run-pass
|
||||
#![feature(deref_patterns)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
fn simple_vec(vec: Vec<u32>) -> u32 {
|
||||
match vec {
|
||||
deref!([]) => 100,
|
||||
deref!([x]) if x == 4 => x + 4,
|
||||
deref!([x]) => x,
|
||||
deref!([1, x]) => x + 200,
|
||||
deref!(ref slice) => slice.iter().sum(),
|
||||
_ => 2000,
|
||||
}
|
||||
}
|
||||
|
||||
fn nested_vec(vecvec: Vec<Vec<u32>>) -> u32 {
|
||||
match vecvec {
|
||||
deref!([]) => 0,
|
||||
deref!([deref!([x])]) => x,
|
||||
deref!([deref!([0, x]) | deref!([1, x])]) => x,
|
||||
deref!([ref x]) => x.iter().sum(),
|
||||
deref!([deref!([]), deref!([1, x, y])]) => y - x,
|
||||
_ => 2000,
|
||||
}
|
||||
}
|
||||
|
||||
fn ref_mut(val: u32) -> u32 {
|
||||
let mut b = Box::new(0u32);
|
||||
match &mut b {
|
||||
deref!(_x) if false => unreachable!(),
|
||||
deref!(x) => {
|
||||
*x = val;
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
let deref!(x) = &b else { unreachable!() };
|
||||
*x
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
fn or_and_guard(tuple: (u32, u32)) -> u32 {
|
||||
let mut sum = 0;
|
||||
let b = Box::new(tuple);
|
||||
match b {
|
||||
deref!((x, _) | (_, x)) if { sum += x; false } => {},
|
||||
_ => {},
|
||||
}
|
||||
sum
|
||||
}
|
||||
|
||||
fn main() {
|
||||
assert_eq!(simple_vec(vec![1]), 1);
|
||||
assert_eq!(simple_vec(vec![1, 2]), 202);
|
||||
assert_eq!(simple_vec(vec![1, 2, 3]), 6);
|
||||
assert_eq!(simple_vec(vec![4]), 8);
|
||||
|
||||
assert_eq!(nested_vec(vec![vec![0, 42]]), 42);
|
||||
assert_eq!(nested_vec(vec![vec![1, 42]]), 42);
|
||||
assert_eq!(nested_vec(vec![vec![1, 2, 3]]), 6);
|
||||
assert_eq!(nested_vec(vec![vec![], vec![1, 2, 3]]), 1);
|
||||
|
||||
assert_eq!(ref_mut(42), 42);
|
||||
assert_eq!(or_and_guard((10, 32)), 42);
|
||||
}
|
40
tests/ui/pattern/deref-patterns/branch.rs
Normal file
40
tests/ui/pattern/deref-patterns/branch.rs
Normal file
@ -0,0 +1,40 @@
|
||||
//@ run-pass
|
||||
// Test the execution of deref patterns.
|
||||
#![feature(deref_patterns)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
fn branch(vec: Vec<u32>) -> u32 {
|
||||
match vec {
|
||||
deref!([]) => 0,
|
||||
deref!([1, _, 3]) => 1,
|
||||
deref!([2, ..]) => 2,
|
||||
_ => 1000,
|
||||
}
|
||||
}
|
||||
|
||||
fn nested(vec: Vec<Vec<u32>>) -> u32 {
|
||||
match vec {
|
||||
deref!([deref!([]), ..]) => 1,
|
||||
deref!([deref!([0, ..]), deref!([1, ..])]) => 2,
|
||||
_ => 1000,
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
assert!(matches!(Vec::<u32>::new(), deref!([])));
|
||||
assert!(matches!(vec![1], deref!([1])));
|
||||
assert!(matches!(&vec![1], deref!([1])));
|
||||
assert!(matches!(vec![&1], deref!([1])));
|
||||
assert!(matches!(vec![vec![1]], deref!([deref!([1])])));
|
||||
|
||||
assert_eq!(branch(vec![]), 0);
|
||||
assert_eq!(branch(vec![1, 2, 3]), 1);
|
||||
assert_eq!(branch(vec![3, 2, 1]), 1000);
|
||||
assert_eq!(branch(vec![2]), 2);
|
||||
assert_eq!(branch(vec![2, 3]), 2);
|
||||
assert_eq!(branch(vec![3, 2]), 1000);
|
||||
|
||||
assert_eq!(nested(vec![vec![], vec![2]]), 1);
|
||||
assert_eq!(nested(vec![vec![0], vec![1]]), 2);
|
||||
assert_eq!(nested(vec![vec![0, 2], vec![1, 2]]), 2);
|
||||
}
|
24
tests/ui/pattern/deref-patterns/cant_move_out_of_pattern.rs
Normal file
24
tests/ui/pattern/deref-patterns/cant_move_out_of_pattern.rs
Normal file
@ -0,0 +1,24 @@
|
||||
#![feature(deref_patterns)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
use std::rc::Rc;
|
||||
|
||||
struct Struct;
|
||||
|
||||
fn cant_move_out_box(b: Box<Struct>) -> Struct {
|
||||
match b {
|
||||
//~^ ERROR: cannot move out of a shared reference
|
||||
deref!(x) => x,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn cant_move_out_rc(rc: Rc<Struct>) -> Struct {
|
||||
match rc {
|
||||
//~^ ERROR: cannot move out of a shared reference
|
||||
deref!(x) => x,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
@ -0,0 +1,27 @@
|
||||
error[E0507]: cannot move out of a shared reference
|
||||
--> $DIR/cant_move_out_of_pattern.rs:9:11
|
||||
|
|
||||
LL | match b {
|
||||
| ^
|
||||
LL |
|
||||
LL | deref!(x) => x,
|
||||
| -
|
||||
| |
|
||||
| data moved here
|
||||
| move occurs because `x` has type `Struct`, which does not implement the `Copy` trait
|
||||
|
||||
error[E0507]: cannot move out of a shared reference
|
||||
--> $DIR/cant_move_out_of_pattern.rs:17:11
|
||||
|
|
||||
LL | match rc {
|
||||
| ^^
|
||||
LL |
|
||||
LL | deref!(x) => x,
|
||||
| -
|
||||
| |
|
||||
| data moved here
|
||||
| move occurs because `x` has type `Struct`, which does not implement the `Copy` trait
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0507`.
|
21
tests/ui/pattern/deref-patterns/closure_capture.rs
Normal file
21
tests/ui/pattern/deref-patterns/closure_capture.rs
Normal file
@ -0,0 +1,21 @@
|
||||
//@ run-pass
|
||||
#![feature(deref_patterns)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
fn main() {
|
||||
let b = Box::new("aaa".to_string());
|
||||
let f = || {
|
||||
let deref!(ref s) = b else { unreachable!() };
|
||||
assert_eq!(s.len(), 3);
|
||||
};
|
||||
assert_eq!(b.len(), 3);
|
||||
f();
|
||||
|
||||
let mut b = Box::new("aaa".to_string());
|
||||
let mut f = || {
|
||||
let deref!(ref mut s) = b else { unreachable!() };
|
||||
s.push_str("aa");
|
||||
};
|
||||
f();
|
||||
assert_eq!(b.len(), 5);
|
||||
}
|
14
tests/ui/pattern/deref-patterns/fake_borrows.rs
Normal file
14
tests/ui/pattern/deref-patterns/fake_borrows.rs
Normal file
@ -0,0 +1,14 @@
|
||||
#![feature(deref_patterns)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
#[rustfmt::skip]
|
||||
fn main() {
|
||||
let mut b = Box::new(false);
|
||||
match b {
|
||||
deref!(true) => {}
|
||||
_ if { *b = true; false } => {}
|
||||
//~^ ERROR cannot assign `*b` in match guard
|
||||
deref!(false) => {}
|
||||
_ => {},
|
||||
}
|
||||
}
|
12
tests/ui/pattern/deref-patterns/fake_borrows.stderr
Normal file
12
tests/ui/pattern/deref-patterns/fake_borrows.stderr
Normal file
@ -0,0 +1,12 @@
|
||||
error[E0510]: cannot assign `*b` in match guard
|
||||
--> $DIR/fake_borrows.rs:9:16
|
||||
|
|
||||
LL | match b {
|
||||
| - value is immutable in match guard
|
||||
LL | deref!(true) => {}
|
||||
LL | _ if { *b = true; false } => {}
|
||||
| ^^^^^^^^^ cannot assign
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0510`.
|
@ -4,6 +4,8 @@
|
||||
|
||||
use std::rc::Rc;
|
||||
|
||||
struct Struct;
|
||||
|
||||
fn main() {
|
||||
let vec: Vec<u32> = Vec::new();
|
||||
match vec {
|
||||
@ -22,10 +24,12 @@ fn main() {
|
||||
deref!(1..) => {}
|
||||
_ => {}
|
||||
}
|
||||
// FIXME(deref_patterns): fails to typecheck because `"foo"` has type &str but deref creates a
|
||||
// place of type `str`.
|
||||
// match "foo".to_string() {
|
||||
// box "foo" => {}
|
||||
// _ => {}
|
||||
// }
|
||||
let _: &Struct = match &Rc::new(Struct) {
|
||||
deref!(x) => x,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let _: &[Struct] = match &Rc::new(vec![Struct]) {
|
||||
deref!(deref!(x)) => x,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
}
|
||||
|
17
tests/ui/pattern/deref-patterns/typeck_fail.rs
Normal file
17
tests/ui/pattern/deref-patterns/typeck_fail.rs
Normal file
@ -0,0 +1,17 @@
|
||||
#![feature(deref_patterns)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
fn main() {
|
||||
// FIXME(deref_patterns): fails to typecheck because `"foo"` has type &str but deref creates a
|
||||
// place of type `str`.
|
||||
match "foo".to_string() {
|
||||
deref!("foo") => {}
|
||||
//~^ ERROR: mismatched types
|
||||
_ => {}
|
||||
}
|
||||
match &"foo".to_string() {
|
||||
deref!("foo") => {}
|
||||
//~^ ERROR: mismatched types
|
||||
_ => {}
|
||||
}
|
||||
}
|
19
tests/ui/pattern/deref-patterns/typeck_fail.stderr
Normal file
19
tests/ui/pattern/deref-patterns/typeck_fail.stderr
Normal file
@ -0,0 +1,19 @@
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/typeck_fail.rs:8:16
|
||||
|
|
||||
LL | match "foo".to_string() {
|
||||
| ----------------- this expression has type `String`
|
||||
LL | deref!("foo") => {}
|
||||
| ^^^^^ expected `str`, found `&str`
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/typeck_fail.rs:13:16
|
||||
|
|
||||
LL | match &"foo".to_string() {
|
||||
| ------------------ this expression has type `&String`
|
||||
LL | deref!("foo") => {}
|
||||
| ^^^^^ expected `str`, found `&str`
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0308`.
|
Loading…
x
Reference in New Issue
Block a user