mirror of
https://github.com/rust-lang/rust
synced 2024-10-14 12:33:57 +00:00
Rollup merge of #126835 - Nadrieril:reify-decision-tree, r=matthewjasper
Simplifications in match lowering A series of small simplifications and deduplications in the MIR lowering of patterns. r? ````@matthewjasper````
This commit is contained in:
commit
806c5c1971
|
@ -189,38 +189,37 @@ fn ast_block_stmts(
|
||||||
|
|
||||||
let initializer_span = this.thir[*initializer].span;
|
let initializer_span = this.thir[*initializer].span;
|
||||||
let scope = (*init_scope, source_info);
|
let scope = (*init_scope, source_info);
|
||||||
let failure = unpack!(
|
let failure_and_block = this.in_scope(scope, *lint_level, |this| {
|
||||||
block = this.in_scope(scope, *lint_level, |this| {
|
this.declare_bindings(
|
||||||
this.declare_bindings(
|
visibility_scope,
|
||||||
visibility_scope,
|
remainder_span,
|
||||||
remainder_span,
|
pattern,
|
||||||
pattern,
|
None,
|
||||||
None,
|
Some((Some(&destination), initializer_span)),
|
||||||
Some((Some(&destination), initializer_span)),
|
);
|
||||||
);
|
this.visit_primary_bindings(
|
||||||
this.visit_primary_bindings(
|
pattern,
|
||||||
pattern,
|
UserTypeProjections::none(),
|
||||||
UserTypeProjections::none(),
|
&mut |this, _, _, node, span, _, _| {
|
||||||
&mut |this, _, _, node, span, _, _| {
|
this.storage_live_binding(block, node, span, OutsideGuard, true);
|
||||||
this.storage_live_binding(
|
},
|
||||||
block,
|
);
|
||||||
node,
|
let else_block_span = this.thir[*else_block].span;
|
||||||
span,
|
let (matching, failure) =
|
||||||
OutsideGuard,
|
this.in_if_then_scope(last_remainder_scope, else_block_span, |this| {
|
||||||
true,
|
this.lower_let_expr(
|
||||||
);
|
block,
|
||||||
},
|
*initializer,
|
||||||
);
|
pattern,
|
||||||
this.ast_let_else(
|
None,
|
||||||
block,
|
initializer_span,
|
||||||
*initializer,
|
false,
|
||||||
initializer_span,
|
true,
|
||||||
*else_block,
|
)
|
||||||
&last_remainder_scope,
|
});
|
||||||
pattern,
|
matching.and(failure)
|
||||||
)
|
});
|
||||||
})
|
let failure = unpack!(block = failure_and_block);
|
||||||
);
|
|
||||||
this.cfg.goto(failure, source_info, failure_entry);
|
this.cfg.goto(failure, source_info, failure_entry);
|
||||||
|
|
||||||
if let Some(source_scope) = visibility_scope {
|
if let Some(source_scope) = visibility_scope {
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
use rustc_span::{BytePos, Pos, Span};
|
use rustc_span::{BytePos, Pos, Span};
|
||||||
use rustc_target::abi::VariantIdx;
|
use rustc_target::abi::VariantIdx;
|
||||||
use tracing::{debug, instrument};
|
use tracing::{debug, instrument};
|
||||||
|
use util::visit_bindings;
|
||||||
|
|
||||||
// helper functions, broken out by category:
|
// helper functions, broken out by category:
|
||||||
mod simplify;
|
mod simplify;
|
||||||
|
@ -146,6 +147,7 @@ fn then_else_break_inner(
|
||||||
Some(args.variable_source_info.scope),
|
Some(args.variable_source_info.scope),
|
||||||
args.variable_source_info.span,
|
args.variable_source_info.span,
|
||||||
args.declare_let_bindings,
|
args.declare_let_bindings,
|
||||||
|
false,
|
||||||
),
|
),
|
||||||
_ => {
|
_ => {
|
||||||
let mut block = block;
|
let mut block = block;
|
||||||
|
@ -314,13 +316,21 @@ pub(crate) fn match_expr(
|
||||||
|
|
||||||
let match_start_span = span.shrink_to_lo().to(scrutinee_span);
|
let match_start_span = span.shrink_to_lo().to(scrutinee_span);
|
||||||
|
|
||||||
let fake_borrow_temps = self.lower_match_tree(
|
// 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_borrow_temps: Vec<(Place<'tcx>, Local, FakeBorrowKind)> = if match_has_guard {
|
||||||
|
util::collect_fake_borrows(self, &candidates, scrutinee_span, scrutinee_place.base())
|
||||||
|
} else {
|
||||||
|
Vec::new()
|
||||||
|
};
|
||||||
|
|
||||||
|
self.lower_match_tree(
|
||||||
block,
|
block,
|
||||||
scrutinee_span,
|
scrutinee_span,
|
||||||
&scrutinee_place,
|
&scrutinee_place,
|
||||||
match_start_span,
|
match_start_span,
|
||||||
match_has_guard,
|
|
||||||
&mut candidates,
|
&mut candidates,
|
||||||
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
self.lower_match_arms(
|
self.lower_match_arms(
|
||||||
|
@ -375,89 +385,6 @@ fn create_match_candidates<'pat>(
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create the decision tree for the match expression, starting from `block`.
|
|
||||||
///
|
|
||||||
/// Modifies `candidates` to store the bindings and type ascriptions for
|
|
||||||
/// that candidate.
|
|
||||||
///
|
|
||||||
/// Returns the places that need fake borrows because we bind or test them.
|
|
||||||
fn lower_match_tree<'pat>(
|
|
||||||
&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>],
|
|
||||||
) -> 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
|
|
||||||
// unreachable.
|
|
||||||
let otherwise_block = self.cfg.start_new_block();
|
|
||||||
|
|
||||||
// This will generate code to test scrutinee_place and
|
|
||||||
// branch to the appropriate arm block
|
|
||||||
self.match_candidates(match_start_span, scrutinee_span, block, otherwise_block, candidates);
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
// Link each leaf candidate to the `pre_binding_block` of the next one.
|
|
||||||
let mut previous_candidate: Option<&mut Candidate<'_, '_>> = None;
|
|
||||||
|
|
||||||
for candidate in candidates {
|
|
||||||
candidate.visit_leaves(|leaf_candidate| {
|
|
||||||
if let Some(ref mut prev) = previous_candidate {
|
|
||||||
assert!(leaf_candidate.false_edge_start_block.is_some());
|
|
||||||
prev.next_candidate_start_block = leaf_candidate.false_edge_start_block;
|
|
||||||
}
|
|
||||||
previous_candidate = Some(leaf_candidate);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fake_borrows
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Lower the bindings, guards and arm bodies of a `match` expression.
|
/// Lower the bindings, guards and arm bodies of a `match` expression.
|
||||||
///
|
///
|
||||||
/// The decision tree should have already been created
|
/// The decision tree should have already been created
|
||||||
|
@ -728,59 +655,53 @@ pub(crate) fn place_into_pattern(
|
||||||
set_match_place: bool,
|
set_match_place: bool,
|
||||||
) -> BlockAnd<()> {
|
) -> BlockAnd<()> {
|
||||||
let mut candidate = Candidate::new(initializer.clone(), irrefutable_pat, false, self);
|
let mut candidate = Candidate::new(initializer.clone(), irrefutable_pat, false, self);
|
||||||
let fake_borrow_temps = self.lower_match_tree(
|
|
||||||
block,
|
|
||||||
irrefutable_pat.span,
|
|
||||||
&initializer,
|
|
||||||
irrefutable_pat.span,
|
|
||||||
false,
|
|
||||||
&mut [&mut candidate],
|
|
||||||
);
|
|
||||||
|
|
||||||
// For matches and function arguments, the place that is being matched
|
// For matches and function arguments, the place that is being matched
|
||||||
// can be set when creating the variables. But the place for
|
// can be set when creating the variables. But the place for
|
||||||
// let PATTERN = ... might not even exist until we do the assignment.
|
// let PATTERN = ... might not even exist until we do the assignment.
|
||||||
// so we set it here instead.
|
// so we set it here instead.
|
||||||
if set_match_place {
|
if set_match_place {
|
||||||
let mut next = Some(&candidate);
|
// `try_to_place` may fail if it is unable to resolve the given `PlaceBuilder` inside a
|
||||||
while let Some(candidate_ref) = next.take() {
|
// closure. In this case, we don't want to include a scrutinee place.
|
||||||
for binding in &candidate_ref.extra_data.bindings {
|
// `scrutinee_place_builder` will fail for destructured assignments. This is because a
|
||||||
|
// closure only captures the precise places that it will read and as a result a closure
|
||||||
|
// may not capture the entire tuple/struct and rather have individual places that will
|
||||||
|
// be read in the final MIR.
|
||||||
|
// Example:
|
||||||
|
// ```
|
||||||
|
// let foo = (0, 1);
|
||||||
|
// let c = || {
|
||||||
|
// let (v1, v2) = foo;
|
||||||
|
// };
|
||||||
|
// ```
|
||||||
|
if let Some(place) = initializer.try_to_place(self) {
|
||||||
|
visit_bindings(&[&mut candidate], |binding: &Binding<'_>| {
|
||||||
let local = self.var_local_id(binding.var_id, OutsideGuard);
|
let local = self.var_local_id(binding.var_id, OutsideGuard);
|
||||||
// `try_to_place` may fail if it is unable to resolve the given
|
if let LocalInfo::User(BindingForm::Var(VarBindingForm {
|
||||||
// `PlaceBuilder` inside a closure. In this case, we don't want to include
|
opt_match_place: Some((ref mut match_place, _)),
|
||||||
// a scrutinee place. `scrutinee_place_builder` will fail for destructured
|
..
|
||||||
// assignments. This is because a closure only captures the precise places
|
})) = **self.local_decls[local].local_info.as_mut().assert_crate_local()
|
||||||
// that it will read and as a result a closure may not capture the entire
|
{
|
||||||
// tuple/struct and rather have individual places that will be read in the
|
|
||||||
// final MIR.
|
|
||||||
// Example:
|
|
||||||
// ```
|
|
||||||
// let foo = (0, 1);
|
|
||||||
// let c = || {
|
|
||||||
// let (v1, v2) = foo;
|
|
||||||
// };
|
|
||||||
// ```
|
|
||||||
if let Some(place) = initializer.try_to_place(self) {
|
|
||||||
let LocalInfo::User(BindingForm::Var(VarBindingForm {
|
|
||||||
opt_match_place: Some((ref mut match_place, _)),
|
|
||||||
..
|
|
||||||
})) = **self.local_decls[local].local_info.as_mut().assert_crate_local()
|
|
||||||
else {
|
|
||||||
bug!("Let binding to non-user variable.")
|
|
||||||
};
|
|
||||||
*match_place = Some(place);
|
*match_place = Some(place);
|
||||||
}
|
} else {
|
||||||
}
|
bug!("Let binding to non-user variable.")
|
||||||
// All of the subcandidates should bind the same locals, so we
|
};
|
||||||
// only visit the first one.
|
});
|
||||||
next = candidate_ref.subcandidates.get(0)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.lower_match_tree(
|
||||||
|
block,
|
||||||
|
irrefutable_pat.span,
|
||||||
|
&initializer,
|
||||||
|
irrefutable_pat.span,
|
||||||
|
&mut [&mut candidate],
|
||||||
|
false,
|
||||||
|
);
|
||||||
self.bind_pattern(
|
self.bind_pattern(
|
||||||
self.source_info(irrefutable_pat.span),
|
self.source_info(irrefutable_pat.span),
|
||||||
candidate,
|
candidate,
|
||||||
fake_borrow_temps.as_slice(),
|
&[],
|
||||||
irrefutable_pat.span,
|
irrefutable_pat.span,
|
||||||
None,
|
None,
|
||||||
false,
|
false,
|
||||||
|
@ -1306,6 +1227,79 @@ fn as_constant(&self) -> Option<&Const<'tcx>> {
|
||||||
// Main matching algorithm
|
// Main matching algorithm
|
||||||
|
|
||||||
impl<'a, 'tcx> Builder<'a, 'tcx> {
|
impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
|
/// The entrypoint of the matching algorithm. Create the decision tree for the match expression,
|
||||||
|
/// starting from `block`.
|
||||||
|
///
|
||||||
|
/// Modifies `candidates` to store the bindings and type ascriptions for
|
||||||
|
/// that candidate.
|
||||||
|
///
|
||||||
|
/// `refutable` indicates whether the candidate list is refutable (for `if let` and `let else`)
|
||||||
|
/// or not (for `let` and `match`). In the refutable case we return the block to which we branch
|
||||||
|
/// on failure.
|
||||||
|
fn lower_match_tree<'pat>(
|
||||||
|
&mut self,
|
||||||
|
block: BasicBlock,
|
||||||
|
scrutinee_span: Span,
|
||||||
|
scrutinee_place_builder: &PlaceBuilder<'tcx>,
|
||||||
|
match_start_span: Span,
|
||||||
|
candidates: &mut [&mut Candidate<'pat, 'tcx>],
|
||||||
|
refutable: bool,
|
||||||
|
) -> BasicBlock {
|
||||||
|
// See the doc comment on `match_candidates` for why we have an otherwise block.
|
||||||
|
let otherwise_block = self.cfg.start_new_block();
|
||||||
|
|
||||||
|
// This will generate code to test scrutinee_place and branch to the appropriate arm block
|
||||||
|
self.match_candidates(match_start_span, scrutinee_span, block, otherwise_block, candidates);
|
||||||
|
|
||||||
|
// Link each leaf candidate to the `false_edge_start_block` of the next one.
|
||||||
|
let mut previous_candidate: Option<&mut Candidate<'_, '_>> = None;
|
||||||
|
for candidate in candidates {
|
||||||
|
candidate.visit_leaves(|leaf_candidate| {
|
||||||
|
if let Some(ref mut prev) = previous_candidate {
|
||||||
|
assert!(leaf_candidate.false_edge_start_block.is_some());
|
||||||
|
prev.next_candidate_start_block = leaf_candidate.false_edge_start_block;
|
||||||
|
}
|
||||||
|
previous_candidate = Some(leaf_candidate);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if refutable {
|
||||||
|
// In refutable cases there's always at least one candidate, and we want a false edge to
|
||||||
|
// the failure block.
|
||||||
|
previous_candidate.as_mut().unwrap().next_candidate_start_block = Some(otherwise_block)
|
||||||
|
} else {
|
||||||
|
// Match checking ensures `otherwise_block` is actually unreachable in irrefutable
|
||||||
|
// cases.
|
||||||
|
let source_info = self.source_info(scrutinee_span);
|
||||||
|
|
||||||
|
// Matching on a scrutinee place of an uninhabited type doesn't generate any memory
|
||||||
|
// reads by itself, and so if the place is uninitialized we wouldn't know. In order to
|
||||||
|
// disallow the following:
|
||||||
|
// ```rust
|
||||||
|
// let x: !;
|
||||||
|
// match x {}
|
||||||
|
// ```
|
||||||
|
// we add a dummy read on the place.
|
||||||
|
//
|
||||||
|
// NOTE: If we require never patterns for empty matches, those will check that the place
|
||||||
|
// is initialized, and so this read would no longer be needed.
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
otherwise_block
|
||||||
|
}
|
||||||
|
|
||||||
/// The main match algorithm. It begins with a set of candidates
|
/// The main match algorithm. It begins with a set of candidates
|
||||||
/// `candidates` and has the job of generating code to determine
|
/// `candidates` and has the job of generating code to determine
|
||||||
/// which of these candidates, if any, is the correct one. The
|
/// which of these candidates, if any, is the correct one. The
|
||||||
|
@ -1998,52 +1992,50 @@ fn test_candidates<'pat, 'b, 'c>(
|
||||||
|
|
||||||
impl<'a, 'tcx> Builder<'a, 'tcx> {
|
impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
/// If the bindings have already been declared, set `declare_bindings` to
|
/// If the bindings have already been declared, set `declare_bindings` to
|
||||||
/// `false` to avoid duplicated bindings declaration. Used for if-let guards.
|
/// `false` to avoid duplicated bindings declaration; used for if-let guards.
|
||||||
pub(crate) fn lower_let_expr(
|
pub(crate) fn lower_let_expr(
|
||||||
&mut self,
|
&mut self,
|
||||||
mut block: BasicBlock,
|
mut block: BasicBlock,
|
||||||
expr_id: ExprId,
|
expr_id: ExprId,
|
||||||
pat: &Pat<'tcx>,
|
pat: &Pat<'tcx>,
|
||||||
source_scope: Option<SourceScope>,
|
source_scope: Option<SourceScope>,
|
||||||
span: Span,
|
scope_span: Span,
|
||||||
declare_bindings: bool,
|
declare_bindings: bool,
|
||||||
|
storages_alive: bool,
|
||||||
) -> BlockAnd<()> {
|
) -> BlockAnd<()> {
|
||||||
let expr_span = self.thir[expr_id].span;
|
let expr_span = self.thir[expr_id].span;
|
||||||
let expr_place_builder = unpack!(block = self.lower_scrutinee(block, expr_id, expr_span));
|
let scrutinee = unpack!(block = self.lower_scrutinee(block, expr_id, expr_span));
|
||||||
let wildcard = Pat::wildcard_from_ty(pat.ty);
|
let mut candidate = Candidate::new(scrutinee.clone(), pat, false, self);
|
||||||
let mut guard_candidate = Candidate::new(expr_place_builder.clone(), pat, false, self);
|
let otherwise_block = self.lower_match_tree(
|
||||||
let mut otherwise_candidate =
|
|
||||||
Candidate::new(expr_place_builder.clone(), &wildcard, false, self);
|
|
||||||
let fake_borrow_temps = self.lower_match_tree(
|
|
||||||
block,
|
block,
|
||||||
|
expr_span,
|
||||||
|
&scrutinee,
|
||||||
pat.span,
|
pat.span,
|
||||||
&expr_place_builder,
|
&mut [&mut candidate],
|
||||||
pat.span,
|
true,
|
||||||
false,
|
|
||||||
&mut [&mut guard_candidate, &mut otherwise_candidate],
|
|
||||||
);
|
);
|
||||||
let expr_place = expr_place_builder.try_to_place(self);
|
|
||||||
let opt_expr_place = expr_place.as_ref().map(|place| (Some(place), expr_span));
|
self.break_for_else(otherwise_block, self.source_info(expr_span));
|
||||||
let otherwise_post_guard_block = otherwise_candidate.pre_binding_block.unwrap();
|
|
||||||
self.break_for_else(otherwise_post_guard_block, self.source_info(expr_span));
|
|
||||||
|
|
||||||
if declare_bindings {
|
if declare_bindings {
|
||||||
self.declare_bindings(source_scope, pat.span.to(span), pat, None, opt_expr_place);
|
let expr_place = scrutinee.try_to_place(self);
|
||||||
|
let opt_expr_place = expr_place.as_ref().map(|place| (Some(place), expr_span));
|
||||||
|
self.declare_bindings(source_scope, pat.span.to(scope_span), pat, None, opt_expr_place);
|
||||||
}
|
}
|
||||||
|
|
||||||
let post_guard_block = self.bind_pattern(
|
let success = self.bind_pattern(
|
||||||
self.source_info(pat.span),
|
self.source_info(pat.span),
|
||||||
guard_candidate,
|
candidate,
|
||||||
fake_borrow_temps.as_slice(),
|
&[],
|
||||||
expr_span,
|
expr_span,
|
||||||
None,
|
None,
|
||||||
false,
|
storages_alive,
|
||||||
);
|
);
|
||||||
|
|
||||||
// If branch coverage is enabled, record this branch.
|
// If branch coverage is enabled, record this branch.
|
||||||
self.visit_coverage_conditional_let(pat, post_guard_block, otherwise_post_guard_block);
|
self.visit_coverage_conditional_let(pat, success, otherwise_block);
|
||||||
|
|
||||||
post_guard_block.unit()
|
success.unit()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Initializes each of the bindings from the candidate by
|
/// Initializes each of the bindings from the candidate by
|
||||||
|
@ -2091,14 +2083,15 @@ fn bind_and_guard_matched_candidate<'pat>(
|
||||||
return self.cfg.start_new_block();
|
return self.cfg.start_new_block();
|
||||||
}
|
}
|
||||||
|
|
||||||
self.ascribe_types(
|
let ascriptions = parent_data
|
||||||
block,
|
.iter()
|
||||||
parent_data
|
.flat_map(|d| &d.ascriptions)
|
||||||
.iter()
|
.cloned()
|
||||||
.flat_map(|d| &d.ascriptions)
|
.chain(candidate.extra_data.ascriptions);
|
||||||
.cloned()
|
let bindings =
|
||||||
.chain(candidate.extra_data.ascriptions),
|
parent_data.iter().flat_map(|d| &d.bindings).chain(&candidate.extra_data.bindings);
|
||||||
);
|
|
||||||
|
self.ascribe_types(block, ascriptions);
|
||||||
|
|
||||||
// rust-lang/rust#27282: The `autoref` business deserves some
|
// rust-lang/rust#27282: The `autoref` business deserves some
|
||||||
// explanation here.
|
// explanation here.
|
||||||
|
@ -2185,12 +2178,11 @@ fn bind_and_guard_matched_candidate<'pat>(
|
||||||
&& let Some(guard) = arm.guard
|
&& let Some(guard) = arm.guard
|
||||||
{
|
{
|
||||||
let tcx = self.tcx;
|
let tcx = self.tcx;
|
||||||
let bindings =
|
|
||||||
parent_data.iter().flat_map(|d| &d.bindings).chain(&candidate.extra_data.bindings);
|
|
||||||
|
|
||||||
self.bind_matched_candidate_for_guard(block, schedule_drops, bindings.clone());
|
self.bind_matched_candidate_for_guard(block, schedule_drops, bindings.clone());
|
||||||
let guard_frame =
|
let guard_frame = GuardFrame {
|
||||||
GuardFrame { locals: bindings.map(|b| GuardFrameLocal::new(b.var_id)).collect() };
|
locals: bindings.clone().map(|b| GuardFrameLocal::new(b.var_id)).collect(),
|
||||||
|
};
|
||||||
debug!("entering guard building context: {:?}", guard_frame);
|
debug!("entering guard building context: {:?}", guard_frame);
|
||||||
self.guard_context.push(guard_frame);
|
self.guard_context.push(guard_frame);
|
||||||
|
|
||||||
|
@ -2263,11 +2255,8 @@ fn bind_and_guard_matched_candidate<'pat>(
|
||||||
// ```
|
// ```
|
||||||
//
|
//
|
||||||
// and that is clearly not correct.
|
// and that is clearly not correct.
|
||||||
let by_value_bindings = parent_data
|
let by_value_bindings =
|
||||||
.iter()
|
bindings.filter(|binding| matches!(binding.binding_mode.0, ByRef::No));
|
||||||
.flat_map(|d| &d.bindings)
|
|
||||||
.chain(&candidate.extra_data.bindings)
|
|
||||||
.filter(|binding| matches!(binding.binding_mode.0, ByRef::No));
|
|
||||||
// Read all of the by reference bindings to ensure that the
|
// Read all of the by reference bindings to ensure that the
|
||||||
// place they refer to can't be modified by the guard.
|
// place they refer to can't be modified by the guard.
|
||||||
for binding in by_value_bindings.clone() {
|
for binding in by_value_bindings.clone() {
|
||||||
|
@ -2291,7 +2280,7 @@ fn bind_and_guard_matched_candidate<'pat>(
|
||||||
self.bind_matched_candidate_for_arm_body(
|
self.bind_matched_candidate_for_arm_body(
|
||||||
block,
|
block,
|
||||||
schedule_drops,
|
schedule_drops,
|
||||||
parent_data.iter().flat_map(|d| &d.bindings).chain(&candidate.extra_data.bindings),
|
bindings,
|
||||||
storages_alive,
|
storages_alive,
|
||||||
);
|
);
|
||||||
block
|
block
|
||||||
|
@ -2493,55 +2482,4 @@ fn declare_binding(
|
||||||
debug!(?locals);
|
debug!(?locals);
|
||||||
self.var_indices.insert(var_id, locals);
|
self.var_indices.insert(var_id, locals);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn ast_let_else(
|
|
||||||
&mut self,
|
|
||||||
mut block: BasicBlock,
|
|
||||||
init_id: ExprId,
|
|
||||||
initializer_span: Span,
|
|
||||||
else_block: BlockId,
|
|
||||||
let_else_scope: ®ion::Scope,
|
|
||||||
pattern: &Pat<'tcx>,
|
|
||||||
) -> BlockAnd<BasicBlock> {
|
|
||||||
let else_block_span = self.thir[else_block].span;
|
|
||||||
let (matching, failure) = self.in_if_then_scope(*let_else_scope, else_block_span, |this| {
|
|
||||||
let scrutinee = unpack!(block = this.lower_scrutinee(block, init_id, initializer_span));
|
|
||||||
let pat = Pat { ty: pattern.ty, span: else_block_span, kind: PatKind::Wild };
|
|
||||||
let mut wildcard = Candidate::new(scrutinee.clone(), &pat, false, this);
|
|
||||||
let mut candidate = Candidate::new(scrutinee.clone(), pattern, false, this);
|
|
||||||
let fake_borrow_temps = this.lower_match_tree(
|
|
||||||
block,
|
|
||||||
initializer_span,
|
|
||||||
&scrutinee,
|
|
||||||
pattern.span,
|
|
||||||
false,
|
|
||||||
&mut [&mut candidate, &mut wildcard],
|
|
||||||
);
|
|
||||||
// This block is for the matching case
|
|
||||||
let matching = this.bind_pattern(
|
|
||||||
this.source_info(pattern.span),
|
|
||||||
candidate,
|
|
||||||
fake_borrow_temps.as_slice(),
|
|
||||||
initializer_span,
|
|
||||||
None,
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
// This block is for the failure case
|
|
||||||
let failure = this.bind_pattern(
|
|
||||||
this.source_info(else_block_span),
|
|
||||||
wildcard,
|
|
||||||
fake_borrow_temps.as_slice(),
|
|
||||||
initializer_span,
|
|
||||||
None,
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
|
|
||||||
// If branch coverage is enabled, record this branch.
|
|
||||||
this.visit_coverage_conditional_let(pattern, matching, failure);
|
|
||||||
|
|
||||||
this.break_for_else(failure, this.source_info(initializer_span));
|
|
||||||
matching.unit()
|
|
||||||
});
|
|
||||||
matching.and(failure)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
use crate::build::expr::as_place::{PlaceBase, PlaceBuilder};
|
use crate::build::expr::as_place::{PlaceBase, PlaceBuilder};
|
||||||
use crate::build::matches::{Binding, Candidate, FlatPat, MatchPair, TestCase};
|
use crate::build::matches::{Binding, Candidate, FlatPat, MatchPair, TestCase};
|
||||||
use crate::build::Builder;
|
use crate::build::Builder;
|
||||||
|
@ -269,18 +271,6 @@ pub(in crate::build) fn new(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) struct FakeBorrowCollector<'a, 'b, 'tcx> {
|
|
||||||
cx: &'a mut Builder<'b, '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.
|
/// 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.
|
/// Returns a list of places that need a fake borrow along with a local to store it.
|
||||||
|
@ -344,6 +334,18 @@ pub(super) fn collect_fake_borrows<'tcx>(
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) struct FakeBorrowCollector<'a, 'b, 'tcx> {
|
||||||
|
cx: &'a mut Builder<'b, '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>,
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a, 'b, 'tcx> FakeBorrowCollector<'a, 'b, 'tcx> {
|
impl<'a, 'b, 'tcx> FakeBorrowCollector<'a, 'b, 'tcx> {
|
||||||
// Fake borrow this place and its dereference prefixes.
|
// Fake borrow this place and its dereference prefixes.
|
||||||
fn fake_borrow(&mut self, place: Place<'tcx>, kind: FakeBorrowKind) {
|
fn fake_borrow(&mut self, place: Place<'tcx>, kind: FakeBorrowKind) {
|
||||||
|
@ -457,6 +459,57 @@ fn visit_binding(&mut self, Binding { source, .. }: &Binding<'tcx>) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Visit all the bindings of these candidates. Because or-alternatives bind the same variables, we
|
||||||
|
/// only explore the first one of each or-pattern.
|
||||||
|
pub(super) fn visit_bindings<'tcx>(
|
||||||
|
candidates: &[&mut Candidate<'_, 'tcx>],
|
||||||
|
f: impl FnMut(&Binding<'tcx>),
|
||||||
|
) {
|
||||||
|
let mut visitor = BindingsVisitor { f, phantom: PhantomData };
|
||||||
|
for candidate in candidates.iter() {
|
||||||
|
visitor.visit_candidate(candidate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) struct BindingsVisitor<'tcx, F> {
|
||||||
|
f: F,
|
||||||
|
phantom: PhantomData<&'tcx ()>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'tcx, F> BindingsVisitor<'tcx, F>
|
||||||
|
where
|
||||||
|
F: FnMut(&Binding<'tcx>),
|
||||||
|
{
|
||||||
|
fn visit_candidate(&mut self, candidate: &Candidate<'_, 'tcx>) {
|
||||||
|
for binding in &candidate.extra_data.bindings {
|
||||||
|
(self.f)(binding)
|
||||||
|
}
|
||||||
|
for match_pair in &candidate.match_pairs {
|
||||||
|
self.visit_match_pair(match_pair);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_flat_pat(&mut self, flat_pat: &FlatPat<'_, 'tcx>) {
|
||||||
|
for binding in &flat_pat.extra_data.bindings {
|
||||||
|
(self.f)(binding)
|
||||||
|
}
|
||||||
|
for match_pair in &flat_pat.match_pairs {
|
||||||
|
self.visit_match_pair(match_pair);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_match_pair(&mut self, match_pair: &MatchPair<'_, 'tcx>) {
|
||||||
|
if let TestCase::Or { pats, .. } = &match_pair.test_case {
|
||||||
|
// All the or-alternatives should bind the same locals, so we only visit the first one.
|
||||||
|
self.visit_flat_pat(&pats[0])
|
||||||
|
} else {
|
||||||
|
for subpair in &match_pair.subpairs {
|
||||||
|
self.visit_match_pair(subpair);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub(crate) fn ref_pat_borrow_kind(ref_mutability: Mutability) -> BorrowKind {
|
pub(crate) fn ref_pat_borrow_kind(ref_mutability: Mutability) -> BorrowKind {
|
||||||
match ref_mutability {
|
match ref_mutability {
|
||||||
|
|
|
@ -27,13 +27,13 @@ fn main() -> () {
|
||||||
StorageLive(_5);
|
StorageLive(_5);
|
||||||
PlaceMention(_1);
|
PlaceMention(_1);
|
||||||
_6 = discriminant(_1);
|
_6 = discriminant(_1);
|
||||||
switchInt(move _6) -> [1: bb6, otherwise: bb4];
|
switchInt(move _6) -> [1: bb4, otherwise: bb3];
|
||||||
}
|
}
|
||||||
|
|
||||||
bb1: {
|
bb1: {
|
||||||
StorageLive(_3);
|
StorageLive(_3);
|
||||||
StorageLive(_4);
|
StorageLive(_4);
|
||||||
_4 = begin_panic::<&str>(const "explicit panic") -> bb10;
|
_4 = begin_panic::<&str>(const "explicit panic") -> bb8;
|
||||||
}
|
}
|
||||||
|
|
||||||
bb2: {
|
bb2: {
|
||||||
|
@ -43,12 +43,11 @@ fn main() -> () {
|
||||||
}
|
}
|
||||||
|
|
||||||
bb3: {
|
bb3: {
|
||||||
FakeRead(ForMatchedPlace(None), _1);
|
goto -> bb7;
|
||||||
unreachable;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bb4: {
|
bb4: {
|
||||||
goto -> bb9;
|
falseEdge -> [real: bb6, imaginary: bb3];
|
||||||
}
|
}
|
||||||
|
|
||||||
bb5: {
|
bb5: {
|
||||||
|
@ -56,14 +55,6 @@ fn main() -> () {
|
||||||
}
|
}
|
||||||
|
|
||||||
bb6: {
|
bb6: {
|
||||||
falseEdge -> [real: bb8, imaginary: bb4];
|
|
||||||
}
|
|
||||||
|
|
||||||
bb7: {
|
|
||||||
goto -> bb4;
|
|
||||||
}
|
|
||||||
|
|
||||||
bb8: {
|
|
||||||
_5 = ((_1 as Some).0: u8);
|
_5 = ((_1 as Some).0: u8);
|
||||||
_0 = const ();
|
_0 = const ();
|
||||||
StorageDead(_5);
|
StorageDead(_5);
|
||||||
|
@ -71,12 +62,12 @@ fn main() -> () {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bb9: {
|
bb7: {
|
||||||
StorageDead(_5);
|
StorageDead(_5);
|
||||||
goto -> bb1;
|
goto -> bb1;
|
||||||
}
|
}
|
||||||
|
|
||||||
bb10 (cleanup): {
|
bb8 (cleanup): {
|
||||||
resume;
|
resume;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,22 +19,21 @@ fn test_complex() -> () {
|
||||||
bb0: {
|
bb0: {
|
||||||
StorageLive(_1);
|
StorageLive(_1);
|
||||||
StorageLive(_2);
|
StorageLive(_2);
|
||||||
_2 = E::f() -> [return: bb1, unwind: bb38];
|
_2 = E::f() -> [return: bb1, unwind: bb34];
|
||||||
}
|
}
|
||||||
|
|
||||||
bb1: {
|
bb1: {
|
||||||
PlaceMention(_2);
|
PlaceMention(_2);
|
||||||
_3 = discriminant(_2);
|
_3 = discriminant(_2);
|
||||||
switchInt(move _3) -> [0: bb5, otherwise: bb3];
|
switchInt(move _3) -> [0: bb3, otherwise: bb2];
|
||||||
}
|
}
|
||||||
|
|
||||||
bb2: {
|
bb2: {
|
||||||
FakeRead(ForMatchedPlace(None), _2);
|
goto -> bb21;
|
||||||
unreachable;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bb3: {
|
bb3: {
|
||||||
goto -> bb23;
|
falseEdge -> [real: bb5, imaginary: bb2];
|
||||||
}
|
}
|
||||||
|
|
||||||
bb4: {
|
bb4: {
|
||||||
|
@ -42,175 +41,158 @@ fn test_complex() -> () {
|
||||||
}
|
}
|
||||||
|
|
||||||
bb5: {
|
bb5: {
|
||||||
falseEdge -> [real: bb7, imaginary: bb3];
|
StorageLive(_4);
|
||||||
|
_4 = always_true() -> [return: bb6, unwind: bb34];
|
||||||
}
|
}
|
||||||
|
|
||||||
bb6: {
|
bb6: {
|
||||||
goto -> bb3;
|
switchInt(move _4) -> [0: bb8, otherwise: bb7];
|
||||||
}
|
}
|
||||||
|
|
||||||
bb7: {
|
bb7: {
|
||||||
StorageLive(_4);
|
|
||||||
_4 = always_true() -> [return: bb8, unwind: bb38];
|
|
||||||
}
|
|
||||||
|
|
||||||
bb8: {
|
|
||||||
switchInt(move _4) -> [0: bb10, otherwise: bb9];
|
|
||||||
}
|
|
||||||
|
|
||||||
bb9: {
|
|
||||||
StorageLive(_5);
|
StorageLive(_5);
|
||||||
StorageLive(_6);
|
StorageLive(_6);
|
||||||
StorageLive(_7);
|
StorageLive(_7);
|
||||||
_7 = Droppy(const 0_u8);
|
_7 = Droppy(const 0_u8);
|
||||||
_6 = (_7.0: u8);
|
_6 = (_7.0: u8);
|
||||||
_5 = Gt(move _6, const 0_u8);
|
_5 = Gt(move _6, const 0_u8);
|
||||||
switchInt(move _5) -> [0: bb12, otherwise: bb11];
|
switchInt(move _5) -> [0: bb10, otherwise: bb9];
|
||||||
|
}
|
||||||
|
|
||||||
|
bb8: {
|
||||||
|
goto -> bb14;
|
||||||
|
}
|
||||||
|
|
||||||
|
bb9: {
|
||||||
|
drop(_7) -> [return: bb11, unwind: bb34];
|
||||||
}
|
}
|
||||||
|
|
||||||
bb10: {
|
bb10: {
|
||||||
goto -> bb16;
|
goto -> bb12;
|
||||||
}
|
}
|
||||||
|
|
||||||
bb11: {
|
bb11: {
|
||||||
drop(_7) -> [return: bb13, unwind: bb38];
|
StorageDead(_7);
|
||||||
|
StorageDead(_6);
|
||||||
|
goto -> bb18;
|
||||||
}
|
}
|
||||||
|
|
||||||
bb12: {
|
bb12: {
|
||||||
goto -> bb14;
|
drop(_7) -> [return: bb13, unwind: bb34];
|
||||||
}
|
}
|
||||||
|
|
||||||
bb13: {
|
bb13: {
|
||||||
StorageDead(_7);
|
StorageDead(_7);
|
||||||
StorageDead(_6);
|
StorageDead(_6);
|
||||||
goto -> bb20;
|
goto -> bb14;
|
||||||
}
|
}
|
||||||
|
|
||||||
bb14: {
|
bb14: {
|
||||||
drop(_7) -> [return: bb15, unwind: bb38];
|
|
||||||
}
|
|
||||||
|
|
||||||
bb15: {
|
|
||||||
StorageDead(_7);
|
|
||||||
StorageDead(_6);
|
|
||||||
goto -> bb16;
|
|
||||||
}
|
|
||||||
|
|
||||||
bb16: {
|
|
||||||
StorageLive(_8);
|
StorageLive(_8);
|
||||||
StorageLive(_9);
|
StorageLive(_9);
|
||||||
StorageLive(_10);
|
StorageLive(_10);
|
||||||
_10 = Droppy(const 1_u8);
|
_10 = Droppy(const 1_u8);
|
||||||
_9 = (_10.0: u8);
|
_9 = (_10.0: u8);
|
||||||
_8 = Gt(move _9, const 1_u8);
|
_8 = Gt(move _9, const 1_u8);
|
||||||
switchInt(move _8) -> [0: bb18, otherwise: bb17];
|
switchInt(move _8) -> [0: bb16, otherwise: bb15];
|
||||||
|
}
|
||||||
|
|
||||||
|
bb15: {
|
||||||
|
drop(_10) -> [return: bb17, unwind: bb34];
|
||||||
|
}
|
||||||
|
|
||||||
|
bb16: {
|
||||||
|
goto -> bb19;
|
||||||
}
|
}
|
||||||
|
|
||||||
bb17: {
|
bb17: {
|
||||||
drop(_10) -> [return: bb19, unwind: bb38];
|
StorageDead(_10);
|
||||||
|
StorageDead(_9);
|
||||||
|
goto -> bb18;
|
||||||
}
|
}
|
||||||
|
|
||||||
bb18: {
|
bb18: {
|
||||||
goto -> bb21;
|
_1 = const ();
|
||||||
|
goto -> bb22;
|
||||||
}
|
}
|
||||||
|
|
||||||
bb19: {
|
bb19: {
|
||||||
StorageDead(_10);
|
drop(_10) -> [return: bb20, unwind: bb34];
|
||||||
StorageDead(_9);
|
|
||||||
goto -> bb20;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bb20: {
|
bb20: {
|
||||||
_1 = const ();
|
StorageDead(_10);
|
||||||
goto -> bb24;
|
StorageDead(_9);
|
||||||
|
goto -> bb21;
|
||||||
}
|
}
|
||||||
|
|
||||||
bb21: {
|
bb21: {
|
||||||
drop(_10) -> [return: bb22, unwind: bb38];
|
_1 = const ();
|
||||||
|
goto -> bb22;
|
||||||
}
|
}
|
||||||
|
|
||||||
bb22: {
|
bb22: {
|
||||||
StorageDead(_10);
|
|
||||||
StorageDead(_9);
|
|
||||||
goto -> bb23;
|
|
||||||
}
|
|
||||||
|
|
||||||
bb23: {
|
|
||||||
_1 = const ();
|
|
||||||
goto -> bb24;
|
|
||||||
}
|
|
||||||
|
|
||||||
bb24: {
|
|
||||||
StorageDead(_8);
|
StorageDead(_8);
|
||||||
StorageDead(_5);
|
StorageDead(_5);
|
||||||
StorageDead(_4);
|
StorageDead(_4);
|
||||||
StorageDead(_2);
|
StorageDead(_2);
|
||||||
StorageDead(_1);
|
StorageDead(_1);
|
||||||
StorageLive(_11);
|
StorageLive(_11);
|
||||||
_11 = always_true() -> [return: bb25, unwind: bb38];
|
_11 = always_true() -> [return: bb23, unwind: bb34];
|
||||||
|
}
|
||||||
|
|
||||||
|
bb23: {
|
||||||
|
switchInt(move _11) -> [0: bb25, otherwise: bb24];
|
||||||
|
}
|
||||||
|
|
||||||
|
bb24: {
|
||||||
|
goto -> bb32;
|
||||||
}
|
}
|
||||||
|
|
||||||
bb25: {
|
bb25: {
|
||||||
switchInt(move _11) -> [0: bb27, otherwise: bb26];
|
goto -> bb26;
|
||||||
}
|
}
|
||||||
|
|
||||||
bb26: {
|
bb26: {
|
||||||
goto -> bb36;
|
StorageLive(_12);
|
||||||
|
_12 = E::f() -> [return: bb27, unwind: bb34];
|
||||||
}
|
}
|
||||||
|
|
||||||
bb27: {
|
bb27: {
|
||||||
goto -> bb28;
|
PlaceMention(_12);
|
||||||
|
_13 = discriminant(_12);
|
||||||
|
switchInt(move _13) -> [1: bb29, otherwise: bb28];
|
||||||
}
|
}
|
||||||
|
|
||||||
bb28: {
|
bb28: {
|
||||||
StorageLive(_12);
|
goto -> bb32;
|
||||||
_12 = E::f() -> [return: bb29, unwind: bb38];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bb29: {
|
bb29: {
|
||||||
PlaceMention(_12);
|
falseEdge -> [real: bb31, imaginary: bb28];
|
||||||
_13 = discriminant(_12);
|
|
||||||
switchInt(move _13) -> [1: bb33, otherwise: bb31];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bb30: {
|
bb30: {
|
||||||
FakeRead(ForMatchedPlace(None), _12);
|
goto -> bb28;
|
||||||
unreachable;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bb31: {
|
bb31: {
|
||||||
goto -> bb36;
|
_0 = const ();
|
||||||
|
goto -> bb33;
|
||||||
}
|
}
|
||||||
|
|
||||||
bb32: {
|
bb32: {
|
||||||
goto -> bb30;
|
_0 = const ();
|
||||||
|
goto -> bb33;
|
||||||
}
|
}
|
||||||
|
|
||||||
bb33: {
|
bb33: {
|
||||||
falseEdge -> [real: bb35, imaginary: bb31];
|
|
||||||
}
|
|
||||||
|
|
||||||
bb34: {
|
|
||||||
goto -> bb31;
|
|
||||||
}
|
|
||||||
|
|
||||||
bb35: {
|
|
||||||
_0 = const ();
|
|
||||||
goto -> bb37;
|
|
||||||
}
|
|
||||||
|
|
||||||
bb36: {
|
|
||||||
_0 = const ();
|
|
||||||
goto -> bb37;
|
|
||||||
}
|
|
||||||
|
|
||||||
bb37: {
|
|
||||||
StorageDead(_11);
|
StorageDead(_11);
|
||||||
StorageDead(_12);
|
StorageDead(_12);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bb38 (cleanup): {
|
bb34 (cleanup): {
|
||||||
resume;
|
resume;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue