From d7f31a6aca886d179a7bd040aaa083ccfa5398eb Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Thu, 9 May 2019 23:01:39 +0100 Subject: [PATCH] Create fewer basic blocks in match MIR lowering --- src/librustc_mir/build/matches/mod.rs | 216 ++++---- src/librustc_mir/build/matches/test.rs | 480 ++++++++++-------- src/librustc_mir/build/mod.rs | 11 - src/librustc_mir/transform/qualify_consts.rs | 3 + .../transform/qualify_min_const_fn.rs | 3 + 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/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_false_edges.rs | 131 ++--- src/test/mir-opt/match_test.rs | 56 +- .../mir-opt/nll/region-subtyping-basic.rs | 6 +- src/test/mir-opt/remove_fake_borrows.rs | 60 +-- 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 | 12 +- .../ui/consts/single_variant_match_ice.stderr | 20 +- src/test/ui/issues/issue-46843.rs | 4 +- src/test/ui/issues/issue-46843.stderr | 10 +- .../liveness-assign-imm-local-notes.stderr | 6 +- 28 files changed, 618 insertions(+), 562 deletions(-) diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs index 4e6fe5c48eef3..12fc5743edaac 100644 --- a/src/librustc_mir/build/matches/mod.rs +++ b/src/librustc_mir/build/matches/mod.rs @@ -205,33 +205,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. @@ -264,19 +249,19 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { ); } else { arm_block = self.cfg.start_new_block(); - for candidate in candidates { + for candidate in candidates { let binding_end = self.bind_and_guard_matched_candidate( - candidate, - arm.guard.clone(), - &fake_borrow_temps, - scrutinee_span, - ); + 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 { @@ -793,11 +778,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 @@ -811,13 +795,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 @@ -840,52 +828,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, - &mut None, - ) } /// Link up matched candidates. For example, if we have something like @@ -909,7 +902,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!( @@ -957,16 +950,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 { @@ -1018,25 +1013,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 @@ -1154,8 +1130,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); @@ -1209,9 +1186,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(); @@ -1237,20 +1213,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 @@ -1344,10 +1348,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 a5834b02ffc82..61259f8bb71ab 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,161 +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 method = self.hir.tcx().mk_const(method); - - 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 }) => { @@ -375,20 +281,31 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { let hi = self.literal_operand(test.span, ty.clone(), hi.clone()); 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, @@ -397,44 +314,189 @@ 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: 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 method = self.hir.tcx().mk_const(method); + + 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 @@ -756,6 +818,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 16ab233bd2e36..2247e9d76b72a 100644 --- a/src/librustc_mir/build/mod.rs +++ b/src/librustc_mir/build/mod.rs @@ -987,17 +987,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 9a39e07172114..49fc6694a758f 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -1398,6 +1398,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 e1d41ba4fc509..89787ae6e806d 100644 --- a/src/librustc_mir/transform/qualify_min_const_fn.rs +++ b/src/librustc_mir/transform/qualify_min_const_fn.rs @@ -220,6 +220,9 @@ fn check_statement( check_rvalue(tcx, mir, rval, span) } + StatementKind::FakeRead(FakeReadCause::ForMatchedPlace, _) => { + Err((span, "`match` or `if let` in `const fn` is unstable".into())) + } StatementKind::FakeRead(_, place) => check_place(tcx, mir, 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/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 29446d2ecc23e..8e39efa3b120f 100644 --- a/src/test/mir-opt/issue-49232.rs +++ b/src/test/mir-opt/issue-49232.rs @@ -34,77 +34,59 @@ 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: { +// bb11: { // StorageDead(_4); -// goto -> bb18; +// goto -> bb12; // } -// bb18: { +// bb12: { // FakeRead(ForLet, _2); // StorageDead(_3); // StorageLive(_6); // _6 = &_2; -// _5 = const std::mem::drop(move _6) -> [return: bb19, unwind: bb4]; +// _5 = const std::mem::drop(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_false_edges.rs b/src/test/mir-opt/match_false_edges.rs index 7ac36a22274f3..ae8a129b11d50 100644 --- a/src/test/mir-opt/match_false_edges.rs +++ b/src/test/mir-opt/match_false_edges.rs @@ -45,64 +45,55 @@ fn main() { // _2 = std::option::Option::::Some(const 42i32,); // FakeRead(ForMatchedPlace, _2); // _3 = discriminant(_2); -// switchInt(move _3) -> [0isize: bb4, 1isize: bb2, otherwise: bb7]; +// switchInt(move _3) -> [0isize: bb4, 1isize: bb2, otherwise: bb5]; // } // bb1 (cleanup): { // resume; // } // bb2: { -// falseEdges -> [real: bb8, imaginary: bb3]; //pre_binding1 +// falseEdges -> [real: bb6, imaginary: bb3]; //pre_binding1 // } // bb3: { -// falseEdges -> [real: bb11, imaginary: bb4]; //pre_binding2 +// falseEdges -> [real: bb9, imaginary: bb4]; //pre_binding2 // } -// bb4: { -// falseEdges -> [real: bb12, imaginary: bb5]; //pre_binding3 +// bb4: { //pre_binding3 and arm3 +// _1 = (const 3i32, const 3i32); +// goto -> bb10; // } // bb5: { // unreachable; // } -// bb6: { // to pre_binding2 -// falseEdges -> [real: bb3, imaginary: bb3]; -// } -// bb7: { -// unreachable; -// } -// bb8: { // 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: bb9, unwind: bb1]; +// _7 = const guard() -> [return: bb7, unwind: bb1]; // } -// bb9: { +// bb7: { // FakeRead(ForMatchGuard, _4); // FakeRead(ForGuardBinding, _6); -// switchInt(move _7) -> [false: bb6, otherwise: bb10]; +// switchInt(move _7) -> [false: bb3, otherwise: bb8]; // } -// bb10: { +// bb8: { // StorageLive(_5); // _5 = ((_2 as Some).0: i32); // StorageLive(_8); // _8 = _5; // _1 = (const 1i32, move _8); // StorageDead(_8); -// goto -> bb13; +// goto -> bb10; // } -// bb11: { +// bb9: { // StorageLive(_9); // _9 = ((_2 as Some).0: i32); // StorageLive(_10); // _10 = _9; // _1 = (const 2i32, move _10); // StorageDead(_10); -// goto -> bb13; -// } -// bb12: { -// _1 = (const 3i32, const 3i32); -// goto -> bb13; +// goto -> bb10; // } -// bb13: { +// bb10: { // ... // return; // } @@ -114,64 +105,58 @@ fn main() { // _2 = std::option::Option::::Some(const 42i32,); // FakeRead(ForMatchedPlace, _2); // _3 = discriminant(_2); -// switchInt(move _3) -> [0isize: bb3, 1isize: bb2, otherwise: bb7]; +// switchInt(move _3) -> [0isize: bb3, 1isize: bb2, otherwise: bb5]; // } // bb1 (cleanup): { // resume; // } // bb2: { -// falseEdges -> [real: bb8, imaginary: bb3]; +// falseEdges -> [real: bb6, imaginary: bb3]; // } // bb3: { -// falseEdges -> [real: bb11, imaginary: bb4]; +// falseEdges -> [real: bb9, imaginary: bb10]; // } -// bb4: { -// falseEdges -> [real: bb12, imaginary: bb5]; +// bb4: { // to arm3 (can skip 2 since this is `Some`) +// falseEdges -> [real: bb10, imaginary: bb3]; // } // bb5: { // unreachable; // } -// bb6: { // to pre_binding3 (can skip 2 since this is `Some`) -// falseEdges -> [real: bb4, imaginary: bb3]; -// } -// bb7: { -// unreachable; -// } -// bb8: { // binding1 and guard +// bb6: { // binding1 and guard // StorageLive(_6); // _6 = &((_2 as Some).0: i32); // _4 = &shallow _2; // StorageLive(_7); -// _7 = const guard() -> [return: bb9, unwind: bb1]; +// _7 = const guard() -> [return: bb7, unwind: bb1]; // } -// bb9: { // end of guard +// bb7: { // end of guard // FakeRead(ForMatchGuard, _4); // FakeRead(ForGuardBinding, _6); -// switchInt(move _7) -> [false: bb6, otherwise: bb10]; +// switchInt(move _7) -> [false: bb4, otherwise: bb8]; // } -// bb10: { // arm1 +// bb8: { // arm1 // StorageLive(_5); // _5 = ((_2 as Some).0: i32); // StorageLive(_8); // _8 = _5; // _1 = (const 1i32, move _8); // StorageDead(_8); -// goto -> bb13; +// goto -> bb11; // } -// 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); // _10 = _9; // _1 = (const 2i32, move _10); // StorageDead(_10); -// goto -> bb13; +// goto -> bb11; // } -// bb13: { +// bb11: { // ... // return; // } @@ -182,85 +167,79 @@ 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: bb9, imaginary: bb3]; +// falseEdges -> [real: bb7, imaginary: bb3]; // } // bb3: { -// falseEdges -> [real: bb12, imaginary: bb4]; +// falseEdges -> [real: bb10, imaginary: bb4]; // } // bb4: { -// falseEdges -> [real: bb13, imaginary: bb5]; +// falseEdges -> [real: bb11, imaginary: bb14]; // } // bb5: { -// falseEdges -> [real: bb16, imaginary: bb6]; -// } -// bb6: { -// unreachable; -// } -// bb7: { // falseEdges -> [real: bb3, imaginary: bb3]; // } -// bb8: { -// falseEdges -> [real: bb5, imaginary: bb5]; +// bb6: { +// falseEdges -> [real: bb14, imaginary: bb14]; // } -// bb9: { // binding1: Some(w) if guard() +// bb7: { // binding1: Some(w) if guard() // StorageLive(_7); // _7 = &((_2 as Some).0: i32); // _5 = &shallow _2; // StorageLive(_8); -// _8 = const guard() -> [return: bb10, unwind: bb1]; +// _8 = const guard() -> [return: bb8, unwind: bb1]; // } -// bb10: { //end of guard +// bb8: { //end of guard // FakeRead(ForMatchGuard, _5); // FakeRead(ForGuardBinding, _7); -// switchInt(move _8) -> [false: bb7, otherwise: bb11]; +// switchInt(move _8) -> [false: bb5, otherwise: bb9]; // } -// bb11: { // set up bindings for arm1 +// bb9: { // set up bindings for arm1 // StorageLive(_6); // _6 = ((_2 as Some).0: i32); // _1 = const 1i32; -// goto -> bb17; +// goto -> bb15; // } -// bb12: { // binding2 & arm2 +// bb10: { // binding2 & arm2 // StorageLive(_9); // _9 = _2; // _1 = const 2i32; -// goto -> bb17; +// goto -> bb15; // } -// bb13: { // binding3: Some(y) if guard2(y) +// bb11: { // 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: bb14, unwind: bb1]; +// _12 = const guard2(move _13) -> [return: bb12, unwind: bb1]; // } -// bb14: { // end of guard2 +// bb12: { // end of guard2 // StorageDead(_13); // FakeRead(ForMatchGuard, _5); // FakeRead(ForGuardBinding, _11); -// switchInt(move _12) -> [false: bb8, otherwise: bb15]; +// switchInt(move _12) -> [false: bb6, otherwise: bb13]; // } -// bb15: { // binding4 & arm4 +// bb13: { // binding4 & arm4 // StorageLive(_10); // _10 = ((_2 as Some).0: i32); // _1 = const 3i32; -// goto -> bb17; +// goto -> bb15; // } -// bb16: { +// bb14: { // StorageLive(_14); // _14 = _2; // _1 = const 4i32; -// goto -> bb17; +// goto -> bb15; // } -// bb17: { +// bb15: { // ... // return; // } diff --git a/src/test/mir-opt/match_test.rs b/src/test/mir-opt/match_test.rs index a5317f98ef188..8a1fb4bc3361e 100644 --- a/src/test/mir-opt/match_test.rs +++ b/src/test/mir-opt/match_test.rs @@ -19,66 +19,60 @@ fn main() { // END RUST SOURCE // START rustc.main.SimplifyCfg-initial.after.mir // bb0: { -// ... -// switchInt(move _4) -> [false: bb7, otherwise: bb8]; +// ... +// switchInt(move _6) -> [false: bb6, otherwise: bb9]; // } // bb1: { -// falseEdges -> [real: bb12, imaginary: bb2]; +// falseEdges -> [real: bb10, 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; +// falseEdges -> [real: bb4, imaginary: bb2]; // } // bb6: { -// falseEdges -> [real: bb4, imaginary: bb2]; +// _4 = Le(const 10i32, _1); +// switchInt(move _4) -> [false: bb7, otherwise: bb8]; // } // bb7: { -// _6 = Le(const 10i32, _1); -// switchInt(move _6) -> [false: bb9, otherwise: bb10]; +// switchInt(_1) -> [-1i32: bb3, otherwise: bb4]; // } // bb8: { -// _5 = Lt(_1, const 10i32); -// switchInt(move _5) -> [false: bb7, otherwise: bb1]; +// _5 = Le(_1, const 20i32); +// switchInt(move _5) -> [false: bb7, otherwise: bb2]; // } // bb9: { -// switchInt(_1) -> [-1i32: bb3, otherwise: bb4]; +// _7 = Lt(_1, const 10i32); +// switchInt(move _7) -> [false: bb6, otherwise: bb1]; // } // bb10: { -// _7 = Le(_1, const 20i32); -// switchInt(move _7) -> [false: bb9, otherwise: bb2]; -// } -// bb11: { -// _3 = const 0i32; -// goto -> bb16; -// } -// bb12: { // _8 = &shallow _1; // StorageLive(_9); // _9 = _2; // FakeRead(ForMatchGuard, _8); -// switchInt(move _9) -> [false: bb6, otherwise: bb11]; +// switchInt(move _9) -> [false: bb5, otherwise: bb11]; // } -// bb13: { +// bb11: { +// _3 = const 0i32; +// goto -> bb14; +// } +// 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: { // StorageDead(_9); // _0 = (); // StorageDead(_2); 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 144348450a91b..2eeb9ef5eac15 100644 --- a/src/test/mir-opt/remove_fake_borrows.rs +++ b/src/test/mir-opt/remove_fake_borrows.rs @@ -21,28 +21,22 @@ fn main() { // bb0: { // FakeRead(ForMatchedPlace, _1); // _3 = discriminant(_1); -// switchInt(move _3) -> [1isize: bb5, otherwise: bb2]; +// switchInt(move _3) -> [1isize: bb4, otherwise: bb2]; // } // bb1: { -// goto -> bb7; +// goto -> bb5; // } // bb2: { -// goto -> bb8; +// _0 = const 1i32; +// goto -> bb7; // } // bb3: { -// unreachable; -// } -// bb4: { // goto -> bb2; // } -// bb5: { +// bb4: { // switchInt((*(*((_1 as Some).0: &' &' i32)))) -> [0i32: bb1, otherwise: bb2]; // } -// bb6: { -// _0 = const 0i32; -// goto -> bb9; -// } -// bb7: { +// bb5: { // _4 = &shallow _1; // _5 = &shallow ((_1 as Some).0: &' &' i32); // _6 = &shallow (*((_1 as Some).0: &' &' i32)); @@ -53,17 +47,17 @@ fn main() { // FakeRead(ForMatchGuard, _5); // FakeRead(ForMatchGuard, _6); // FakeRead(ForMatchGuard, _7); -// switchInt(move _8) -> [false: bb4, otherwise: bb6]; +// switchInt(move _8) -> [false: bb3, otherwise: bb6]; // } -// bb8: { -// _0 = const 1i32; -// goto -> bb9; +// bb6: { +// _0 = const 0i32; +// goto -> bb7; // } -// bb9: { +// bb7: { // StorageDead(_8); // return; // } -// bb10 (cleanup): { +// bb8 (cleanup): { // resume; // } // END rustc.match_guard.CleanupNonCodegenStatements.before.mir @@ -72,28 +66,22 @@ fn main() { // bb0: { // nop; // _3 = discriminant(_1); -// switchInt(move _3) -> [1isize: bb5, otherwise: bb2]; +// switchInt(move _3) -> [1isize: bb4, otherwise: bb2]; // } // bb1: { -// goto -> bb7; +// goto -> bb5; // } // bb2: { -// goto -> bb8; +// _0 = const 1i32; +// goto -> bb7; // } // bb3: { -// unreachable; -// } -// bb4: { // goto -> bb2; // } -// bb5: { +// bb4: { // switchInt((*(*((_1 as Some).0: &' &' i32)))) -> [0i32: bb1, otherwise: bb2]; // } -// bb6: { -// _0 = const 0i32; -// goto -> bb9; -// } -// bb7: { +// bb5: { // nop; // nop; // nop; @@ -104,17 +92,17 @@ fn main() { // nop; // nop; // nop; -// switchInt(move _8) -> [false: bb4, otherwise: bb6]; +// switchInt(move _8) -> [false: bb3, otherwise: bb6]; // } -// bb8: { -// _0 = const 1i32; -// goto -> bb9; +// bb6: { +// _0 = const 0i32; +// goto -> bb7; // } -// bb9: { +// bb7: { // StorageDead(_8); // 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 b2a99a6d446bb..64825e5c84e21 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-copy-prop.before.mir // bb0: { // ... -// switchInt(const false) -> [false: bb3, otherwise: bb1]; +// switchInt(const false) -> [false: bb1, otherwise: bb2]; // } // END rustc.main.SimplifyBranches-after-copy-prop.before.mir // START rustc.main.SimplifyBranches-after-copy-prop.after.mir // bb0: { // ... -// goto -> bb3; +// goto -> bb1; // } // END rustc.main.SimplifyBranches-after-copy-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 79dde3c18e8fa..3926aeb9137b1 100644 --- a/src/test/ui/consts/single_variant_match_ice.rs +++ b/src/test/ui/consts/single_variant_match_ice.rs @@ -2,20 +2,20 @@ 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 { pub const fn as_val(&self) -> u8 { use self::Foo::*; - match *self { - Prob => 0x1, //~ ERROR `if`, `match`, `&&` and `||` are not stable in const fn + match *self { //~ ERROR `match` or `if let` in `const fn` is unstable + 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 b8ad775f1c34f..4e787c354922b 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]: `if`, `match`, `&&` and `||` are not stable in const fn - --> $DIR/single_variant_match_ice.rs:18:13 +error[E0723]: `match` or `if let` in `const fn` is unstable + --> $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