Auto merge of #103208 - cjgillot:match-fake-read, r=oli-obk,RalfJung
Allow partially moved values in match This PR attempts to unify the behaviour between `let _ = PLACE`, `let _: TY = PLACE;` and `match PLACE { _ => {} }`. The logical conclusion is that the `match` version should not check for uninitialised places nor check that borrows are still live. The `match PLACE {}` case is handled by keeping a `FakeRead` in the unreachable fallback case to verify that `PLACE` has a legal value. Schematically, `match PLACE { arms }` in surface rust becomes in MIR: ```rust PlaceMention(PLACE) match PLACE { // Decision tree for the explicit arms arms, // An extra fallback arm _ => { FakeRead(ForMatchedPlace, PLACE); unreachable } } ``` `match *borrow { _ => {} }` continues to check that `*borrow` is live, but does not read the value. `match *borrow {}` both checks that `*borrow` is live, and fake-reads the value. Continuation of ~https://github.com/rust-lang/rust/pull/102256~ ~https://github.com/rust-lang/rust/pull/104844~ Fixes https://github.com/rust-lang/rust/issues/99180 https://github.com/rust-lang/rust/issues/53114
This commit is contained in:
commit
59bb9505bc
64 changed files with 394 additions and 506 deletions
|
@ -157,7 +157,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
/// [ 0. Pre-match ]
|
||||
/// |
|
||||
/// [ 1. Evaluate Scrutinee (expression being matched on) ]
|
||||
/// [ (fake read of scrutinee) ]
|
||||
/// [ (PlaceMention of scrutinee) ]
|
||||
/// |
|
||||
/// [ 2. Decision tree -- check discriminants ] <--------+
|
||||
/// | |
|
||||
|
@ -184,7 +184,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
///
|
||||
/// We generate MIR in the following steps:
|
||||
///
|
||||
/// 1. Evaluate the scrutinee and add the fake read of it ([Builder::lower_scrutinee]).
|
||||
/// 1. Evaluate the scrutinee and add the PlaceMention of it ([Builder::lower_scrutinee]).
|
||||
/// 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
|
||||
|
@ -223,6 +223,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
let fake_borrow_temps = self.lower_match_tree(
|
||||
block,
|
||||
scrutinee_span,
|
||||
&scrutinee_place,
|
||||
match_start_span,
|
||||
match_has_guard,
|
||||
&mut candidates,
|
||||
|
@ -238,7 +239,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
)
|
||||
}
|
||||
|
||||
/// Evaluate the scrutinee and add the fake read of it.
|
||||
/// Evaluate the scrutinee and add the PlaceMention for it.
|
||||
fn lower_scrutinee(
|
||||
&mut self,
|
||||
mut block: BasicBlock,
|
||||
|
@ -246,26 +247,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
scrutinee_span: Span,
|
||||
) -> BlockAnd<PlaceBuilder<'tcx>> {
|
||||
let scrutinee_place_builder = unpack!(block = self.as_place_builder(block, scrutinee));
|
||||
// Matching on a `scrutinee_place` with an uninhabited type doesn't
|
||||
// generate any memory reads by itself, and so if the place "expression"
|
||||
// contains unsafe operations like raw pointer dereferences or union
|
||||
// field projections, we wouldn't know to require an `unsafe` block
|
||||
// around a `match` equivalent to `std::intrinsics::unreachable()`.
|
||||
// See issue #47412 for this hole being discovered in the wild.
|
||||
//
|
||||
// HACK(eddyb) Work around the above issue by adding a dummy inspection
|
||||
// of `scrutinee_place`, specifically by applying `ReadForMatch`.
|
||||
//
|
||||
// NOTE: ReadForMatch also checks that the scrutinee is initialized.
|
||||
// This is currently needed to not allow matching on an uninitialized,
|
||||
// uninhabited value. If we get never patterns, those will check that
|
||||
// the place is initialized, and so this read would only be used to
|
||||
// check safety.
|
||||
let cause_matched_place = FakeReadCause::ForMatchedPlace(None);
|
||||
let source_info = self.source_info(scrutinee_span);
|
||||
|
||||
if let Some(scrutinee_place) = scrutinee_place_builder.try_to_place(self) {
|
||||
self.cfg.push_fake_read(block, source_info, cause_matched_place, scrutinee_place);
|
||||
let source_info = self.source_info(scrutinee_span);
|
||||
self.cfg.push_place_mention(block, source_info, scrutinee_place);
|
||||
}
|
||||
|
||||
block.and(scrutinee_place_builder)
|
||||
|
@ -304,6 +288,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
&mut self,
|
||||
block: BasicBlock,
|
||||
scrutinee_span: Span,
|
||||
scrutinee_place_builder: &PlaceBuilder<'tcx>,
|
||||
match_start_span: Span,
|
||||
match_has_guard: bool,
|
||||
candidates: &mut [&mut Candidate<'pat, 'tcx>],
|
||||
|
@ -331,6 +316,33 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
// otherwise block. Match checking will ensure this is actually
|
||||
// unreachable.
|
||||
let source_info = self.source_info(scrutinee_span);
|
||||
|
||||
// Matching on a `scrutinee_place` with an uninhabited type doesn't
|
||||
// generate any memory reads by itself, and so if the place "expression"
|
||||
// contains unsafe operations like raw pointer dereferences or union
|
||||
// field projections, we wouldn't know to require an `unsafe` block
|
||||
// around a `match` equivalent to `std::intrinsics::unreachable()`.
|
||||
// See issue #47412 for this hole being discovered in the wild.
|
||||
//
|
||||
// HACK(eddyb) Work around the above issue by adding a dummy inspection
|
||||
// of `scrutinee_place`, specifically by applying `ReadForMatch`.
|
||||
//
|
||||
// NOTE: ReadForMatch also checks that the scrutinee is initialized.
|
||||
// This is currently needed to not allow matching on an uninitialized,
|
||||
// uninhabited value. If we get never patterns, those will check that
|
||||
// the place is initialized, and so this read would only be used to
|
||||
// check safety.
|
||||
let cause_matched_place = FakeReadCause::ForMatchedPlace(None);
|
||||
|
||||
if let Some(scrutinee_place) = scrutinee_place_builder.try_to_place(self) {
|
||||
self.cfg.push_fake_read(
|
||||
otherwise_block,
|
||||
source_info,
|
||||
cause_matched_place,
|
||||
scrutinee_place,
|
||||
);
|
||||
}
|
||||
|
||||
self.cfg.terminate(otherwise_block, source_info, TerminatorKind::Unreachable);
|
||||
}
|
||||
|
||||
|
@ -599,13 +611,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
}
|
||||
|
||||
_ => {
|
||||
let place_builder = unpack!(block = self.as_place_builder(block, initializer));
|
||||
|
||||
if let Some(place) = place_builder.try_to_place(self) {
|
||||
let source_info = self.source_info(initializer.span);
|
||||
self.cfg.push_place_mention(block, source_info, place);
|
||||
}
|
||||
|
||||
let place_builder =
|
||||
unpack!(block = self.lower_scrutinee(block, initializer, initializer.span));
|
||||
self.place_into_pattern(block, &irrefutable_pat, place_builder, true)
|
||||
}
|
||||
}
|
||||
|
@ -622,6 +629,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
let fake_borrow_temps = self.lower_match_tree(
|
||||
block,
|
||||
irrefutable_pat.span,
|
||||
&initializer,
|
||||
irrefutable_pat.span,
|
||||
false,
|
||||
&mut [&mut candidate],
|
||||
|
@ -1841,6 +1849,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
let fake_borrow_temps = self.lower_match_tree(
|
||||
block,
|
||||
pat.span,
|
||||
&expr_place_builder,
|
||||
pat.span,
|
||||
false,
|
||||
&mut [&mut guard_candidate, &mut otherwise_candidate],
|
||||
|
@ -2342,6 +2351,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
let fake_borrow_temps = this.lower_match_tree(
|
||||
block,
|
||||
initializer_span,
|
||||
&scrutinee,
|
||||
pattern.span,
|
||||
false,
|
||||
&mut [&mut candidate, &mut wildcard],
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue