From 33bc396ff721d9beb9a918099e71852b734a0790 Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Mon, 6 May 2019 15:18:57 +0100 Subject: [PATCH 1/7] Make `FalseEdges` always have two targets We never have more than one imaginary target, so we have no reason for a `Vec` --- src/librustc/mir/mod.rs | 21 ++++++++----------- src/librustc_mir/borrow_check/mod.rs | 2 +- .../borrow_check/nll/invalidation.rs | 2 +- .../borrow_check/nll/type_check/mod.rs | 6 ++---- src/librustc_mir/build/matches/mod.rs | 10 ++++----- src/librustc_mir/dataflow/mod.rs | 6 ++---- src/librustc_mir/transform/inline.rs | 6 ++---- 7 files changed, 21 insertions(+), 32 deletions(-) diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index e73023d9a8cc9..f18afe7b6327c 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -1196,9 +1196,9 @@ pub enum TerminatorKind<'tcx> { FalseEdges { /// The target normal control flow will take real_target: BasicBlock, - /// The list of blocks control flow could conceptually take, but won't + /// A block control flow could conceptually take, but won't /// in practice - imaginary_targets: Vec, + imaginary_target: BasicBlock, }, /// A terminator for blocks that only take one path in reality, but where we /// reserve the right to unwind in borrowck, even if it won't happen in practice. @@ -1335,8 +1335,8 @@ impl<'tcx> TerminatorKind<'tcx> { SwitchInt { ref targets, .. } => None.into_iter().chain(&targets[..]), FalseEdges { ref real_target, - ref imaginary_targets, - } => Some(real_target).into_iter().chain(&imaginary_targets[..]), + ref imaginary_target, + } => Some(real_target).into_iter().chain(slice::from_ref(imaginary_target)), } } @@ -1422,10 +1422,10 @@ impl<'tcx> TerminatorKind<'tcx> { } => None.into_iter().chain(&mut targets[..]), FalseEdges { ref mut real_target, - ref mut imaginary_targets, + ref mut imaginary_target, } => Some(real_target) .into_iter() - .chain(&mut imaginary_targets[..]), + .chain(slice::from_mut(imaginary_target)), } } @@ -1722,12 +1722,9 @@ impl<'tcx> TerminatorKind<'tcx> { Assert { cleanup: None, .. } => vec!["".into()], Assert { .. } => vec!["success".into(), "unwind".into()], FalseEdges { - ref imaginary_targets, .. } => { - let mut l = vec!["real".into()]; - l.resize(imaginary_targets.len() + 1, "imaginary".into()); - l + vec!["real".into(), "imaginary".into()] } FalseUnwind { unwind: Some(_), .. @@ -3356,10 +3353,10 @@ impl<'tcx> TypeFoldable<'tcx> for Terminator<'tcx> { Unreachable => Unreachable, FalseEdges { real_target, - ref imaginary_targets, + imaginary_target, } => FalseEdges { real_target, - imaginary_targets: imaginary_targets.clone(), + imaginary_target, }, FalseUnwind { real_target, diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs index 60ad8921c0b3d..335db27d2356e 100644 --- a/src/librustc_mir/borrow_check/mod.rs +++ b/src/librustc_mir/borrow_check/mod.rs @@ -780,7 +780,7 @@ impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx | TerminatorKind::Unreachable | TerminatorKind::FalseEdges { real_target: _, - imaginary_targets: _, + imaginary_target: _, } | TerminatorKind::FalseUnwind { real_target: _, diff --git a/src/librustc_mir/borrow_check/nll/invalidation.rs b/src/librustc_mir/borrow_check/nll/invalidation.rs index 12433ca365d68..7933fb80a3642 100644 --- a/src/librustc_mir/borrow_check/nll/invalidation.rs +++ b/src/librustc_mir/borrow_check/nll/invalidation.rs @@ -244,7 +244,7 @@ impl<'cx, 'tcx, 'gcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx, 'gcx> { | TerminatorKind::Unreachable | TerminatorKind::FalseEdges { real_target: _, - imaginary_targets: _, + imaginary_target: _, } | TerminatorKind::FalseUnwind { real_target: _, diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index bbfffc68b9692..104a8cb4894bf 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -1792,12 +1792,10 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { } TerminatorKind::FalseEdges { real_target, - ref imaginary_targets, + imaginary_target, } => { self.assert_iscleanup(body, block_data, real_target, is_cleanup); - for target in imaginary_targets { - self.assert_iscleanup(body, block_data, *target, is_cleanup); - } + self.assert_iscleanup(body, block_data, imaginary_target, is_cleanup); } TerminatorKind::FalseUnwind { real_target, diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs index 0aabebcf9dc2c..353842730c7e7 100644 --- a/src/librustc_mir/build/matches/mod.rs +++ b/src/librustc_mir/build/matches/mod.rs @@ -961,9 +961,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { source_info, TerminatorKind::FalseEdges { real_target: second_candidate.pre_binding_block, - imaginary_targets: vec![ - first_candidate.next_candidate_pre_binding_block - ], + imaginary_target: first_candidate.next_candidate_pre_binding_block, } ) } else { @@ -984,7 +982,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { source_info, TerminatorKind::FalseEdges { real_target: unreachable, - imaginary_targets: vec![candidate.next_candidate_pre_binding_block], + imaginary_targets: candidate.next_candidate_pre_binding_block, } ); self.cfg.terminate(unreachable, source_info, TerminatorKind::Unreachable); @@ -1001,7 +999,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { source_info, TerminatorKind::FalseEdges { real_target: block, - imaginary_targets: vec![last_candidate.next_candidate_pre_binding_block] + imaginary_target: last_candidate.next_candidate_pre_binding_block, } ); Some(block) @@ -1330,7 +1328,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { candidate_source_info, TerminatorKind::FalseEdges { real_target: block, - imaginary_targets: vec![candidate.next_candidate_pre_binding_block], + imaginary_target: candidate.next_candidate_pre_binding_block, }, ); self.ascribe_types(block, &candidate.ascriptions); diff --git a/src/librustc_mir/dataflow/mod.rs b/src/librustc_mir/dataflow/mod.rs index 10e46507d5a26..9350881f1e1fc 100644 --- a/src/librustc_mir/dataflow/mod.rs +++ b/src/librustc_mir/dataflow/mod.rs @@ -802,11 +802,9 @@ impl<'a, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D> where D: BitDenotation<'tcx> self.propagate_bits_into_entry_set_for(in_out, dest_bb, dirty_list); } } - mir::TerminatorKind::FalseEdges { real_target, ref imaginary_targets } => { + mir::TerminatorKind::FalseEdges { real_target, imaginary_target } => { self.propagate_bits_into_entry_set_for(in_out, real_target, dirty_list); - for target in imaginary_targets { - self.propagate_bits_into_entry_set_for(in_out, *target, dirty_list); - } + self.propagate_bits_into_entry_set_for(in_out, imaginary_target, dirty_list); } mir::TerminatorKind::FalseUnwind { real_target, unwind } => { self.propagate_bits_into_entry_set_for(in_out, real_target, dirty_list); diff --git a/src/librustc_mir/transform/inline.rs b/src/librustc_mir/transform/inline.rs index 55c035f2858a2..e20d5eb2e3d36 100644 --- a/src/librustc_mir/transform/inline.rs +++ b/src/librustc_mir/transform/inline.rs @@ -791,11 +791,9 @@ impl<'a, 'tcx> MutVisitor<'tcx> for Integrator<'a, 'tcx> { } TerminatorKind::Abort => { } TerminatorKind::Unreachable => { } - TerminatorKind::FalseEdges { ref mut real_target, ref mut imaginary_targets } => { + TerminatorKind::FalseEdges { ref mut real_target, ref mut imaginary_target } => { *real_target = self.update_target(*real_target); - for target in imaginary_targets { - *target = self.update_target(*target); - } + *imaginary_target = self.update_target(*imaginary_target); } TerminatorKind::FalseUnwind { real_target: _ , unwind: _ } => // see the ordering of passes in the optimized_mir query. From 32c337724dd3a1b651cb7ba9769e60262f9f52f8 Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Mon, 6 May 2019 16:13:20 +0100 Subject: [PATCH 2/7] Avoid unnecessary false edges in MIR match lowering --- src/librustc_mir/build/matches/mod.rs | 118 ++++++++++++++----------- src/librustc_mir/build/matches/util.rs | 33 +++++++ 2 files changed, 98 insertions(+), 53 deletions(-) diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs index 353842730c7e7..38c9a177c388e 100644 --- a/src/librustc_mir/build/matches/mod.rs +++ b/src/librustc_mir/build/matches/mod.rs @@ -143,19 +143,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // create binding start block for link them by false edges let candidate_count = arms.iter().map(|c| c.patterns.len()).sum::(); - let pre_binding_blocks: Vec<_> = (0..=candidate_count) + let pre_binding_blocks: Vec<_> = (0..candidate_count) .map(|_| self.cfg.start_new_block()) .collect(); - // There's one more pre_binding block than there are candidates so that - // every candidate can have a `next_candidate_pre_binding_block`. - let outer_source_info = self.source_info(span); - self.cfg.terminate( - *pre_binding_blocks.last().unwrap(), - outer_source_info, - TerminatorKind::Unreachable, - ); - let mut match_has_guard = false; let mut candidate_pre_binding_blocks = pre_binding_blocks.iter(); @@ -171,9 +162,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { let arm_candidates: Vec<_> = arm.patterns .iter() .zip(candidate_pre_binding_blocks.by_ref()) - .zip(next_candidate_pre_binding_blocks.by_ref()) .map( - |((pattern, pre_binding_block), next_candidate_pre_binding_block)| { + |(pattern, pre_binding_block)| { Candidate { span: pattern.span, match_pairs: vec![ @@ -188,7 +178,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { }, pre_binding_block: *pre_binding_block, next_candidate_pre_binding_block: - *next_candidate_pre_binding_block, + next_candidate_pre_binding_blocks.next().copied(), } }, ) @@ -225,6 +215,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { &mut fake_borrows, ); + let outer_source_info = self.source_info(span); + if !otherwise.is_empty() { // All matches are exhaustive. However, because some matches // only have exponentially-large exhaustive decision trees, we @@ -251,12 +243,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { }; // Step 5. Create everything else: the guards and the arms. - - let arm_end_blocks: Vec<_> = arm_candidates.into_iter().map(|(arm, candidates)| { + let arm_end_blocks: Vec<_> = arm_candidates.into_iter().map(|(arm, mut candidates)| { let arm_source_info = self.source_info(arm.span); let region_scope = (arm.scope, arm_source_info); self.in_scope(region_scope, arm.lint_level, |this| { - let arm_block = this.cfg.start_new_block(); + let mut arm_block = this.cfg.start_new_block(); let body = this.hir.mirror(arm.body.clone()); let scope = this.declare_bindings( @@ -267,6 +258,30 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { Some((Some(&scrutinee_place), scrutinee_span)), ); + if candidates.len() == 1 { + arm_block = self.bind_and_guard_matched_candidate( + candidates.pop().unwrap(), + arm.guard.clone(), + &fake_borrow_temps, + scrutinee_span, + ); + } else { + arm_block = self.cfg.start_new_block(); + for candidate in candidates { + let binding_end = self.bind_and_guard_matched_candidate( + candidate, + arm.guard.clone(), + &fake_borrow_temps, + scrutinee_span, + ); + self.cfg.terminate( + binding_end, + source_info, + TerminatorKind::Goto { target: arm_block }, + ); + } + } + if let Some(source_scope) = scope { this.source_scope = source_scope; } @@ -434,7 +449,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // since we don't call `match_candidates`, next fields are unused otherwise_block: None, pre_binding_block: block, - next_candidate_pre_binding_block: block, + next_candidate_pre_binding_block: None, }; // Simplify the candidate. Since the pattern is irrefutable, this should @@ -689,7 +704,7 @@ pub struct Candidate<'pat, 'tcx: 'pat> { // ...and the blocks for add false edges between candidates pre_binding_block: BasicBlock, - next_candidate_pre_binding_block: BasicBlock, + next_candidate_pre_binding_block: Option, } #[derive(Clone, Debug)] @@ -956,14 +971,12 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { if let [first_candidate, second_candidate] = window { let source_info = self.source_info(first_candidate.span); if let Some(otherwise_block) = first_candidate.otherwise_block { - self.cfg.terminate( + self.false_edges( otherwise_block, + second_candidate.pre_binding_block, + first_candidate.next_candidate_pre_binding_block, source_info, - TerminatorKind::FalseEdges { - real_target: second_candidate.pre_binding_block, - imaginary_target: first_candidate.next_candidate_pre_binding_block, - } - ) + ); } else { bug!("candidate other than the last has no guard"); } @@ -977,13 +990,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { if let Some(otherwise) = candidate.otherwise_block { let source_info = self.source_info(candidate.span); let unreachable = self.cfg.start_new_block(); - self.cfg.terminate( + self.false_edges( otherwise, + unreachable, + candidate.next_candidate_pre_binding_block, source_info, - TerminatorKind::FalseEdges { - real_target: unreachable, - imaginary_targets: candidate.next_candidate_pre_binding_block, - } ); self.cfg.terminate(unreachable, source_info, TerminatorKind::Unreachable); } @@ -994,13 +1005,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { if let Some(otherwise) = last_candidate.otherwise_block { let source_info = self.source_info(last_candidate.span); let block = self.cfg.start_new_block(); - self.cfg.terminate( + self.false_edges( otherwise, + block, + last_candidate.next_candidate_pre_binding_block, source_info, - TerminatorKind::FalseEdges { - real_target: block, - imaginary_target: last_candidate.next_candidate_pre_binding_block, - } ); Some(block) } else { @@ -1311,27 +1320,38 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { &mut self, candidate: Candidate<'pat, 'tcx>, guard: Option>, - arm_block: BasicBlock, fake_borrows: &Vec<(&Place<'tcx>, Local)>, scrutinee_span: Span, region_scope: (region::Scope, SourceInfo), ) { + ) -> BasicBlock { debug!("bind_and_guard_matched_candidate(candidate={:?})", candidate); debug_assert!(candidate.match_pairs.is_empty()); let candidate_source_info = self.source_info(candidate.span); - let mut block = self.cfg.start_new_block(); - self.cfg.terminate( - candidate.pre_binding_block, + let mut block = candidate.pre_binding_block; + + // If we are adding our own statements, then we need a fresh block. + let create_fresh_block = candidate.next_candidate_pre_binding_block.is_some() + || !candidate.bindings.is_empty() + || !candidate.ascriptions.is_empty() + || guard.is_some(); + + if create_fresh_block { + let fresh_block = self.cfg.start_new_block(); + self.false_edges( + block, + fresh_block, + candidate.next_candidate_pre_binding_block, candidate_source_info, - TerminatorKind::FalseEdges { - real_target: block, - imaginary_target: candidate.next_candidate_pre_binding_block, - }, ); + block = fresh_block; self.ascribe_types(block, &candidate.ascriptions); + } else { + return block; + } // rust-lang/rust#27282: The `autoref` business deserves some // explanation here. @@ -1476,7 +1496,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // because that would be before we've checked the result // from the guard. // - // But binding them on `arm_block` is *too late*, because + // But binding them on the arm is *too late*, because // then all of the candidates for a single arm would be // bound in the same place, that would cause a case like: // @@ -1552,22 +1572,14 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { by_value_bindings, ); - self.cfg.terminate( - post_guard_block, - source_info, - TerminatorKind::Goto { target: arm_block }, - ); + post_guard_block } else { assert!(candidate.otherwise_block.is_none()); // (Here, it is not too early to bind the matched // candidate on `block`, because there is no guard result // that we have to inspect before we bind them.) self.bind_matched_candidate_for_arm_body(block, &candidate.bindings); - self.cfg.terminate( - block, - candidate_source_info, - TerminatorKind::Goto { target: arm_block }, - ); + block } } diff --git a/src/librustc_mir/build/matches/util.rs b/src/librustc_mir/build/matches/util.rs index 3b90ff7884f01..04ae5a87e49e6 100644 --- a/src/librustc_mir/build/matches/util.rs +++ b/src/librustc_mir/build/matches/util.rs @@ -65,6 +65,39 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { }) ); } + + /// Creates a false edge to `imaginary_target` and a real edge to + /// real_target. If `imaginary_target` is none, or is the same as the real + /// target, a Goto is generated instead to simplify the generated MIR. + pub fn false_edges( + &mut self, + from_block: BasicBlock, + real_target: BasicBlock, + imaginary_target: Option, + source_info: SourceInfo, + ) { + match imaginary_target { + Some(target) if target != real_target => { + self.cfg.terminate( + from_block, + source_info, + TerminatorKind::FalseEdges { + real_target, + imaginary_target: target, + }, + ); + } + _ => { + self.cfg.terminate( + from_block, + source_info, + TerminatorKind::Goto { + target: real_target + } + ); + } + } + } } impl<'pat, 'tcx> MatchPair<'pat, 'tcx> { From da22793a35b725e2fe2f7e2ac1d808404c39c4aa Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Thu, 9 May 2019 23:01:39 +0100 Subject: [PATCH 3/7] Create fewer basic blocks in match MIR lowering --- src/librustc_mir/build/matches/mod.rs | 229 ++++----- src/librustc_mir/build/matches/test.rs | 478 ++++++++++-------- src/librustc_mir/build/mod.rs | 11 - src/librustc_mir/transform/qualify_consts.rs | 3 + .../transform/qualify_min_const_fn.rs | 4 + src/test/codegen/match.rs | 4 +- src/test/compile-fail/const-fn-error.rs | 1 + src/test/compile-fail/issue-52443.rs | 1 + src/test/mir-opt/const_prop/switch_int.rs | 8 +- src/test/mir-opt/deaggregator_test_enum_2.rs | 26 +- src/test/mir-opt/issue-38669.rs | 14 +- src/test/mir-opt/issue-49232.rs | 48 +- src/test/mir-opt/loop_test.rs | 13 +- src/test/mir-opt/match-arm-scopes.rs | 150 +++--- src/test/mir-opt/match_false_edges.rs | 134 +++-- src/test/mir-opt/match_test.rs | 48 +- .../mir-opt/nll/region-subtyping-basic.rs | 6 +- src/test/mir-opt/remove_fake_borrows.rs | 56 +- src/test/mir-opt/simplify_if.rs | 4 +- src/test/mir-opt/simplify_match.rs | 4 +- .../borrowck-mut-borrow-linear-errors.stderr | 14 +- .../consts/const-eval/match-test-ptr-null.rs | 1 + .../const-eval/match-test-ptr-null.stderr | 12 +- src/test/ui/consts/const-match-pattern-arm.rs | 2 + .../ui/consts/const-match-pattern-arm.stderr | 18 +- .../ui/consts/single_variant_match_ice.rs | 11 +- .../ui/consts/single_variant_match_ice.stderr | 18 +- src/test/ui/issues/issue-46843.rs | 4 +- src/test/ui/issues/issue-46843.stderr | 10 +- .../liveness-assign-imm-local-notes.stderr | 6 +- 30 files changed, 685 insertions(+), 653 deletions(-) diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs index 38c9a177c388e..521aca56108a6 100644 --- a/src/librustc_mir/build/matches/mod.rs +++ b/src/librustc_mir/build/matches/mod.rs @@ -206,33 +206,18 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { .flat_map(|(_, candidates)| candidates) .collect::>(); + let outer_source_info = self.source_info(span); + // this will generate code to test scrutinee_place and // branch to the appropriate arm block - let otherwise = self.match_candidates( + self.match_candidates( scrutinee_span, + &mut Some(block), + None, candidates, - block, &mut fake_borrows, ); - let outer_source_info = self.source_info(span); - - if !otherwise.is_empty() { - // All matches are exhaustive. However, because some matches - // only have exponentially-large exhaustive decision trees, we - // sometimes generate an inexhaustive decision tree. - // - // In that case, the inexhaustive tips of the decision tree - // can't be reached - terminate them with an `unreachable`. - let mut otherwise = otherwise; - otherwise.sort(); - otherwise.dedup(); // variant switches can introduce duplicate target blocks - for block in otherwise { - self.cfg - .terminate(block, outer_source_info, TerminatorKind::Unreachable); - } - } - // Step 4. Determine the fake borrows that are needed from the above // places. Create the required temporaries for them. @@ -247,8 +232,6 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { let arm_source_info = self.source_info(arm.span); let region_scope = (arm.scope, arm_source_info); self.in_scope(region_scope, arm.lint_level, |this| { - let mut arm_block = this.cfg.start_new_block(); - let body = this.hir.mirror(arm.body.clone()); let scope = this.declare_bindings( None, @@ -258,23 +241,27 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { Some((Some(&scrutinee_place), scrutinee_span)), ); + let arm_block; if candidates.len() == 1 { - arm_block = self.bind_and_guard_matched_candidate( + arm_block = this.bind_and_guard_matched_candidate( candidates.pop().unwrap(), arm.guard.clone(), &fake_borrow_temps, scrutinee_span, + region_scope, ); } else { - arm_block = self.cfg.start_new_block(); + arm_block = this.cfg.start_new_block(); for candidate in candidates { - let binding_end = self.bind_and_guard_matched_candidate( + this.clear_top_scope(arm.scope); + let binding_end = this.bind_and_guard_matched_candidate( candidate, arm.guard.clone(), &fake_borrow_temps, scrutinee_span, + region_scope, ); - self.cfg.terminate( + this.cfg.terminate( binding_end, source_info, TerminatorKind::Goto { target: arm_block }, @@ -286,18 +273,6 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { this.source_scope = source_scope; } - for candidate in candidates { - this.clear_top_scope(arm.scope); - this.bind_and_guard_matched_candidate( - candidate, - arm.guard.clone(), - arm_block, - &fake_borrow_temps, - scrutinee_span, - region_scope, - ); - } - this.into(destination, arm_block, body) }) }).collect(); @@ -792,11 +767,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { /// the value, we will generate a branch to the appropriate /// prebinding block. /// - /// The return value is a list of "otherwise" blocks. These are - /// points in execution where we found that *NONE* of the - /// candidates apply. In principle, this means that the input - /// list was not exhaustive, though at present we sometimes are - /// not smart enough to recognize all exhaustive inputs. + /// If we find that *NONE* of the candidates apply, we branch to the + /// `otherwise_block`. In principle, this means that the input list was not + /// exhaustive, though at present we sometimes are not smart enough to + /// recognize all exhaustive inputs. /// /// It might be surprising that the input can be inexhaustive. /// Indeed, initially, it is not, because all matches are @@ -810,13 +784,17 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { fn match_candidates<'pat>( &mut self, span: Span, + start_block: &mut Option, + otherwise_block: Option, candidates: &mut [&mut Candidate<'pat, 'tcx>], - mut block: BasicBlock, fake_borrows: &mut Option>>, - ) -> Vec { + ) { debug!( - "matched_candidate(span={:?}, block={:?}, candidates={:?})", - span, block, candidates + "matched_candidate(span={:?}, candidates={:?}, start_block={:?}, otherwise_block={:?})", + span, + candidates, + start_block, + otherwise_block, ); // Start by simplifying candidates. Once this process is complete, all @@ -839,52 +817,57 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { ); let (matched_candidates, unmatched_candidates) = candidates.split_at_mut(fully_matched); + let block: BasicBlock; + if !matched_candidates.is_empty() { - block = if let Some(last_otherwise_block) = self.select_matched_candidates( + let otherwise_block = self.select_matched_candidates( matched_candidates, - block, + start_block, fake_borrows, - ) { - last_otherwise_block + ); + + if let Some(last_otherwise_block) = otherwise_block { + block = last_otherwise_block } else { // Any remaining candidates are unreachable. if unmatched_candidates.is_empty() { - return Vec::new(); - } else { - self.cfg.start_new_block() + return; } + block = self.cfg.start_new_block(); }; + } else { + block = *start_block.get_or_insert_with(|| self.cfg.start_new_block()); } // If there are no candidates that still need testing, we're // done. Since all matches are exhaustive, execution should // never reach this point. if unmatched_candidates.is_empty() { - return vec![block]; + let source_info = self.source_info(span); + if let Some(otherwise) = otherwise_block { + self.cfg.terminate( + block, + source_info, + TerminatorKind::Goto { target: otherwise }, + ); + } else { + self.cfg.terminate( + block, + source_info, + TerminatorKind::Unreachable, + ) + } + return; } - // Test candidates where possible. - let (otherwise, untested_candidates) = self.test_candidates( + // Test for the remaining candidates. + self.test_candidates( span, unmatched_candidates, block, + otherwise_block, fake_borrows, ); - - // If the target candidates were exhaustive, then we are done. - // But for borrowck continue build decision tree. - if untested_candidates.is_empty() { - return otherwise; - } - - // Otherwise, let's process those remaining candidates. - let join_block = self.join_otherwise_blocks(span, otherwise); - self.match_candidates( - span, - untested_candidates, - join_block, - fake_borrows, - ) } /// Link up matched candidates. For example, if we have something like @@ -908,7 +891,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { fn select_matched_candidates( &mut self, matched_candidates: &mut [&mut Candidate<'_, 'tcx>], - block: BasicBlock, + start_block: &mut Option, fake_borrows: &mut Option>>, ) -> Option { debug_assert!( @@ -956,16 +939,18 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { = matched_candidates.split_at_mut(fully_matched_with_guard + 1); let first_candidate = &reachable_candidates[0]; + let first_prebinding_block = first_candidate.pre_binding_block; - let candidate_source_info = self.source_info(first_candidate.span); - - self.cfg.terminate( - block, - candidate_source_info, - TerminatorKind::Goto { - target: first_candidate.pre_binding_block, - }, - ); + if let Some(start_block) = *start_block { + let source_info = self.source_info(first_candidate.span); + self.cfg.terminate( + start_block, + source_info, + TerminatorKind::Goto { target: first_prebinding_block }, + ); + } else { + *start_block = Some(first_prebinding_block); + } for window in reachable_candidates.windows(2) { if let [first_candidate, second_candidate] = window { @@ -1017,25 +1002,6 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } } - fn join_otherwise_blocks(&mut self, span: Span, mut otherwise: Vec) -> BasicBlock { - let source_info = self.source_info(span); - otherwise.sort(); - otherwise.dedup(); // variant switches can introduce duplicate target blocks - if otherwise.len() == 1 { - otherwise[0] - } else { - let join_block = self.cfg.start_new_block(); - for block in otherwise { - self.cfg.terminate( - block, - source_info, - TerminatorKind::Goto { target: join_block }, - ); - } - join_block - } - } - /// This is the most subtle part of the matching algorithm. At /// this point, the input candidates have been fully simplified, /// and so we know that all remaining match-pairs require some @@ -1153,8 +1119,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { span: Span, mut candidates: &'b mut [&'c mut Candidate<'pat, 'tcx>], block: BasicBlock, + mut otherwise_block: Option, fake_borrows: &mut Option>>, - ) -> (Vec, &'b mut [&'c mut Candidate<'pat, 'tcx>]) { + ) { // extract the match-pair from the highest priority candidate let match_pair = &candidates.first().unwrap().match_pairs[0]; let mut test = self.test(match_pair); @@ -1208,9 +1175,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { "match_candidates: test={:?} match_pair={:?}", test, match_pair ); - let target_blocks = self.perform_test(block, &match_place, &test); let mut target_candidates: Vec>> = vec![]; - target_candidates.resize_with(target_blocks.len(), Default::default); + target_candidates.resize_with(test.targets(), Default::default); let total_candidate_count = candidates.len(); @@ -1236,20 +1202,48 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // apply. Collect a list of blocks where control flow will // branch if one of the `target_candidate` sets is not // exhaustive. - let otherwise: Vec<_> = target_blocks - .into_iter() - .zip(target_candidates) - .flat_map(|(target_block, mut target_candidates)| { + if !candidates.is_empty() { + let remainder_start = &mut None; + self.match_candidates( + span, + remainder_start, + otherwise_block, + candidates, + fake_borrows, + ); + otherwise_block = Some(remainder_start.unwrap()); + }; + let target_blocks: Vec<_> = target_candidates.into_iter().map(|mut candidates| { + if candidates.len() != 0 { + let candidate_start = &mut None; self.match_candidates( span, - &mut *target_candidates, - target_block, + candidate_start, + otherwise_block, + &mut *candidates, fake_borrows, - ) - }) - .collect(); + ); + candidate_start.unwrap() + } else { + *otherwise_block.get_or_insert_with(|| { + let unreachable = self.cfg.start_new_block(); + let source_info = self.source_info(span); + self.cfg.terminate( + unreachable, + source_info, + TerminatorKind::Unreachable, + ); + unreachable + }) + } + }).collect(); - (otherwise, candidates) + self.perform_test( + block, + &match_place, + &test, + target_blocks, + ); } // Determine the fake borrows that are needed to ensure that the place @@ -1323,7 +1317,6 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { fake_borrows: &Vec<(&Place<'tcx>, Local)>, scrutinee_span: Span, region_scope: (region::Scope, SourceInfo), - ) { ) -> BasicBlock { debug!("bind_and_guard_matched_candidate(candidate={:?})", candidate); @@ -1345,10 +1338,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { block, fresh_block, candidate.next_candidate_pre_binding_block, - candidate_source_info, - ); + candidate_source_info, + ); block = fresh_block; - self.ascribe_types(block, &candidate.ascriptions); + self.ascribe_types(block, &candidate.ascriptions); } else { return block; } diff --git a/src/librustc_mir/build/matches/test.rs b/src/librustc_mir/build/matches/test.rs index c767fff925322..bbaf92bf9f562 100644 --- a/src/librustc_mir/build/matches/test.rs +++ b/src/librustc_mir/build/matches/test.rs @@ -16,7 +16,6 @@ use rustc::ty::util::IntTypeExt; use rustc::ty::layout::VariantIdx; use rustc::mir::*; use rustc::hir::{RangeEnd, Mutability}; -use syntax_pos::Span; use std::cmp::Ordering; impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { @@ -162,43 +161,50 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } } - /// Generates the code to perform a test. - pub fn perform_test(&mut self, - block: BasicBlock, - place: &Place<'tcx>, - test: &Test<'tcx>) - -> Vec { + pub fn perform_test( + &mut self, + block: BasicBlock, + place: &Place<'tcx>, + test: &Test<'tcx>, + target_blocks: Vec, + ) { debug!("perform_test({:?}, {:?}: {:?}, {:?})", block, place, place.ty(&self.local_decls, self.hir.tcx()), test); + let source_info = self.source_info(test.span); match test.kind { TestKind::Switch { adt_def, ref variants } => { // Variants is a BitVec of indexes into adt_def.variants. let num_enum_variants = adt_def.variants.len(); let used_variants = variants.count(); - let mut otherwise_block = None; - let mut target_blocks = Vec::with_capacity(num_enum_variants); + debug_assert_eq!(target_blocks.len(), num_enum_variants + 1); + let otherwise_block = *target_blocks.last().unwrap(); let mut targets = Vec::with_capacity(used_variants + 1); let mut values = Vec::with_capacity(used_variants); let tcx = self.hir.tcx(); for (idx, discr) in adt_def.discriminants(tcx) { - target_blocks.push(if variants.contains(idx) { + if variants.contains(idx) { + debug_assert_ne!( + target_blocks[idx.index()], + otherwise_block, + "no canididates for tested discriminant: {:?}", + discr, + ); values.push(discr.val); - let block = self.cfg.start_new_block(); - targets.push(block); - block + targets.push(target_blocks[idx.index()]); } else { - *otherwise_block - .get_or_insert_with(|| self.cfg.start_new_block()) - }); + debug_assert_eq!( + target_blocks[idx.index()], + otherwise_block, + "found canididates for untested discriminant: {:?}", + discr, + ); + } } - targets.push( - otherwise_block - .unwrap_or_else(|| self.unreachable_block()), - ); + targets.push(otherwise_block); debug!("num_enum_variants: {}, tested variants: {:?}, variants: {:?}", num_enum_variants, values, variants); let discr_ty = adt_def.repr.discr_type().to_ty(tcx); @@ -212,160 +218,61 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { values: From::from(values), targets, }); - target_blocks } TestKind::SwitchInt { switch_ty, ref options, indices: _ } => { - let (ret, terminator) = if switch_ty.sty == ty::Bool { + let terminator = if switch_ty.sty == ty::Bool { assert!(options.len() > 0 && options.len() <= 2); - let (true_bb, false_bb) = (self.cfg.start_new_block(), - self.cfg.start_new_block()); - let ret = match options[0] { - 1 => vec![true_bb, false_bb], - 0 => vec![false_bb, true_bb], - v => span_bug!(test.span, "expected boolean value but got {:?}", v) - }; - (ret, TerminatorKind::if_(self.hir.tcx(), Operand::Copy(place.clone()), - true_bb, false_bb)) + if let [first_bb, second_bb] = *target_blocks { + let (true_bb, false_bb) = match options[0] { + 1 => (first_bb, second_bb), + 0 => (second_bb, first_bb), + v => span_bug!(test.span, "expected boolean value but got {:?}", v) + }; + TerminatorKind::if_( + self.hir.tcx(), + Operand::Copy(place.clone()), + true_bb, + false_bb, + ) + } else { + bug!("`TestKind::SwitchInt` on `bool` should have two targets") + } } else { - // The switch may be inexhaustive so we - // add a catch all block - let otherwise = self.cfg.start_new_block(); - let targets: Vec<_> = - options.iter() - .map(|_| self.cfg.start_new_block()) - .chain(Some(otherwise)) - .collect(); - (targets.clone(), TerminatorKind::SwitchInt { + // The switch may be inexhaustive so we have a catch all block + debug_assert_eq!(options.len() + 1, target_blocks.len()); + TerminatorKind::SwitchInt { discr: Operand::Copy(place.clone()), switch_ty, values: options.clone().into(), - targets, - }) + targets: target_blocks, + } }; self.cfg.terminate(block, source_info, terminator); - ret } - TestKind::Eq { value, mut ty } => { - let val = Operand::Copy(place.clone()); - let mut expect = self.literal_operand(test.span, ty, value); + TestKind::Eq { value, ty } => { // Use `PartialEq::eq` instead of `BinOp::Eq` // (the binop can only handle primitives) - let fail = self.cfg.start_new_block(); - if !ty.is_scalar() { - // If we're using `b"..."` as a pattern, we need to insert an - // unsizing coercion, as the byte string has the type `&[u8; N]`. - // - // We want to do this even when the scrutinee is a reference to an - // array, so we can call `<[u8]>::eq` rather than having to find an - // `<[u8; N]>::eq`. - let unsize = |ty: Ty<'tcx>| match ty.sty { - ty::Ref(region, rty, _) => match rty.sty { - ty::Array(inner_ty, n) => Some((region, inner_ty, n)), - _ => None, - }, - _ => None, - }; - let opt_ref_ty = unsize(ty); - let opt_ref_test_ty = unsize(value.ty); - let mut place = place.clone(); - match (opt_ref_ty, opt_ref_test_ty) { - // nothing to do, neither is an array - (None, None) => {}, - (Some((region, elem_ty, _)), _) | - (None, Some((region, elem_ty, _))) => { - let tcx = self.hir.tcx(); - // make both a slice - ty = tcx.mk_imm_ref(region, tcx.mk_slice(elem_ty)); - if opt_ref_ty.is_some() { - place = self.temp(ty, test.span); - self.cfg.push_assign( - block, source_info, &place, Rvalue::Cast( - CastKind::Pointer(PointerCast::Unsize), val, ty - ) - ); - } - if opt_ref_test_ty.is_some() { - let array = self.literal_operand( - test.span, - value.ty, - value, - ); - - let slice = self.temp(ty, test.span); - self.cfg.push_assign( - block, source_info, &slice, Rvalue::Cast( - CastKind::Pointer(PointerCast::Unsize), array, ty - ) - ); - expect = Operand::Move(slice); - } - }, + if let [success, fail] = *target_blocks { + if !ty.is_scalar() { + self.non_scalar_compare( + block, + success, + fail, + source_info, + value, + place, + ty, + ); + } else { + let val = Operand::Copy(place.clone()); + let expect = self.literal_operand(test.span, ty, value); + self.compare(block, success, fail, source_info, BinOp::Eq, expect, val); } - let eq_def_id = self.hir.tcx().lang_items().eq_trait().unwrap(); - let (mty, method) = self.hir.trait_method(eq_def_id, "eq", ty, &[ty.into()]); - - let re_erased = self.hir.tcx().lifetimes.re_erased; - // take the argument by reference - let tam = ty::TypeAndMut { - ty, - mutbl: Mutability::MutImmutable, - }; - let ref_ty = self.hir.tcx().mk_ref(re_erased, tam); - - // let lhs_ref_place = &lhs; - let ref_rvalue = Rvalue::Ref(re_erased, BorrowKind::Shared, place); - let lhs_ref_place = self.temp(ref_ty, test.span); - self.cfg.push_assign(block, source_info, &lhs_ref_place, ref_rvalue); - let val = Operand::Move(lhs_ref_place); - - // let rhs_place = rhs; - let rhs_place = self.temp(ty, test.span); - self.cfg.push_assign(block, source_info, &rhs_place, Rvalue::Use(expect)); - - // let rhs_ref_place = &rhs_place; - let ref_rvalue = Rvalue::Ref(re_erased, BorrowKind::Shared, rhs_place); - let rhs_ref_place = self.temp(ref_ty, test.span); - self.cfg.push_assign(block, source_info, &rhs_ref_place, ref_rvalue); - let expect = Operand::Move(rhs_ref_place); - - let bool_ty = self.hir.bool_ty(); - let eq_result = self.temp(bool_ty, test.span); - let eq_block = self.cfg.start_new_block(); - let cleanup = self.diverge_cleanup(); - self.cfg.terminate(block, source_info, TerminatorKind::Call { - func: Operand::Constant(box Constant { - span: test.span, - ty: mty, - - // FIXME(#54571): This constant comes from user - // input (a constant in a pattern). Are - // there forms where users can add type - // annotations here? For example, an - // associated constant? Need to - // experiment. - user_ty: None, - - literal: method, - }), - args: vec![val, expect], - destination: Some((eq_result.clone(), eq_block)), - cleanup: Some(cleanup), - from_hir_call: false, - }); - - // check the result - let block = self.cfg.start_new_block(); - self.cfg.terminate(eq_block, source_info, - TerminatorKind::if_(self.hir.tcx(), - Operand::Move(eq_result), - block, fail)); - vec![block, fail] } else { - let block = self.compare(block, fail, test.span, BinOp::Eq, expect, val); - vec![block, fail] - } + bug!("`TestKind::Eq` should have two target blocks") + }; } TestKind::Range(PatternRange { ref lo, ref hi, ty, ref end }) => { @@ -374,20 +281,31 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { let hi = self.literal_operand(test.span, ty, hi); let val = Operand::Copy(place.clone()); - let fail = self.cfg.start_new_block(); - let block = self.compare(block, fail, test.span, BinOp::Le, lo, val.clone()); - let block = match *end { - RangeEnd::Included => self.compare(block, fail, test.span, BinOp::Le, val, hi), - RangeEnd::Excluded => self.compare(block, fail, test.span, BinOp::Lt, val, hi), - }; - - vec![block, fail] + if let [success, fail] = *target_blocks { + let lower_bound_success = self.cfg.start_new_block(); + + self.compare( + block, + lower_bound_success, + fail, + source_info, + BinOp::Le, + lo, + val.clone(), + ); + let op = match *end { + RangeEnd::Included => BinOp::Le, + RangeEnd::Excluded => BinOp::Lt, + }; + self.compare(lower_bound_success, success, fail, source_info, op, val, hi); + } else { + bug!("`TestKind::Range` should have two target blocks"); + } } TestKind::Len { len, op } => { - let (usize_ty, bool_ty) = (self.hir.usize_ty(), self.hir.bool_ty()); - let (actual, result) = (self.temp(usize_ty, test.span), - self.temp(bool_ty, test.span)); + let usize_ty = self.hir.usize_ty(); + let actual = self.temp(usize_ty, test.span); // actual = len(place) self.cfg.push_assign(block, source_info, @@ -396,44 +314,188 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // expected = let expected = self.push_usize(block, source_info, len); - // result = actual == expected OR result = actual < expected - self.cfg.push_assign(block, source_info, &result, - Rvalue::BinaryOp(op, - Operand::Move(actual), - Operand::Move(expected))); - - // branch based on result - let (false_bb, true_bb) = (self.cfg.start_new_block(), - self.cfg.start_new_block()); - self.cfg.terminate(block, source_info, - TerminatorKind::if_(self.hir.tcx(), Operand::Move(result), - true_bb, false_bb)); - vec![true_bb, false_bb] + if let [true_bb, false_bb] = *target_blocks { + // result = actual == expected OR result = actual < expected + // branch based on result + self.compare( + block, + true_bb, + false_bb, + source_info, + op, + Operand::Move(actual), + Operand::Move(expected), + ); + } else { + bug!("`TestKind::Len` should have two target blocks"); + } } } } - fn compare(&mut self, - block: BasicBlock, - fail_block: BasicBlock, - span: Span, - op: BinOp, - left: Operand<'tcx>, - right: Operand<'tcx>) -> BasicBlock { + /// Compare using the provided built-in comparison operator + fn compare( + &mut self, + block: BasicBlock, + success_block: BasicBlock, + fail_block: BasicBlock, + source_info: SourceInfo, + op: BinOp, + left: Operand<'tcx>, + right: Operand<'tcx>, + ) { let bool_ty = self.hir.bool_ty(); - let result = self.temp(bool_ty, span); + let result = self.temp(bool_ty, source_info.span); // result = op(left, right) - let source_info = self.source_info(span); - self.cfg.push_assign(block, source_info, &result, - Rvalue::BinaryOp(op, left, right)); + self.cfg.push_assign( + block, + source_info, + &result, + Rvalue::BinaryOp(op, left, right), + ); // branch based on result - let target_block = self.cfg.start_new_block(); - self.cfg.terminate(block, source_info, - TerminatorKind::if_(self.hir.tcx(), Operand::Move(result), - target_block, fail_block)); - target_block + self.cfg.terminate( + block, + source_info, + TerminatorKind::if_( + self.hir.tcx(), + Operand::Move(result), + success_block, + fail_block, + ), + ); + } + + /// Compare using `std::compare::PartialEq::eq` + fn non_scalar_compare( + &mut self, + block: BasicBlock, + success_block: BasicBlock, + fail_block: BasicBlock, + source_info: SourceInfo, + value: &'tcx ty::Const<'tcx>, + place: &Place<'tcx>, + mut ty: Ty<'tcx>, + ) { + use rustc::middle::lang_items::EqTraitLangItem; + + let mut expect = self.literal_operand(source_info.span, ty, value); + let val = Operand::Copy(place.clone()); + + // If we're using `b"..."` as a pattern, we need to insert an + // unsizing coercion, as the byte string has the type `&[u8; N]`. + // + // We want to do this even when the scrutinee is a reference to an + // array, so we can call `<[u8]>::eq` rather than having to find an + // `<[u8; N]>::eq`. + let unsize = |ty: Ty<'tcx>| match ty.sty { + ty::Ref(region, rty, _) => match rty.sty { + ty::Array(inner_ty, n) => Some((region, inner_ty, n)), + _ => None, + }, + _ => None, + }; + let opt_ref_ty = unsize(ty); + let opt_ref_test_ty = unsize(value.ty); + let mut place = place.clone(); + match (opt_ref_ty, opt_ref_test_ty) { + // nothing to do, neither is an array + (None, None) => {}, + (Some((region, elem_ty, _)), _) | + (None, Some((region, elem_ty, _))) => { + let tcx = self.hir.tcx(); + // make both a slice + ty = tcx.mk_imm_ref(region, tcx.mk_slice(elem_ty)); + if opt_ref_ty.is_some() { + place = self.temp(ty, source_info.span); + self.cfg.push_assign( + block, source_info, &place, Rvalue::Cast( + CastKind::Pointer(PointerCast::Unsize), val, ty + ) + ); + } + if opt_ref_test_ty.is_some() { + let array = self.literal_operand( + source_info.span, + value.ty, + value, + ); + + let slice = self.temp(ty, source_info.span); + self.cfg.push_assign( + block, source_info, &slice, Rvalue::Cast( + CastKind::Pointer(PointerCast::Unsize), array, ty + ) + ); + expect = Operand::Move(slice); + } + }, + } + let eq_def_id = self.hir.tcx().require_lang_item(EqTraitLangItem); + let (mty, method) = self.hir.trait_method(eq_def_id, "eq", ty, &[ty.into()]); + + let re_erased = self.hir.tcx().lifetimes.re_erased; + // take the argument by reference + let tam = ty::TypeAndMut { + ty, + mutbl: Mutability::MutImmutable, + }; + let ref_ty = self.hir.tcx().mk_ref(re_erased, tam); + + // let lhs_ref_place = &lhs; + let ref_rvalue = Rvalue::Ref(re_erased, BorrowKind::Shared, place); + let lhs_ref_place = self.temp(ref_ty, source_info.span); + self.cfg.push_assign(block, source_info, &lhs_ref_place, ref_rvalue); + let val = Operand::Move(lhs_ref_place); + + // let rhs_place = rhs; + let rhs_place = self.temp(ty, source_info.span); + self.cfg.push_assign(block, source_info, &rhs_place, Rvalue::Use(expect)); + + // let rhs_ref_place = &rhs_place; + let ref_rvalue = Rvalue::Ref(re_erased, BorrowKind::Shared, rhs_place); + let rhs_ref_place = self.temp(ref_ty, source_info.span); + self.cfg.push_assign(block, source_info, &rhs_ref_place, ref_rvalue); + let expect = Operand::Move(rhs_ref_place); + + let bool_ty = self.hir.bool_ty(); + let eq_result = self.temp(bool_ty, source_info.span); + let eq_block = self.cfg.start_new_block(); + let cleanup = self.diverge_cleanup(); + self.cfg.terminate(block, source_info, TerminatorKind::Call { + func: Operand::Constant(box Constant { + span: source_info.span, + ty: mty, + + // FIXME(#54571): This constant comes from user + // input (a constant in a pattern). Are + // there forms where users can add type + // annotations here? For example, an + // associated constant? Need to + // experiment. + user_ty: None, + + literal: method, + }), + args: vec![val, expect], + destination: Some((eq_result.clone(), eq_block)), + cleanup: Some(cleanup), + from_hir_call: false, + }); + + // check the result + self.cfg.terminate( + eq_block, + source_info, + TerminatorKind::if_( + self.hir.tcx(), + Operand::Move(eq_result), + success_block, + fail_block, + ), + ); } /// Given that we are performing `test` against `test_place`, this job @@ -755,6 +817,26 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } } +impl Test<'_> { + pub(super) fn targets(&self) -> usize { + match self.kind { + TestKind::Eq { .. } | TestKind::Range(_) | TestKind::Len { .. } => { + 2 + } + TestKind::Switch { adt_def, .. } => { + adt_def.variants.len() + 1 + } + TestKind::SwitchInt { switch_ty, ref options, .. } => { + if switch_ty.is_bool() { + 2 + } else { + options.len() + 1 + } + } + } + } +} + fn is_switch_ty<'tcx>(ty: Ty<'tcx>) -> bool { ty.is_integral() || ty.is_char() || ty.is_bool() } diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs index 579a403f3aca7..fdab73eb3ca7e 100644 --- a/src/librustc_mir/build/mod.rs +++ b/src/librustc_mir/build/mod.rs @@ -952,17 +952,6 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } } } - - fn unreachable_block(&mut self) -> BasicBlock { - match self.cached_unreachable_block { - Some(ub) => ub, - None => { - let ub = self.cfg.start_new_block(); - self.cached_unreachable_block = Some(ub); - ub - } - } - } } /////////////////////////////////////////////////////////////////////////// diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index 125411a717d1e..2fc52969a3cbc 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -1448,6 +1448,9 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> { StatementKind::Assign(..) => { self.super_statement(statement, location); } + StatementKind::FakeRead(FakeReadCause::ForMatchedPlace, _) => { + self.not_const(); + } // FIXME(eddyb) should these really do nothing? StatementKind::FakeRead(..) | StatementKind::SetDiscriminant { .. } | diff --git a/src/librustc_mir/transform/qualify_min_const_fn.rs b/src/librustc_mir/transform/qualify_min_const_fn.rs index 7f5afb2394a46..418a88859ac42 100644 --- a/src/librustc_mir/transform/qualify_min_const_fn.rs +++ b/src/librustc_mir/transform/qualify_min_const_fn.rs @@ -211,6 +211,10 @@ fn check_statement( check_rvalue(tcx, body, rval, span) } + StatementKind::FakeRead(FakeReadCause::ForMatchedPlace, _) => { + Err((span, "loops and conditional expressions are not stable in const fn".into())) + } + StatementKind::FakeRead(_, place) => check_place(place, span), // just an assignment diff --git a/src/test/codegen/match.rs b/src/test/codegen/match.rs index 1b46bb3b25f90..145d4ba6b4c5a 100644 --- a/src/test/codegen/match.rs +++ b/src/test/codegen/match.rs @@ -14,11 +14,11 @@ pub fn exhaustive_match(e: E, unit: ()) { // CHECK-NEXT: i[[TY:[0-9]+]] [[DISCR:[0-9]+]], label %[[A:[a-zA-Z0-9_]+]] // CHECK-NEXT: i[[TY:[0-9]+]] [[DISCR:[0-9]+]], label %[[B:[a-zA-Z0-9_]+]] // CHECK-NEXT: ] +// CHECK: [[B]]: +// CHECK-NEXT: br label %[[EXIT:[a-zA-Z0-9_]+]] // CHECK: [[OTHERWISE]]: // CHECK-NEXT: unreachable // CHECK: [[A]]: -// CHECK-NEXT: br label %[[EXIT:[a-zA-Z0-9_]+]] -// CHECK: [[B]]: // CHECK-NEXT: br label %[[EXIT:[a-zA-Z0-9_]+]] match e { E::A => unit, diff --git a/src/test/compile-fail/const-fn-error.rs b/src/test/compile-fail/const-fn-error.rs index da6036a04a549..87a9cf9490d86 100644 --- a/src/test/compile-fail/const-fn-error.rs +++ b/src/test/compile-fail/const-fn-error.rs @@ -7,6 +7,7 @@ const fn f(x: usize) -> usize { for i in 0..x { //~^ ERROR E0015 //~| ERROR E0019 + //~| ERROR E0019 //~| ERROR E0080 sum += i; } diff --git a/src/test/compile-fail/issue-52443.rs b/src/test/compile-fail/issue-52443.rs index e1f07ff96beed..0d6906086e9b1 100644 --- a/src/test/compile-fail/issue-52443.rs +++ b/src/test/compile-fail/issue-52443.rs @@ -4,5 +4,6 @@ fn main() { [(); {while true {break}; 0}]; //~ ERROR constant contains unimplemented expression type [(); { for _ in 0usize.. {}; 0}]; //~ ERROR calls in constants are limited to constant functions //~^ ERROR constant contains unimplemented expression type + //~| ERROR constant contains unimplemented expression type //~| ERROR evaluation of constant value failed } diff --git a/src/test/mir-opt/const_prop/switch_int.rs b/src/test/mir-opt/const_prop/switch_int.rs index 0df1112ec3eb7..904d303d87e31 100644 --- a/src/test/mir-opt/const_prop/switch_int.rs +++ b/src/test/mir-opt/const_prop/switch_int.rs @@ -13,26 +13,26 @@ fn main() { // bb0: { // ... // _1 = const 1i32; -// switchInt(_1) -> [1i32: bb1, otherwise: bb2]; +// switchInt(_1) -> [1i32: bb2, otherwise: bb1]; // } // END rustc.main.ConstProp.before.mir // START rustc.main.ConstProp.after.mir // bb0: { // ... -// switchInt(const 1i32) -> [1i32: bb1, otherwise: bb2]; +// switchInt(const 1i32) -> [1i32: bb2, otherwise: bb1]; // } // END rustc.main.ConstProp.after.mir // START rustc.main.SimplifyBranches-after-const-prop.before.mir // bb0: { // ... // _1 = const 1i32; -// switchInt(const 1i32) -> [1i32: bb1, otherwise: bb2]; +// switchInt(const 1i32) -> [1i32: bb2, otherwise: bb1]; // } // END rustc.main.SimplifyBranches-after-const-prop.before.mir // START rustc.main.SimplifyBranches-after-const-prop.after.mir // bb0: { // ... // _1 = const 1i32; -// goto -> bb1; +// goto -> bb2; // } // END rustc.main.SimplifyBranches-after-const-prop.after.mir diff --git a/src/test/mir-opt/deaggregator_test_enum_2.rs b/src/test/mir-opt/deaggregator_test_enum_2.rs index 59c75739d81a4..b39ad1bef8e34 100644 --- a/src/test/mir-opt/deaggregator_test_enum_2.rs +++ b/src/test/mir-opt/deaggregator_test_enum_2.rs @@ -21,30 +21,22 @@ fn main() { // END RUST SOURCE // START rustc.test1.Deaggregator.before.mir // bb1: { -// StorageLive(_4); -// _4 = _2; -// _0 = Foo::A(move _4,); -// StorageDead(_4); -// goto -> bb3; -// } -// bb2: { // StorageLive(_5); // _5 = _2; // _0 = Foo::B(move _5,); // StorageDead(_5); // goto -> bb3; // } -// END rustc.test1.Deaggregator.before.mir -// START rustc.test1.Deaggregator.after.mir -// bb1: { +// bb2: { // StorageLive(_4); // _4 = _2; -// ((_0 as A).0: i32) = move _4; -// discriminant(_0) = 0; +// _0 = Foo::A(move _4,); // StorageDead(_4); // goto -> bb3; // } -// bb2: { +// END rustc.test1.Deaggregator.before.mir +// START rustc.test1.Deaggregator.after.mir +// bb1: { // StorageLive(_5); // _5 = _2; // ((_0 as B).0: i32) = move _5; @@ -52,5 +44,13 @@ fn main() { // StorageDead(_5); // goto -> bb3; // } +// bb2: { +// StorageLive(_4); +// _4 = _2; +// ((_0 as A).0: i32) = move _4; +// discriminant(_0) = 0; +// StorageDead(_4); +// goto -> bb3; +// } // END rustc.test1.Deaggregator.after.mir // diff --git a/src/test/mir-opt/issue-38669.rs b/src/test/mir-opt/issue-38669.rs index e8ab690bb4648..909f9b7b6b79a 100644 --- a/src/test/mir-opt/issue-38669.rs +++ b/src/test/mir-opt/issue-38669.rs @@ -31,17 +31,17 @@ fn main() { // switchInt(_4) -> [false: bb5, otherwise: bb4]; // } // ... -// bb7: { -// _0 = (); -// StorageDead(_4); -// StorageDead(_1); -// return; -// } -// bb8: { +// bb5: { // _3 = (); // StorageDead(_4); // _1 = const true; // _2 = (); // goto -> bb2; // } +// bb6: { +// _0 = (); +// StorageDead(_4); +// StorageDead(_1); +// return; +// } // END rustc.main.SimplifyCfg-initial.after.mir diff --git a/src/test/mir-opt/issue-49232.rs b/src/test/mir-opt/issue-49232.rs index 447f3a07a6a98..9dde6d821f2bc 100644 --- a/src/test/mir-opt/issue-49232.rs +++ b/src/test/mir-opt/issue-49232.rs @@ -32,76 +32,58 @@ fn main() { // falseUnwind -> [real: bb3, cleanup: bb4]; // } // bb2: { -// goto -> bb20; +// goto -> bb14; // } // bb3: { // StorageLive(_2); // StorageLive(_3); // _3 = const true; // FakeRead(ForMatchedPlace, _3); -// switchInt(_3) -> [false: bb9, otherwise: bb8]; +// switchInt(_3) -> [false: bb5, otherwise: bb6]; // } // bb4 (cleanup): { // resume; // } // bb5: { -// falseEdges -> [real: bb11, imaginary: bb6]; +// falseEdges -> [real: bb7, imaginary: bb6]; // } // bb6: { -// falseEdges -> [real: bb13, imaginary: bb7]; +// _0 = (); +// goto -> bb8; // } // bb7: { -// unreachable; -// } -// bb8: { -// goto -> bb6; -// } -// bb9: { -// goto -> bb5; -// } -// bb10: { // _2 = const 4i32; -// goto -> bb18; -// } -// bb11: { -// goto -> bb10; -// } -// bb12: { -// _0 = (); -// goto -> bb14; -// } -// bb13: { // goto -> bb12; // } -// bb14: { +// bb8: { // StorageDead(_3); -// goto -> bb15; +// goto -> bb9; // } -// bb15: { +// bb9: { // StorageDead(_2); // goto -> bb2; // } -// bb16: { +// bb10: { // _4 = (); // unreachable; // } -// bb17: { -// goto -> bb18; +// bb11: { +// goto -> bb12; // } -// bb18: { +// bb12: { // FakeRead(ForLet, _2); // StorageDead(_3); // StorageLive(_6); // _6 = &_2; -// _5 = const std::mem::drop::<&i32>(move _6) -> [return: bb19, unwind: bb4]; +// _5 = const std::mem::drop::<&i32>(move _6) -> [return: bb13, unwind: bb4]; // } -// bb19: { +// bb13: { // StorageDead(_6); // _1 = (); // StorageDead(_2); // goto -> bb1; // } -// bb20: { +// bb14: { // return; // } // } diff --git a/src/test/mir-opt/loop_test.rs b/src/test/mir-opt/loop_test.rs index e75955b9b2440..68ea60d92787c 100644 --- a/src/test/mir-opt/loop_test.rs +++ b/src/test/mir-opt/loop_test.rs @@ -22,20 +22,21 @@ fn main() { // resume; // } // ... -// bb6: { // Entry into the loop +// bb3: { // Entry into the loop // _1 = (); // StorageDead(_2); -// goto -> bb7; +// goto -> bb5; // } -// bb7: { // The loop_block -// falseUnwind -> [real: bb8, cleanup: bb1]; +// ... +// bb5: { // The loop_block +// falseUnwind -> [real: bb6, cleanup: bb1]; // } -// bb8: { // The loop body (body_block) +// bb6: { // The loop body (body_block) // StorageLive(_6); // _6 = const 1i32; // FakeRead(ForLet, _6); // StorageDead(_6); -// goto -> bb7; +// goto -> bb5; // } // ... // END rustc.main.SimplifyCfg-qualify-consts.after.mir diff --git a/src/test/mir-opt/match-arm-scopes.rs b/src/test/mir-opt/match-arm-scopes.rs index 0f026b8a08dfa..a2bc238c68ad9 100644 --- a/src/test/mir-opt/match-arm-scopes.rs +++ b/src/test/mir-opt/match-arm-scopes.rs @@ -42,55 +42,49 @@ fn main() { // let mut _0: i32; // let mut _3: &bool; // Temp for fake borrow of `items.0` // let mut _4: &bool; // Temp for fake borrow of `items.1` -// let _5: bool; // `a` in arm -// let _6: &bool; // `a` in guard -// let _7: std::string::String; // `s` in arm -// let _8: &std::string::String; // `s` in guard +// let _5: bool; // `a` in arm +// let _6: &bool; // `a` in guard +// let _7: std::string::String; // `s` in arm +// let _8: &std::string::String; // `s` in guard // let mut _9: bool; // `if cond { return 3 } else { a }` // let mut _10: bool; // `cond` // let mut _11: !; // `return 3` // let mut _12: bool; // `if cond { return 3 } else { a }` // let mut _13: bool; // `cond` // let mut _14: !; // `return 3` -// let _15: bool; // `b` -// let _16: std::string::String; // `t` +// let _15: bool; // `b` +// let _16: std::string::String; // `t` // scope 1 { // } // scope 2 { // } // bb0: { // FakeRead(ForMatchedPlace, _2); -// switchInt((_2.0: bool)) -> [false: bb2, otherwise: bb7]; +// switchInt((_2.0: bool)) -> [false: bb2, otherwise: bb5]; // } // bb1 (cleanup): { // resume; // } // bb2: { -// falseEdges -> [real: bb10, imaginary: bb3]; +// falseEdges -> [real: bb8, imaginary: bb3]; // } // bb3: { -// falseEdges -> [real: bb21, imaginary: bb4]; +// falseEdges -> [real: bb17, imaginary: bb4]; // } // bb4: { -// falseEdges -> [real: bb31, imaginary: bb5]; +// falseEdges -> [real: bb25, imaginary: bb26]; // } // bb5: { -// falseEdges -> [real: bb32, imaginary: bb6]; +// switchInt((_2.1: bool)) -> [false: bb3, otherwise: bb6]; // } // bb6: { -// unreachable; +// switchInt((_2.0: bool)) -> [false: bb26, otherwise: bb4]; // } -// bb7: { -// switchInt((_2.1: bool)) -> [false: bb3, otherwise: bb8]; -// } -// bb8: { -// switchInt((_2.0: bool)) -> [false: bb5, otherwise: bb4]; -// } -// bb9: { // arm 1 +// bb7: { // arm 1 // _0 = const 1i32; -// drop(_7) -> [return: bb29, unwind: bb16]; +// drop(_7) -> [return: bb23, unwind: bb13]; // } -// bb10: { // guard - first time +// bb8: { // guard - first time // StorageLive(_6); // _6 = &(_2.1: bool); // StorageLive(_8); @@ -101,58 +95,52 @@ fn main() { // StorageLive(_10); // _10 = _1; // FakeRead(ForMatchedPlace, _10); -// switchInt(_10) -> [false: bb12, otherwise: bb11]; +// switchInt(_10) -> [false: bb10, otherwise: bb9]; // } -// bb11: { -// falseEdges -> [real: bb14, imaginary: bb12]; +// bb9: { +// falseEdges -> [real: bb11, imaginary: bb10]; // } -// bb12: { -// falseEdges -> [real: bb18, imaginary: bb13]; -// } -// bb13: { -// unreachable; +// bb10: { // `else` block - first time +// _9 = (*_6); +// StorageDead(_10); +// FakeRead(ForMatchGuard, _3); +// FakeRead(ForMatchGuard, _4); +// FakeRead(ForGuardBinding, _6); +// FakeRead(ForGuardBinding, _8); +// switchInt(move _9) -> [false: bb16, otherwise: bb15]; // } -// bb14: { // `return 3` - first time +// bb11: { // `return 3` - first time // _0 = const 3i32; // StorageDead(_10); // StorageDead(_9); // StorageDead(_8); // StorageDead(_6); -// goto -> bb17; +// goto -> bb14; // } -// bb15: { +// bb12: { // return; // } -// bb16 (cleanup): { +// bb13 (cleanup): { // drop(_2) -> bb1; // } -// bb17: { -// drop(_2) -> [return: bb15, unwind: bb1]; -// } -// bb18: { // `else` block - first time -// _9 = (*_6); -// StorageDead(_10); -// FakeRead(ForMatchGuard, _3); -// FakeRead(ForMatchGuard, _4); -// FakeRead(ForGuardBinding, _6); -// FakeRead(ForGuardBinding, _8); -// switchInt(move _9) -> [false: bb20, otherwise: bb19]; +// bb14: { +// drop(_2) -> [return: bb12, unwind: bb1]; // } -// bb19: { +// bb15: { // StorageDead(_9); // StorageLive(_5); // _5 = (_2.1: bool); // StorageLive(_7); // _7 = move (_2.2: std::string::String); -// goto -> bb9; +// goto -> bb7; // } -// bb20: { // guard otherwise case - first time +// bb16: { // guard otherwise case - first time // StorageDead(_9); // StorageDead(_8); // StorageDead(_6); -// falseEdges -> [real: bb7, imaginary: bb3]; +// falseEdges -> [real: bb5, imaginary: bb3]; // } -// bb21: { // guard - second time +// bb17: { // guard - second time // StorageLive(_6); // _6 = &(_2.0: bool); // StorageLive(_8); @@ -163,80 +151,74 @@ fn main() { // StorageLive(_13); // _13 = _1; // FakeRead(ForMatchedPlace, _13); -// switchInt(_13) -> [false: bb23, otherwise: bb22]; -// } -// bb22: { -// falseEdges -> [real: bb25, imaginary: bb23]; -// } -// bb23: { -// falseEdges -> [real: bb26, imaginary: bb24]; +// switchInt(_13) -> [false: bb19, otherwise: bb18]; // } -// bb24: { -// unreachable; +// bb18: { +// falseEdges -> [real: bb20, imaginary: bb19]; // } -// bb25: { // `return 3` - second time -// _0 = const 3i32; -// StorageDead(_13); -// StorageDead(_12); -// StorageDead(_8); -// StorageDead(_6); -// goto -> bb17; -// } -// bb26: { // `else` block - second time +// bb19: { // `else` block - second time // _12 = (*_6); // StorageDead(_13); // FakeRead(ForMatchGuard, _3); // FakeRead(ForMatchGuard, _4); // FakeRead(ForGuardBinding, _6); // FakeRead(ForGuardBinding, _8); -// switchInt(move _12) -> [false: bb28, otherwise: bb27]; +// switchInt(move _12) -> [false: bb22, otherwise: bb21]; +// } +// bb20: { +// _0 = const 3i32; +// StorageDead(_13); +// StorageDead(_12); +// StorageDead(_8); +// StorageDead(_6); +// goto -> bb14; // } -// bb27: { // Guard otherwise case - second time +// bb21: { // bindings for arm 1 // StorageDead(_12); // StorageLive(_5); // _5 = (_2.0: bool); // StorageLive(_7); // _7 = move (_2.2: std::string::String); -// goto -> bb9; +// goto -> bb7; // } -// bb28: { // rest of arm 1 +// bb22: { // Guard otherwise case - second time // StorageDead(_12); // StorageDead(_8); // StorageDead(_6); -// falseEdges -> [real: bb8, imaginary: bb4]; +// falseEdges -> [real: bb6, imaginary: bb4]; // } -// bb29: { +// bb23: { // rest of arm 1 // StorageDead(_7); // StorageDead(_5); // StorageDead(_8); // StorageDead(_6); -// goto -> bb34; +// goto -> bb28; // } -// bb30: { // arm 2 +// bb24: { // arm 2 // _0 = const 2i32; -// drop(_16) -> [return: bb33, unwind: bb16]; +// drop(_16) -> [return: bb27, unwind: bb13]; // } -// bb31: { // bindings for arm 2 - first pattern +// bb25: { // bindings for arm 2 - first pattern // StorageLive(_15); // _15 = (_2.1: bool); // StorageLive(_16); // _16 = move (_2.2: std::string::String); -// goto -> bb30; +// goto -> bb24; // } -// bb32: { // bindings for arm 2 - first pattern +// bb26: { // bindings for arm 2 - second pattern // StorageLive(_15); // _15 = (_2.1: bool); // StorageLive(_16); // _16 = move (_2.2: std::string::String); -// goto -> bb30; +// goto -> bb24; // } -// bb33: { // rest of arm 2 +// bb27: { // rest of arm 2 // StorageDead(_16); // StorageDead(_15); -// goto -> bb34; +// goto -> bb28; // } -// bb34: { // end of match -// drop(_2) -> [return: bb15, unwind: bb1]; +// bb28: { +// drop(_2) -> [return: bb12, unwind: bb1]; // } // END rustc.complicated_match.SimplifyCfg-initial.after.mir // START rustc.complicated_match.ElaborateDrops.after.mir diff --git a/src/test/mir-opt/match_false_edges.rs b/src/test/mir-opt/match_false_edges.rs index 6979924c8cd90..a62e1b21dd162 100644 --- a/src/test/mir-opt/match_false_edges.rs +++ b/src/test/mir-opt/match_false_edges.rs @@ -45,39 +45,37 @@ fn main() { // _2 = std::option::Option::::Some(const 42i32,); // FakeRead(ForMatchedPlace, _2); // _3 = discriminant(_2); -// switchInt(move _3) -> [0isize: bb4, 1isize: bb2, otherwise: bb6]; +// switchInt(move _3) -> [0isize: bb4, 1isize: bb2, otherwise: bb5]; // } // bb1 (cleanup): { // resume; // } // bb2: { -// falseEdges -> [real: bb7, imaginary: bb3]; //pre_binding1 +// falseEdges -> [real: bb6, imaginary: bb3]; //pre_binding1 // } // bb3: { -// falseEdges -> [real: bb11, imaginary: bb4]; //pre_binding2 +// falseEdges -> [real: bb10, imaginary: bb4]; //pre_binding2 // } -// bb4: { -// falseEdges -> [real: bb12, imaginary: bb5]; //pre_binding3 +// bb4: { //pre_binding3 and arm3 +// _1 = (const 3i32, const 3i32); +// goto -> bb11; // } // bb5: { // unreachable; // } -// bb6: { -// unreachable; -// } -// bb7: { // binding1 and guard +// bb6: { // binding1 and guard // StorageLive(_6); // _6 = &(((promoted[0]: std::option::Option) as Some).0: i32); // _4 = &shallow _2; // StorageLive(_7); -// _7 = const guard() -> [return: bb8, unwind: bb1]; +// _7 = const guard() -> [return: bb7, unwind: bb1]; // } -// bb8: { // end of guard +// bb7: { // end of guard // FakeRead(ForMatchGuard, _4); // FakeRead(ForGuardBinding, _6); -// switchInt(move _7) -> [false: bb10, otherwise: bb9]; +// switchInt(move _7) -> [false: bb9, otherwise: bb8]; // } -// bb9: { // arm1 +// bb8: { // arm1 // StorageDead(_7); // StorageLive(_5); // _5 = ((_2 as Some).0: i32); @@ -87,14 +85,14 @@ fn main() { // StorageDead(_8); // StorageDead(_5); // StorageDead(_6); -// goto -> bb13; +// goto -> bb11; // } -// bb10: { // to pre_binding2 +// bb9: { // to pre_binding2 // StorageDead(_7); // StorageDead(_6); -// falseEdges -> [real: bb3, imaginary: bb3]; +// goto -> bb3; // } -// bb11: { // arm2 +// bb10: { // arm2 // StorageLive(_9); // _9 = ((_2 as Some).0: i32); // StorageLive(_10); @@ -102,13 +100,9 @@ fn main() { // _1 = (const 2i32, move _10); // StorageDead(_10); // StorageDead(_9); -// goto -> bb13; +// goto -> bb11; // } -// bb12: { // arm3 -// _1 = (const 3i32, const 3i32); -// goto -> bb13; -// } -// bb13: { +// bb11: { // arm3 // StorageDead(_2); // StorageDead(_1); // _0 = (); @@ -122,39 +116,33 @@ fn main() { // _2 = std::option::Option::::Some(const 42i32,); // FakeRead(ForMatchedPlace, _2); // _3 = discriminant(_2); -// switchInt(move _3) -> [0isize: bb3, 1isize: bb2, otherwise: bb6]; +// switchInt(move _3) -> [0isize: bb3, 1isize: bb2, otherwise: bb4]; // } // bb1 (cleanup): { // resume; // } // bb2: { -// falseEdges -> [real: bb7, imaginary: bb3]; +// falseEdges -> [real: bb5, imaginary: bb3]; // } // bb3: { -// falseEdges -> [real: bb11, imaginary: bb4]; +// falseEdges -> [real: bb9, imaginary: bb10]; // } -// bb4: { -// falseEdges -> [real: bb12, imaginary: bb5]; -// } -// bb5: { +// bb4: { // to arm3 (can skip 2 since this is `Some`) // unreachable; // } -// bb6: { -// unreachable; -// } -// bb7: { // binding1 and guard +// bb5: { // binding1 and guard // StorageLive(_6); // _6 = &((_2 as Some).0: i32); // _4 = &shallow _2; // StorageLive(_7); -// _7 = const guard() -> [return: bb8, unwind: bb1]; +// _7 = const guard() -> [return: bb6, unwind: bb1]; // } -// bb8: { // end of guard +// bb6: { // end of guard // FakeRead(ForMatchGuard, _4); // FakeRead(ForGuardBinding, _6); -// switchInt(move _7) -> [false: bb10, otherwise: bb9]; +// switchInt(move _7) -> [false: bb8, otherwise: bb7]; // } -// bb9: { // arm1 +// bb7: { // StorageDead(_7); // StorageLive(_5); // _5 = ((_2 as Some).0: i32); @@ -164,18 +152,18 @@ fn main() { // StorageDead(_8); // StorageDead(_5); // StorageDead(_6); -// goto -> bb13; +// goto -> bb11; // } -// bb10: { // to pre_binding3 (can skip 2 since this is `Some`) +// bb8: { // to pre_binding3 (can skip 2 since this is `Some`) // StorageDead(_7); // StorageDead(_6); -// falseEdges -> [real: bb4, imaginary: bb3]; +// falseEdges -> [real: bb10, imaginary: bb3]; // } -// bb11: { // arm2 +// bb9: { // arm2 // _1 = (const 3i32, const 3i32); -// goto -> bb13; +// goto -> bb11; // } -// bb12: { // binding3 and arm3 +// bb10: { // binding3 and arm3 // StorageLive(_9); // _9 = ((_2 as Some).0: i32); // StorageLive(_10); @@ -183,9 +171,9 @@ fn main() { // _1 = (const 2i32, move _10); // StorageDead(_10); // StorageDead(_9); -// goto -> bb13; +// goto -> bb11; // } -// bb13: { +// bb11: { // StorageDead(_2); // StorageDead(_1); // _0 = (); @@ -198,97 +186,91 @@ fn main() { // ... // _2 = std::option::Option::::Some(const 1i32,); // FakeRead(ForMatchedPlace, _2); -// _3 = discriminant(_2); -// switchInt(move _3) -> [1isize: bb2, otherwise: bb3]; +// _4 = discriminant(_2); +// switchInt(move _4) -> [1isize: bb2, otherwise: bb3]; // } // bb1 (cleanup): { // resume; // } // bb2: { -// falseEdges -> [real: bb7, imaginary: bb3]; +// falseEdges -> [real: bb5, imaginary: bb3]; // } // bb3: { -// falseEdges -> [real: bb11, imaginary: bb4]; +// falseEdges -> [real: bb9, imaginary: bb4]; // } // bb4: { -// falseEdges -> [real: bb12, imaginary: bb5]; +// falseEdges -> [real: bb10, imaginary: bb14]; // } // bb5: { -// falseEdges -> [real: bb16, imaginary: bb6]; -// } -// bb6: { -// unreachable; -// } -// bb7: { // binding1: Some(w) if guard() // StorageLive(_7); // _7 = &((_2 as Some).0: i32); // _5 = &shallow _2; // StorageLive(_8); -// _8 = const guard() -> [return: bb8, unwind: bb1]; +// _8 = const guard() -> [return: bb6, unwind: bb1]; // } -// bb8: { //end of guard1 +// bb6: { //end of guard1 // FakeRead(ForMatchGuard, _5); // FakeRead(ForGuardBinding, _7); -// switchInt(move _8) -> [false: bb10, otherwise: bb9]; +// switchInt(move _8) -> [false: bb8, otherwise: bb7]; // } -// bb9: { +// bb7: { // StorageDead(_8); // StorageLive(_6); // _6 = ((_2 as Some).0: i32); // _1 = const 1i32; // StorageDead(_6); // StorageDead(_7); -// goto -> bb17; +// goto -> bb15; // } -// bb10: { +// bb8: { // StorageDead(_8); // StorageDead(_7); // falseEdges -> [real: bb3, imaginary: bb3]; // } -// bb11: { // binding2 & arm2 +// bb9: { // binding2 & arm2 // StorageLive(_9); // _9 = _2; // _1 = const 2i32; // StorageDead(_9); -// goto -> bb17; +// goto -> bb15; // } -// bb12: { // binding3: Some(y) if guard2(y) +// bb10: { // binding3: Some(y) if guard2(y) // StorageLive(_11); // _11 = &((_2 as Some).0: i32); // _5 = &shallow _2; // StorageLive(_12); // StorageLive(_13); // _13 = (*_11); -// _12 = const guard2(move _13) -> [return: bb13, unwind: bb1]; +// _12 = const guard2(move _13) -> [return: bb11, unwind: bb1]; // } -// bb13: { // end of guard2 +// bb11: { // end of guard2 // StorageDead(_13); // FakeRead(ForMatchGuard, _5); // FakeRead(ForGuardBinding, _11); -// switchInt(move _12) -> [false: bb15, otherwise: bb14]; +// switchInt(move _12) -> [false: bb13, otherwise: bb12]; // } -// bb14: { // binding4 & arm4 +// bb12: { // binding4 & arm4 // StorageDead(_12); // StorageLive(_10); // _10 = ((_2 as Some).0: i32); // _1 = const 3i32; // StorageDead(_10); // StorageDead(_11); -// goto -> bb17; +// goto -> bb15; // } -// bb15: { +// bb13: { // StorageDead(_12); // StorageDead(_11); -// falseEdges -> [real: bb5, imaginary: bb5]; +// falseEdges -> [real: bb14, imaginary: bb14]; // } -// bb16: { +// bb14: { // StorageLive(_14); // _14 = _2; // _1 = const 4i32; // StorageDead(_14); -// goto -> bb17; +// goto -> bb15; // } -// bb17: { +// bb15: { // StorageDead(_2); // StorageDead(_1); // _0 = (); diff --git a/src/test/mir-opt/match_test.rs b/src/test/mir-opt/match_test.rs index 2ef9520c12c63..ef60a04d1bdfc 100644 --- a/src/test/mir-opt/match_test.rs +++ b/src/test/mir-opt/match_test.rs @@ -20,67 +20,61 @@ fn main() { // START rustc.main.SimplifyCfg-initial.after.mir // bb0: { // ... -// switchInt(move _4) -> [false: bb6, otherwise: bb7]; +// switchInt(move _6) -> [false: bb6, otherwise: bb5]; // } // bb1: { -// falseEdges -> [real: bb10, imaginary: bb2]; +// falseEdges -> [real: bb9, imaginary: bb2]; // } // bb2: { -// falseEdges -> [real: bb13, imaginary: bb3]; +// falseEdges -> [real: bb12, imaginary: bb3]; // } // bb3: { -// falseEdges -> [real: bb14, imaginary: bb4]; +// falseEdges -> [real: bb13, imaginary: bb4]; // } // bb4: { -// falseEdges -> [real: bb15, imaginary: bb5]; +// _3 = const 3i32; +// goto -> bb14; // } // bb5: { -// unreachable; +// _7 = Lt(_1, const 10i32); +// switchInt(move _7) -> [false: bb6, otherwise: bb1]; // } // bb6: { -// _6 = Le(const 10i32, _1); -// switchInt(move _6) -> [false: bb8, otherwise: bb9]; +// _4 = Le(const 10i32, _1); +// switchInt(move _4) -> [false: bb8, otherwise: bb7]; // } // bb7: { -// _5 = Lt(_1, const 10i32); -// switchInt(move _5) -> [false: bb6, otherwise: bb1]; +// _5 = Le(_1, const 20i32); +// switchInt(move _5) -> [false: bb8, otherwise: bb2]; // } // bb8: { // switchInt(_1) -> [-1i32: bb3, otherwise: bb4]; // } // bb9: { -// _7 = Le(_1, const 20i32); -// switchInt(move _7) -> [false: bb8, otherwise: bb2]; -// } -// bb10: { // _8 = &shallow _1; // StorageLive(_9); // _9 = _2; // FakeRead(ForMatchGuard, _8); -// switchInt(move _9) -> [false: bb12, otherwise: bb11]; +// switchInt(move _9) -> [false: bb11, otherwise: bb10]; // } -// bb11: { +// bb10: { // StorageDead(_9); // _3 = const 0i32; -// goto -> bb16; +// goto -> bb14; // } -// bb12: { +// bb11: { // StorageDead(_9); // falseEdges -> [real: bb4, imaginary: bb2]; // } -// bb13: { +// bb12: { // _3 = const 1i32; -// goto -> bb16; +// goto -> bb14; // } -// bb14: { +// bb13: { // _3 = const 2i32; -// goto -> bb16; +// goto -> bb14; // } -// bb15: { -// _3 = const 3i32; -// goto -> bb16; -// } -// bb16: { +// bb14: { // _0 = (); // StorageDead(_2); // StorageDead(_1); diff --git a/src/test/mir-opt/nll/region-subtyping-basic.rs b/src/test/mir-opt/nll/region-subtyping-basic.rs index 622cc99983002..fa0dbe51c5dc3 100644 --- a/src/test/mir-opt/nll/region-subtyping-basic.rs +++ b/src/test/mir-opt/nll/region-subtyping-basic.rs @@ -22,9 +22,9 @@ fn main() { // END RUST SOURCE // START rustc.main.nll.0.mir -// | '_#2r | U0 | {bb2[0..=8], bb3[0], bb6[0..=1]} -// | '_#3r | U0 | {bb2[1..=8], bb3[0], bb6[0..=1]} -// | '_#4r | U0 | {bb2[4..=8], bb3[0], bb6[0..=1]} +// | '_#2r | U0 | {bb2[0..=8], bb3[0], bb5[0..=1]} +// | '_#3r | U0 | {bb2[1..=8], bb3[0], bb5[0..=1]} +// | '_#4r | U0 | {bb2[4..=8], bb3[0], bb5[0..=1]} // END rustc.main.nll.0.mir // START rustc.main.nll.0.mir // let _2: &'_#3r usize; diff --git a/src/test/mir-opt/remove_fake_borrows.rs b/src/test/mir-opt/remove_fake_borrows.rs index 6ac9cee79f53a..0f9c6f62c2bd3 100644 --- a/src/test/mir-opt/remove_fake_borrows.rs +++ b/src/test/mir-opt/remove_fake_borrows.rs @@ -19,21 +19,19 @@ fn main() { // bb0: { // FakeRead(ForMatchedPlace, _1); // _3 = discriminant(_1); -// switchInt(move _3) -> [1isize: bb4, otherwise: bb2]; +// switchInt(move _3) -> [1isize: bb3, otherwise: bb2]; // } // bb1: { -// goto -> bb5; +// goto -> bb4; // } // bb2: { -// goto -> bb8; +// _0 = const 1i32; +// goto -> bb7; // } // bb3: { -// unreachable; -// } -// bb4: { // switchInt((*(*((_1 as Some).0: &' &' i32)))) -> [0i32: bb1, otherwise: bb2]; // } -// bb5: { +// bb4: { // _4 = &shallow _1; // _5 = &shallow ((_1 as Some).0: &' &' i32); // _6 = &shallow (*((_1 as Some).0: &' &' i32)); @@ -44,25 +42,21 @@ fn main() { // FakeRead(ForMatchGuard, _5); // FakeRead(ForMatchGuard, _6); // FakeRead(ForMatchGuard, _7); -// switchInt(move _8) -> [false: bb7, otherwise: bb6]; +// switchInt(move _8) -> [false: bb6, otherwise: bb5]; // } -// bb6: { +// bb5: { // StorageDead(_8); // _0 = const 0i32; -// goto -> bb9; +// goto -> bb7; // } -// bb7: { +// bb6: { // StorageDead(_8); // goto -> bb2; // } -// bb8: { -// _0 = const 1i32; -// goto -> bb9; -// } -// bb9: { +// bb7: { // return; // } -// bb10 (cleanup): { +// bb8 (cleanup): { // resume; // } // END rustc.match_guard.CleanupNonCodegenStatements.before.mir @@ -71,21 +65,19 @@ fn main() { // bb0: { // nop; // _3 = discriminant(_1); -// switchInt(move _3) -> [1isize: bb4, otherwise: bb2]; +// switchInt(move _3) -> [1isize: bb3, otherwise: bb2]; // } // bb1: { -// goto -> bb5; +// goto -> bb4; // } // bb2: { -// goto -> bb8; +// _0 = const 1i32; +// goto -> bb7; // } // bb3: { -// unreachable; -// } -// bb4: { // switchInt((*(*((_1 as Some).0: &' &' i32)))) -> [0i32: bb1, otherwise: bb2]; // } -// bb5: { +// bb4: { // nop; // nop; // nop; @@ -96,25 +88,21 @@ fn main() { // nop; // nop; // nop; -// switchInt(move _8) -> [false: bb7, otherwise: bb6]; +// switchInt(move _8) -> [false: bb6, otherwise: bb5]; // } -// bb6: { +// bb5: { // StorageDead(_8); // _0 = const 0i32; -// goto -> bb9; +// goto -> bb7; // } -// bb7: { +// bb6: { // StorageDead(_8); // goto -> bb2; // } -// bb8: { -// _0 = const 1i32; -// goto -> bb9; -// } -// bb9: { +// bb7: { // return; // } -// bb10 (cleanup): { +// bb8 (cleanup): { // resume; // } // END rustc.match_guard.CleanupNonCodegenStatements.after.mir diff --git a/src/test/mir-opt/simplify_if.rs b/src/test/mir-opt/simplify_if.rs index 35512b94c0c8c..471c1df3300f0 100644 --- a/src/test/mir-opt/simplify_if.rs +++ b/src/test/mir-opt/simplify_if.rs @@ -8,12 +8,12 @@ fn main() { // START rustc.main.SimplifyBranches-after-const-prop.before.mir // bb0: { // ... -// switchInt(const false) -> [false: bb3, otherwise: bb1]; +// switchInt(const false) -> [false: bb1, otherwise: bb2]; // } // END rustc.main.SimplifyBranches-after-const-prop.before.mir // START rustc.main.SimplifyBranches-after-const-prop.after.mir // bb0: { // ... -// goto -> bb3; +// goto -> bb1; // } // END rustc.main.SimplifyBranches-after-const-prop.after.mir diff --git a/src/test/mir-opt/simplify_match.rs b/src/test/mir-opt/simplify_match.rs index 0192aa01d0188..8624899a0abf2 100644 --- a/src/test/mir-opt/simplify_match.rs +++ b/src/test/mir-opt/simplify_match.rs @@ -9,14 +9,14 @@ fn main() { // START rustc.main.SimplifyBranches-after-copy-prop.before.mir // bb0: { // ... -// switchInt(const false) -> [false: bb3, otherwise: bb1]; +// switchInt(const false) -> [false: bb1, otherwise: bb2]; // } // bb1: { // END rustc.main.SimplifyBranches-after-copy-prop.before.mir // START rustc.main.SimplifyBranches-after-copy-prop.after.mir // bb0: { // ... -// goto -> bb3; +// goto -> bb1; // } // bb1: { // END rustc.main.SimplifyBranches-after-copy-prop.after.mir diff --git a/src/test/ui/borrowck/borrowck-mut-borrow-linear-errors.stderr b/src/test/ui/borrowck/borrowck-mut-borrow-linear-errors.stderr index a8d00d152344b..ca1496a6c8d9b 100644 --- a/src/test/ui/borrowck/borrowck-mut-borrow-linear-errors.stderr +++ b/src/test/ui/borrowck/borrowck-mut-borrow-linear-errors.stderr @@ -2,22 +2,22 @@ error[E0499]: cannot borrow `x` as mutable more than once at a time --> $DIR/borrowck-mut-borrow-linear-errors.rs:10:30 | LL | 1 => { addr.push(&mut x); } - | ---- ^^^^^^ second mutable borrow occurs here - | | - | first borrow later used here + | ^^^^^^ second mutable borrow occurs here LL | 2 => { addr.push(&mut x); } LL | _ => { addr.push(&mut x); } - | ------ first mutable borrow occurs here + | ---- ------ first mutable borrow occurs here + | | + | first borrow later used here error[E0499]: cannot borrow `x` as mutable more than once at a time --> $DIR/borrowck-mut-borrow-linear-errors.rs:11:30 | -LL | 1 => { addr.push(&mut x); } - | ---- first borrow later used here LL | 2 => { addr.push(&mut x); } | ^^^^^^ second mutable borrow occurs here LL | _ => { addr.push(&mut x); } - | ------ first mutable borrow occurs here + | ---- ------ first mutable borrow occurs here + | | + | first borrow later used here error[E0499]: cannot borrow `x` as mutable more than once at a time --> $DIR/borrowck-mut-borrow-linear-errors.rs:12:30 diff --git a/src/test/ui/consts/const-eval/match-test-ptr-null.rs b/src/test/ui/consts/const-eval/match-test-ptr-null.rs index e0af01aeef46b..50757afaf5651 100644 --- a/src/test/ui/consts/const-eval/match-test-ptr-null.rs +++ b/src/test/ui/consts/const-eval/match-test-ptr-null.rs @@ -6,6 +6,7 @@ fn main() { match &1 as *const i32 as usize { //~^ ERROR casting pointers to integers in constants //~| NOTE for more information, see + //~| ERROR constant contains unimplemented expression type 0 => 42, //~ ERROR constant contains unimplemented expression type //~^ NOTE "pointer arithmetic or comparison" needs an rfc before being allowed //~| ERROR evaluation of constant value failed diff --git a/src/test/ui/consts/const-eval/match-test-ptr-null.stderr b/src/test/ui/consts/const-eval/match-test-ptr-null.stderr index d005e09b28a12..167d5ad8d61fe 100644 --- a/src/test/ui/consts/const-eval/match-test-ptr-null.stderr +++ b/src/test/ui/consts/const-eval/match-test-ptr-null.stderr @@ -8,18 +8,24 @@ LL | match &1 as *const i32 as usize { = help: add #![feature(const_raw_ptr_to_usize_cast)] to the crate attributes to enable error[E0019]: constant contains unimplemented expression type - --> $DIR/match-test-ptr-null.rs:9:13 + --> $DIR/match-test-ptr-null.rs:6:15 + | +LL | match &1 as *const i32 as usize { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0019]: constant contains unimplemented expression type + --> $DIR/match-test-ptr-null.rs:10:13 | LL | 0 => 42, | ^ error[E0080]: evaluation of constant value failed - --> $DIR/match-test-ptr-null.rs:9:13 + --> $DIR/match-test-ptr-null.rs:10:13 | LL | 0 => 42, | ^ "pointer arithmetic or comparison" needs an rfc before being allowed inside constants -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors Some errors have detailed explanations: E0019, E0080, E0658. For more information about an error, try `rustc --explain E0019`. diff --git a/src/test/ui/consts/const-match-pattern-arm.rs b/src/test/ui/consts/const-match-pattern-arm.rs index 3b985269a56c9..6ed3ac2356243 100644 --- a/src/test/ui/consts/const-match-pattern-arm.rs +++ b/src/test/ui/consts/const-match-pattern-arm.rs @@ -1,6 +1,7 @@ #![allow(warnings)] const x: bool = match Some(true) { + //~^ ERROR: constant contains unimplemented expression type [E0019] Some(value) => true, //~^ ERROR: constant contains unimplemented expression type [E0019] _ => false @@ -8,6 +9,7 @@ const x: bool = match Some(true) { const y: bool = { match Some(true) { + //~^ ERROR: constant contains unimplemented expression type [E0019] Some(value) => true, //~^ ERROR: constant contains unimplemented expression type [E0019] _ => false diff --git a/src/test/ui/consts/const-match-pattern-arm.stderr b/src/test/ui/consts/const-match-pattern-arm.stderr index c793cc0cd780a..709b66b7bf016 100644 --- a/src/test/ui/consts/const-match-pattern-arm.stderr +++ b/src/test/ui/consts/const-match-pattern-arm.stderr @@ -1,15 +1,27 @@ error[E0019]: constant contains unimplemented expression type - --> $DIR/const-match-pattern-arm.rs:4:5 + --> $DIR/const-match-pattern-arm.rs:3:23 + | +LL | const x: bool = match Some(true) { + | ^^^^^^^^^^ + +error[E0019]: constant contains unimplemented expression type + --> $DIR/const-match-pattern-arm.rs:5:5 | LL | Some(value) => true, | ^^^^^^^^^^^ error[E0019]: constant contains unimplemented expression type - --> $DIR/const-match-pattern-arm.rs:11:9 + --> $DIR/const-match-pattern-arm.rs:11:11 + | +LL | match Some(true) { + | ^^^^^^^^^^ + +error[E0019]: constant contains unimplemented expression type + --> $DIR/const-match-pattern-arm.rs:13:9 | LL | Some(value) => true, | ^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0019`. diff --git a/src/test/ui/consts/single_variant_match_ice.rs b/src/test/ui/consts/single_variant_match_ice.rs index 6002506689e12..75793c904838d 100644 --- a/src/test/ui/consts/single_variant_match_ice.rs +++ b/src/test/ui/consts/single_variant_match_ice.rs @@ -2,12 +2,12 @@ enum Foo { Prob, } -const FOO: u32 = match Foo::Prob { - Foo::Prob => 42, //~ ERROR unimplemented expression type +const FOO: u32 = match Foo::Prob { //~ ERROR unimplemented expression type + Foo::Prob => 42, }; -const BAR: u32 = match Foo::Prob { - x => 42, //~ ERROR unimplemented expression type +const BAR: u32 = match Foo::Prob { //~ ERROR unimplemented expression type + x => 42, }; impl Foo { @@ -15,7 +15,8 @@ impl Foo { use self::Foo::*; match *self { - Prob => 0x1, //~ ERROR loops and conditional expressions are not stable in const fn + //~^ ERROR loops and conditional expressions are not stable in const fn + Prob => 0x1, } } } diff --git a/src/test/ui/consts/single_variant_match_ice.stderr b/src/test/ui/consts/single_variant_match_ice.stderr index 1e092c8af9967..851733726ac0f 100644 --- a/src/test/ui/consts/single_variant_match_ice.stderr +++ b/src/test/ui/consts/single_variant_match_ice.stderr @@ -1,20 +1,20 @@ error[E0019]: constant contains unimplemented expression type - --> $DIR/single_variant_match_ice.rs:6:5 + --> $DIR/single_variant_match_ice.rs:5:24 | -LL | Foo::Prob => 42, - | ^^^^^^^^^ +LL | const FOO: u32 = match Foo::Prob { + | ^^^^^^^^^ error[E0019]: constant contains unimplemented expression type - --> $DIR/single_variant_match_ice.rs:10:5 + --> $DIR/single_variant_match_ice.rs:9:24 | -LL | x => 42, - | ^ +LL | const BAR: u32 = match Foo::Prob { + | ^^^^^^^^^ error[E0723]: loops and conditional expressions are not stable in const fn - --> $DIR/single_variant_match_ice.rs:18:13 + --> $DIR/single_variant_match_ice.rs:17:15 | -LL | Prob => 0x1, - | ^^^^ +LL | match *self { + | ^^^^^ | = note: for more information, see issue https://github.com/rust-lang/rust/issues/57563 = help: add #![feature(const_fn)] to the crate attributes to enable diff --git a/src/test/ui/issues/issue-46843.rs b/src/test/ui/issues/issue-46843.rs index a310de624d271..aa252efea464b 100644 --- a/src/test/ui/issues/issue-46843.rs +++ b/src/test/ui/issues/issue-46843.rs @@ -4,7 +4,9 @@ fn non_const() -> Thing { Thing::This } -pub const Q: i32 = match non_const() { //~ ERROR E0015 +pub const Q: i32 = match non_const() { + //~^ ERROR E0015 + //~^^ ERROR unimplemented expression type Thing::This => 1, //~ ERROR unimplemented expression type Thing::That => 0 }; diff --git a/src/test/ui/issues/issue-46843.stderr b/src/test/ui/issues/issue-46843.stderr index b7abf0213b0d5..92ee154552c68 100644 --- a/src/test/ui/issues/issue-46843.stderr +++ b/src/test/ui/issues/issue-46843.stderr @@ -5,12 +5,18 @@ LL | pub const Q: i32 = match non_const() { | ^^^^^^^^^^^ error[E0019]: constant contains unimplemented expression type - --> $DIR/issue-46843.rs:8:5 + --> $DIR/issue-46843.rs:7:26 + | +LL | pub const Q: i32 = match non_const() { + | ^^^^^^^^^^^ + +error[E0019]: constant contains unimplemented expression type + --> $DIR/issue-46843.rs:10:5 | LL | Thing::This => 1, | ^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors Some errors have detailed explanations: E0015, E0019. For more information about an error, try `rustc --explain E0015`. diff --git a/src/test/ui/lifetimes/lifetime-errors/liveness-assign-imm-local-notes.stderr b/src/test/ui/lifetimes/lifetime-errors/liveness-assign-imm-local-notes.stderr index c646912d3b679..13b6a7bbef321 100644 --- a/src/test/ui/lifetimes/lifetime-errors/liveness-assign-imm-local-notes.stderr +++ b/src/test/ui/lifetimes/lifetime-errors/liveness-assign-imm-local-notes.stderr @@ -28,6 +28,9 @@ LL | let x; ... LL | x = 1; | ^^^^^ cannot assign twice to immutable variable +LL | } else { +LL | x = 2; + | ----- first assignment to `x` error[E0384]: cannot assign twice to immutable variable `x` --> $DIR/liveness-assign-imm-local-notes.rs:32:13 @@ -35,9 +38,6 @@ error[E0384]: cannot assign twice to immutable variable `x` LL | let x; | - help: make this binding mutable: `mut x` ... -LL | x = 1; - | ----- first assignment to `x` -LL | } else { LL | x = 2; | ^^^^^ cannot assign twice to immutable variable From ef1fc86b521f457f736a8e110c851133cd953149 Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Sat, 25 May 2019 16:26:57 +0100 Subject: [PATCH 4/7] Call str::eq and <[T]>::eq for match comparisons We used to call the less efficient `<&str>::eq` and `<&[T]>::eq`. --- src/librustc_mir/build/matches/test.rs | 66 ++++++++------------------ src/librustc_mir/hair/cx/mod.rs | 3 +- src/libsyntax_pos/symbol.rs | 1 + 3 files changed, 23 insertions(+), 47 deletions(-) diff --git a/src/librustc_mir/build/matches/test.rs b/src/librustc_mir/build/matches/test.rs index bbaf92bf9f562..47f0aa82a417a 100644 --- a/src/librustc_mir/build/matches/test.rs +++ b/src/librustc_mir/build/matches/test.rs @@ -15,7 +15,9 @@ use rustc::ty::{self, Ty, adjustment::{PointerCast}}; use rustc::ty::util::IntTypeExt; use rustc::ty::layout::VariantIdx; use rustc::mir::*; -use rustc::hir::{RangeEnd, Mutability}; +use rustc::hir::RangeEnd; +use syntax_pos::symbol::sym; + use std::cmp::Ordering; impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { @@ -252,10 +254,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } TestKind::Eq { value, ty } => { - // Use `PartialEq::eq` instead of `BinOp::Eq` - // (the binop can only handle primitives) if let [success, fail] = *target_blocks { if !ty.is_scalar() { + // Use `PartialEq::eq` instead of `BinOp::Eq` + // (the binop can only handle primitives) self.non_scalar_compare( block, success, @@ -368,7 +370,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { ); } - /// Compare using `std::compare::PartialEq::eq` + /// Compare two `&T` values using `::eq` fn non_scalar_compare( &mut self, block: BasicBlock, @@ -381,8 +383,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { ) { use rustc::middle::lang_items::EqTraitLangItem; - let mut expect = self.literal_operand(source_info.span, ty, value); - let val = Operand::Copy(place.clone()); + let mut expect = self.literal_operand(source_info.span, value.ty, value); + let mut val = Operand::Copy(place.clone()); // If we're using `b"..."` as a pattern, we need to insert an // unsizing coercion, as the byte string has the type `&[u8; N]`. @@ -399,7 +401,6 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { }; let opt_ref_ty = unsize(ty); let opt_ref_test_ty = unsize(value.ty); - let mut place = place.clone(); match (opt_ref_ty, opt_ref_test_ty) { // nothing to do, neither is an array (None, None) => {}, @@ -409,56 +410,33 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // make both a slice ty = tcx.mk_imm_ref(region, tcx.mk_slice(elem_ty)); if opt_ref_ty.is_some() { - place = self.temp(ty, source_info.span); + let temp = self.temp(ty, source_info.span); self.cfg.push_assign( - block, source_info, &place, Rvalue::Cast( + block, source_info, &temp, Rvalue::Cast( CastKind::Pointer(PointerCast::Unsize), val, ty ) ); + val = Operand::Move(temp); } if opt_ref_test_ty.is_some() { - let array = self.literal_operand( - source_info.span, - value.ty, - value, - ); - let slice = self.temp(ty, source_info.span); self.cfg.push_assign( block, source_info, &slice, Rvalue::Cast( - CastKind::Pointer(PointerCast::Unsize), array, ty + CastKind::Pointer(PointerCast::Unsize), expect, ty ) ); expect = Operand::Move(slice); } }, } - let eq_def_id = self.hir.tcx().require_lang_item(EqTraitLangItem); - let (mty, method) = self.hir.trait_method(eq_def_id, "eq", ty, &[ty.into()]); - let re_erased = self.hir.tcx().lifetimes.re_erased; - // take the argument by reference - let tam = ty::TypeAndMut { - ty, - mutbl: Mutability::MutImmutable, + let deref_ty = match ty.sty { + ty::Ref(_, deref_ty, _) => deref_ty, + _ => bug!("non_scalar_compare called on non-reference type: {}", ty), }; - let ref_ty = self.hir.tcx().mk_ref(re_erased, tam); - - // let lhs_ref_place = &lhs; - let ref_rvalue = Rvalue::Ref(re_erased, BorrowKind::Shared, place); - let lhs_ref_place = self.temp(ref_ty, source_info.span); - self.cfg.push_assign(block, source_info, &lhs_ref_place, ref_rvalue); - let val = Operand::Move(lhs_ref_place); - // let rhs_place = rhs; - let rhs_place = self.temp(ty, source_info.span); - self.cfg.push_assign(block, source_info, &rhs_place, Rvalue::Use(expect)); - - // let rhs_ref_place = &rhs_place; - let ref_rvalue = Rvalue::Ref(re_erased, BorrowKind::Shared, rhs_place); - let rhs_ref_place = self.temp(ref_ty, source_info.span); - self.cfg.push_assign(block, source_info, &rhs_ref_place, ref_rvalue); - let expect = Operand::Move(rhs_ref_place); + let eq_def_id = self.hir.tcx().require_lang_item(EqTraitLangItem); + let (mty, method) = self.hir.trait_method(eq_def_id, sym::eq, deref_ty, &[deref_ty.into()]); let bool_ty = self.hir.bool_ty(); let eq_result = self.temp(bool_ty, source_info.span); @@ -469,12 +447,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { span: source_info.span, ty: mty, - // FIXME(#54571): This constant comes from user - // input (a constant in a pattern). Are - // there forms where users can add type - // annotations here? For example, an - // associated constant? Need to - // experiment. + // FIXME(#54571): This constant comes from user input (a + // constant in a pattern). Are there forms where users can add + // type annotations here? For example, an associated constant? + // Need to experiment. user_ty: None, literal: method, diff --git a/src/librustc_mir/hair/cx/mod.rs b/src/librustc_mir/hair/cx/mod.rs index ff3383f6e14c0..92df82984412f 100644 --- a/src/librustc_mir/hair/cx/mod.rs +++ b/src/librustc_mir/hair/cx/mod.rs @@ -168,11 +168,10 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> { pub fn trait_method(&mut self, trait_def_id: DefId, - method_name: &str, + method_name: Symbol, self_ty: Ty<'tcx>, params: &[Kind<'tcx>]) -> (Ty<'tcx>, &'tcx ty::Const<'tcx>) { - let method_name = Symbol::intern(method_name); let substs = self.tcx.mk_substs_trait(self_ty, params); for item in self.tcx.associated_items(trait_def_id) { if item.kind == ty::AssocKind::Method && item.ident.name == method_name { diff --git a/src/libsyntax_pos/symbol.rs b/src/libsyntax_pos/symbol.rs index 9ca64a1231f08..0d0233cc2e986 100644 --- a/src/libsyntax_pos/symbol.rs +++ b/src/libsyntax_pos/symbol.rs @@ -247,6 +247,7 @@ symbols! { eh_personality, eh_unwind_resume, enable, + eq, err, Err, Equal, From a1d0266878793bc8b2bf50958eb529005ed19da0 Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Sat, 25 May 2019 17:11:27 +0100 Subject: [PATCH 5/7] Generate MIR thats easier for llvm for str matches LLVM appears to prefer (spend less time optimizing) long if chains if it receives them in approzimately source order. This fixes a ~10% regression for optimized builds of the encoding benchmark on perf.rlo due to the changes to decision tree construction. --- src/librustc_mir/build/matches/mod.rs | 77 ++++++++++++++------------ src/librustc_mir/build/matches/test.rs | 73 +++++++++++++----------- 2 files changed, 82 insertions(+), 68 deletions(-) diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs index 521aca56108a6..db4f9e55df664 100644 --- a/src/librustc_mir/build/matches/mod.rs +++ b/src/librustc_mir/build/matches/mod.rs @@ -1198,51 +1198,58 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { debug!("tested_candidates: {}", total_candidate_count - candidates.len()); debug!("untested_candidates: {}", candidates.len()); - // For each outcome of test, process the candidates that still - // apply. Collect a list of blocks where control flow will - // branch if one of the `target_candidate` sets is not - // exhaustive. - if !candidates.is_empty() { - let remainder_start = &mut None; - self.match_candidates( - span, - remainder_start, - otherwise_block, - candidates, - fake_borrows, - ); - otherwise_block = Some(remainder_start.unwrap()); - }; - let target_blocks: Vec<_> = target_candidates.into_iter().map(|mut candidates| { - if candidates.len() != 0 { - let candidate_start = &mut None; - self.match_candidates( + // HACK(matthewjasper) This is a closure so that we can let the test + // create its blocks before the rest of the match. This currently + // improves the speed of llvm when optimizing long string literal + // matches + let make_target_blocks = move |this: &mut Self| -> Vec { + // For each outcome of test, process the candidates that still + // apply. Collect a list of blocks where control flow will + // branch if one of the `target_candidate` sets is not + // exhaustive. + if !candidates.is_empty() { + let remainder_start = &mut None; + this.match_candidates( span, - candidate_start, + remainder_start, otherwise_block, - &mut *candidates, + candidates, fake_borrows, ); - candidate_start.unwrap() - } else { - *otherwise_block.get_or_insert_with(|| { - let unreachable = self.cfg.start_new_block(); - let source_info = self.source_info(span); - self.cfg.terminate( - unreachable, - source_info, - TerminatorKind::Unreachable, + otherwise_block = Some(remainder_start.unwrap()); + }; + + target_candidates.into_iter().map(|mut candidates| { + if candidates.len() != 0 { + let candidate_start = &mut None; + this.match_candidates( + span, + candidate_start, + otherwise_block, + &mut *candidates, + fake_borrows, ); - unreachable - }) - } - }).collect(); + candidate_start.unwrap() + } else { + *otherwise_block.get_or_insert_with(|| { + let unreachable = this.cfg.start_new_block(); + let source_info = this.source_info(span); + this.cfg.terminate( + unreachable, + source_info, + TerminatorKind::Unreachable, + ); + unreachable + }) + } + }).collect() + }; self.perform_test( block, &match_place, &test, - target_blocks, + make_target_blocks, ); } diff --git a/src/librustc_mir/build/matches/test.rs b/src/librustc_mir/build/matches/test.rs index 47f0aa82a417a..f93ca4b31b3a8 100644 --- a/src/librustc_mir/build/matches/test.rs +++ b/src/librustc_mir/build/matches/test.rs @@ -168,7 +168,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { block: BasicBlock, place: &Place<'tcx>, test: &Test<'tcx>, - target_blocks: Vec, + make_target_blocks: impl FnOnce(&mut Self) -> Vec, ) { debug!("perform_test({:?}, {:?}: {:?}, {:?})", block, @@ -179,6 +179,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { let source_info = self.source_info(test.span); match test.kind { TestKind::Switch { adt_def, ref variants } => { + let target_blocks = make_target_blocks(self); // Variants is a BitVec of indexes into adt_def.variants. let num_enum_variants = adt_def.variants.len(); let used_variants = variants.count(); @@ -223,6 +224,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } TestKind::SwitchInt { switch_ty, ref options, indices: _ } => { + let target_blocks = make_target_blocks(self); let terminator = if switch_ty.sty == ty::Bool { assert!(options.len() > 0 && options.len() <= 2); if let [first_bb, second_bb] = *target_blocks { @@ -254,38 +256,38 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } TestKind::Eq { value, ty } => { - if let [success, fail] = *target_blocks { - if !ty.is_scalar() { - // Use `PartialEq::eq` instead of `BinOp::Eq` - // (the binop can only handle primitives) - self.non_scalar_compare( - block, - success, - fail, - source_info, - value, - place, - ty, - ); - } else { + if !ty.is_scalar() { + // Use `PartialEq::eq` instead of `BinOp::Eq` + // (the binop can only handle primitives) + self.non_scalar_compare( + block, + make_target_blocks, + source_info, + value, + place, + ty, + ); + } else { + if let [success, fail] = *make_target_blocks(self) { let val = Operand::Copy(place.clone()); let expect = self.literal_operand(test.span, ty, value); self.compare(block, success, fail, source_info, BinOp::Eq, expect, val); + } else { + bug!("`TestKind::Eq` should have two target blocks"); } - } else { - bug!("`TestKind::Eq` should have two target blocks") - }; + } } TestKind::Range(PatternRange { ref lo, ref hi, ty, ref end }) => { + let lower_bound_success = self.cfg.start_new_block(); + let target_blocks = make_target_blocks(self); + // Test `val` by computing `lo <= val && val <= hi`, using primitive comparisons. let lo = self.literal_operand(test.span, ty, lo); let hi = self.literal_operand(test.span, ty, hi); let val = Operand::Copy(place.clone()); if let [success, fail] = *target_blocks { - let lower_bound_success = self.cfg.start_new_block(); - self.compare( block, lower_bound_success, @@ -306,6 +308,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } TestKind::Len { len, op } => { + let target_blocks = make_target_blocks(self); + let usize_ty = self.hir.usize_ty(); let actual = self.temp(usize_ty, test.span); @@ -374,8 +378,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { fn non_scalar_compare( &mut self, block: BasicBlock, - success_block: BasicBlock, - fail_block: BasicBlock, + make_target_blocks: impl FnOnce(&mut Self) -> Vec, source_info: SourceInfo, value: &'tcx ty::Const<'tcx>, place: &Place<'tcx>, @@ -461,17 +464,21 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { from_hir_call: false, }); - // check the result - self.cfg.terminate( - eq_block, - source_info, - TerminatorKind::if_( - self.hir.tcx(), - Operand::Move(eq_result), - success_block, - fail_block, - ), - ); + if let [success_block, fail_block] = *make_target_blocks(self) { + // check the result + self.cfg.terminate( + eq_block, + source_info, + TerminatorKind::if_( + self.hir.tcx(), + Operand::Move(eq_result), + success_block, + fail_block, + ), + ); + } else { + bug!("`TestKind::Eq` should have two target blocks") + } } /// Given that we are performing `test` against `test_place`, this job From df3de7b4b3e91a4a8d2bbea60970d74d445c3da0 Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Sat, 25 May 2019 18:26:34 +0100 Subject: [PATCH 6/7] Add some more comments on how TestKind works --- src/librustc/mir/mod.rs | 4 ++-- src/librustc_mir/build/matches/mod.rs | 27 +++++++++++++++++++++----- src/librustc_mir/build/matches/test.rs | 6 ++++++ 3 files changed, 30 insertions(+), 7 deletions(-) diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index f18afe7b6327c..6df43596673cf 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -1196,8 +1196,8 @@ pub enum TerminatorKind<'tcx> { FalseEdges { /// The target normal control flow will take real_target: BasicBlock, - /// A block control flow could conceptually take, but won't - /// in practice + /// A block control flow could conceptually jump to, but won't in + /// practice imaginary_target: BasicBlock, }, /// A terminator for blocks that only take one path in reality, but where we diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs index db4f9e55df664..254f2a4e2a25c 100644 --- a/src/librustc_mir/build/matches/mod.rs +++ b/src/librustc_mir/build/matches/mod.rs @@ -715,29 +715,46 @@ pub struct MatchPair<'pat, 'tcx: 'pat> { #[derive(Clone, Debug, PartialEq)] enum TestKind<'tcx> { - // test the branches of enum + /// Test the branches of enum. Switch { + /// The enum being tested adt_def: &'tcx ty::AdtDef, + /// The set of variants that we should create a branch for. We also + /// create an additional "otherwise" case. variants: BitSet, }, - // test the branches of enum + /// Test what value an `integer`, `bool` or `char` has. SwitchInt { + /// The type of the value that we're testing. switch_ty: Ty<'tcx>, + /// The (ordered) set of values that we test for. + /// + /// For integers and `char`s we create a branch to each of the values in + /// `options`, as well as an "otherwise" branch for all other values, even + /// in the (rare) case that options is exhaustive. + /// + /// For `bool` we always generate two edges, one for `true` and one for + /// `false`. options: Vec, + /// Reverse map used to ensure that the values in `options` are unique. indices: FxHashMap<&'tcx ty::Const<'tcx>, usize>, }, - // test for equality + /// Test for equality with value, possibly after an unsizing coercion to + /// `ty`, Eq { value: &'tcx ty::Const<'tcx>, + // Integer types are handled by `SwitchInt`, and constants with ADT + // types are converted back into patterns, so this can only be `&str`, + // `&[T]`, `f32` or `f64`. ty: Ty<'tcx>, }, - // test whether the value falls within an inclusive or exclusive range + /// Test whether the value falls within an inclusive or exclusive range Range(PatternRange<'tcx>), - // test length of the slice is equal to len + /// Test length of the slice is equal to len Len { len: u64, op: BinOp, diff --git a/src/librustc_mir/build/matches/test.rs b/src/librustc_mir/build/matches/test.rs index f93ca4b31b3a8..21ff16104dcfb 100644 --- a/src/librustc_mir/build/matches/test.rs +++ b/src/librustc_mir/build/matches/test.rs @@ -807,10 +807,16 @@ impl Test<'_> { 2 } TestKind::Switch { adt_def, .. } => { + // While the switch that we generate doesn't test for all + // variants, we have a target for each variant and the + // otherwise case, and we make sure that all of the cases not + // specified have the same block. adt_def.variants.len() + 1 } TestKind::SwitchInt { switch_ty, ref options, .. } => { if switch_ty.is_bool() { + // `bool` is special cased in `perform_test` to always + // branch to two blocks. 2 } else { options.len() + 1 From 89ea69ab238d1d61d00a44e72061f6794d937255 Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Sat, 25 May 2019 20:13:53 +0100 Subject: [PATCH 7/7] Add a test for simple matches --- src/test/mir-opt/simple-match.rs | 39 ++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 src/test/mir-opt/simple-match.rs diff --git a/src/test/mir-opt/simple-match.rs b/src/test/mir-opt/simple-match.rs new file mode 100644 index 0000000000000..fc1a3bb1bf453 --- /dev/null +++ b/src/test/mir-opt/simple-match.rs @@ -0,0 +1,39 @@ +// Test that we don't generate unnecessarily large MIR for very simple matches + +fn match_bool(x: bool) -> usize { + match x { + true => 10, + _ => 20, + } +} + +fn main() {} + + +// END RUST SOURCE +// START rustc.match_bool.mir_map.0.mir +// bb0: { +// FakeRead(ForMatchedPlace, _1); +// switchInt(_1) -> [false: bb3, otherwise: bb2]; +// } +// bb1 (cleanup): { +// resume; +// } +// bb2: { +// falseEdges -> [real: bb4, imaginary: bb3]; +// } +// bb3: { +// _0 = const 20usize; +// goto -> bb5; +// } +// bb4: { +// _0 = const 10usize; +// goto -> bb5; +// } +// bb5: { +// goto -> bb6; +// } +// bb6: { +// return; +// } +// END rustc.match_bool.mir_map.0.mir