| // Testing candidates |
| // |
| // After candidates have been simplified, the only match pairs that |
| // remain are those that require some sort of test. The functions here |
| // identify what tests are needed, perform the tests, and then filter |
| // the candidates based on the result. |
| |
| use crate::build::matches::{Candidate, MatchPair, Test, TestBranch, TestCase, TestKind}; |
| use crate::build::Builder; |
| use rustc_data_structures::fx::FxIndexMap; |
| use rustc_hir::{LangItem, RangeEnd}; |
| use rustc_middle::mir::*; |
| use rustc_middle::ty::util::IntTypeExt; |
| use rustc_middle::ty::GenericArg; |
| use rustc_middle::ty::{self, adjustment::PointerCoercion, Ty, TyCtxt}; |
| use rustc_middle::{bug, span_bug}; |
| use rustc_span::def_id::DefId; |
| use rustc_span::source_map::Spanned; |
| use rustc_span::symbol::{sym, Symbol}; |
| use rustc_span::{Span, DUMMY_SP}; |
| |
| use std::cmp::Ordering; |
| |
| impl<'a, 'tcx> Builder<'a, 'tcx> { |
| /// Identifies what test is needed to decide if `match_pair` is applicable. |
| /// |
| /// It is a bug to call this with a not-fully-simplified pattern. |
| pub(super) fn test<'pat>(&mut self, match_pair: &MatchPair<'pat, 'tcx>) -> Test<'tcx> { |
| let kind = match match_pair.test_case { |
| TestCase::Variant { adt_def, variant_index: _ } => TestKind::Switch { adt_def }, |
| |
| TestCase::Constant { .. } if match_pair.pattern.ty.is_bool() => TestKind::If, |
| TestCase::Constant { .. } if is_switch_ty(match_pair.pattern.ty) => TestKind::SwitchInt, |
| TestCase::Constant { value } => TestKind::Eq { value, ty: match_pair.pattern.ty }, |
| |
| TestCase::Range(range) => { |
| assert_eq!(range.ty, match_pair.pattern.ty); |
| TestKind::Range(Box::new(range.clone())) |
| } |
| |
| TestCase::Slice { len, variable_length } => { |
| let op = if variable_length { BinOp::Ge } else { BinOp::Eq }; |
| TestKind::Len { len: len as u64, op } |
| } |
| |
| TestCase::Deref { temp, mutability } => TestKind::Deref { temp, mutability }, |
| |
| TestCase::Never => TestKind::Never, |
| |
| TestCase::Or { .. } => bug!("or-patterns should have already been handled"), |
| |
| TestCase::Irrefutable { .. } => span_bug!( |
| match_pair.pattern.span, |
| "simplifiable pattern found: {:?}", |
| match_pair.pattern |
| ), |
| }; |
| |
| Test { span: match_pair.pattern.span, kind } |
| } |
| |
| #[instrument(skip(self, target_blocks, place), level = "debug")] |
| pub(super) fn perform_test( |
| &mut self, |
| match_start_span: Span, |
| scrutinee_span: Span, |
| block: BasicBlock, |
| otherwise_block: BasicBlock, |
| place: Place<'tcx>, |
| test: &Test<'tcx>, |
| target_blocks: FxIndexMap<TestBranch<'tcx>, BasicBlock>, |
| ) { |
| let place_ty = place.ty(&self.local_decls, self.tcx); |
| debug!(?place, ?place_ty); |
| let target_block = |branch| target_blocks.get(&branch).copied().unwrap_or(otherwise_block); |
| |
| let source_info = self.source_info(test.span); |
| match test.kind { |
| TestKind::Switch { adt_def } => { |
| let otherwise_block = target_block(TestBranch::Failure); |
| let switch_targets = SwitchTargets::new( |
| adt_def.discriminants(self.tcx).filter_map(|(idx, discr)| { |
| if let Some(&block) = target_blocks.get(&TestBranch::Variant(idx)) { |
| Some((discr.val, block)) |
| } else { |
| None |
| } |
| }), |
| otherwise_block, |
| ); |
| debug!("num_enum_variants: {}", adt_def.variants().len()); |
| let discr_ty = adt_def.repr().discr_type().to_ty(self.tcx); |
| let discr = self.temp(discr_ty, test.span); |
| self.cfg.push_assign( |
| block, |
| self.source_info(scrutinee_span), |
| discr, |
| Rvalue::Discriminant(place), |
| ); |
| self.cfg.terminate( |
| block, |
| self.source_info(match_start_span), |
| TerminatorKind::SwitchInt { |
| discr: Operand::Move(discr), |
| targets: switch_targets, |
| }, |
| ); |
| } |
| |
| TestKind::SwitchInt => { |
| // The switch may be inexhaustive so we have a catch-all block |
| let otherwise_block = target_block(TestBranch::Failure); |
| let switch_targets = SwitchTargets::new( |
| target_blocks.iter().filter_map(|(&branch, &block)| { |
| if let TestBranch::Constant(_, bits) = branch { |
| Some((bits, block)) |
| } else { |
| None |
| } |
| }), |
| otherwise_block, |
| ); |
| let terminator = TerminatorKind::SwitchInt { |
| discr: Operand::Copy(place), |
| targets: switch_targets, |
| }; |
| self.cfg.terminate(block, self.source_info(match_start_span), terminator); |
| } |
| |
| TestKind::If => { |
| let success_block = target_block(TestBranch::Success); |
| let fail_block = target_block(TestBranch::Failure); |
| let terminator = |
| TerminatorKind::if_(Operand::Copy(place), success_block, fail_block); |
| self.cfg.terminate(block, self.source_info(match_start_span), terminator); |
| } |
| |
| TestKind::Eq { value, ty } => { |
| let tcx = self.tcx; |
| let success_block = target_block(TestBranch::Success); |
| let fail_block = target_block(TestBranch::Failure); |
| if let ty::Adt(def, _) = ty.kind() |
| && Some(def.did()) == tcx.lang_items().string() |
| { |
| if !tcx.features().string_deref_patterns { |
| bug!( |
| "matching on `String` went through without enabling string_deref_patterns" |
| ); |
| } |
| let re_erased = tcx.lifetimes.re_erased; |
| let ref_str_ty = Ty::new_imm_ref(tcx, re_erased, tcx.types.str_); |
| let ref_str = self.temp(ref_str_ty, test.span); |
| let eq_block = self.cfg.start_new_block(); |
| // `let ref_str: &str = <String as Deref>::deref(&place);` |
| self.call_deref( |
| block, |
| eq_block, |
| place, |
| Mutability::Not, |
| ty, |
| ref_str, |
| test.span, |
| ); |
| self.non_scalar_compare( |
| eq_block, |
| success_block, |
| fail_block, |
| source_info, |
| value, |
| ref_str, |
| ref_str_ty, |
| ); |
| } else if !ty.is_scalar() { |
| // Use `PartialEq::eq` instead of `BinOp::Eq` |
| // (the binop can only handle primitives) |
| self.non_scalar_compare( |
| block, |
| success_block, |
| fail_block, |
| source_info, |
| value, |
| place, |
| ty, |
| ); |
| } else { |
| assert_eq!(value.ty(), ty); |
| let expect = self.literal_operand(test.span, value); |
| let val = Operand::Copy(place); |
| self.compare( |
| block, |
| success_block, |
| fail_block, |
| source_info, |
| BinOp::Eq, |
| expect, |
| val, |
| ); |
| } |
| } |
| |
| TestKind::Range(ref range) => { |
| let success = target_block(TestBranch::Success); |
| let fail = target_block(TestBranch::Failure); |
| // Test `val` by computing `lo <= val && val <= hi`, using primitive comparisons. |
| let val = Operand::Copy(place); |
| |
| let intermediate_block = if !range.lo.is_finite() { |
| block |
| } else if !range.hi.is_finite() { |
| success |
| } else { |
| self.cfg.start_new_block() |
| }; |
| |
| if let Some(lo) = range.lo.as_finite() { |
| let lo = self.literal_operand(test.span, lo); |
| self.compare( |
| block, |
| intermediate_block, |
| fail, |
| source_info, |
| BinOp::Le, |
| lo, |
| val.clone(), |
| ); |
| }; |
| |
| if let Some(hi) = range.hi.as_finite() { |
| let hi = self.literal_operand(test.span, hi); |
| let op = match range.end { |
| RangeEnd::Included => BinOp::Le, |
| RangeEnd::Excluded => BinOp::Lt, |
| }; |
| self.compare(intermediate_block, success, fail, source_info, op, val, hi); |
| } |
| } |
| |
| TestKind::Len { len, op } => { |
| let usize_ty = self.tcx.types.usize; |
| let actual = self.temp(usize_ty, test.span); |
| |
| // actual = len(place) |
| self.cfg.push_assign(block, source_info, actual, Rvalue::Len(place)); |
| |
| // expected = <N> |
| let expected = self.push_usize(block, source_info, len); |
| |
| let success_block = target_block(TestBranch::Success); |
| let fail_block = target_block(TestBranch::Failure); |
| // result = actual == expected OR result = actual < expected |
| // branch based on result |
| self.compare( |
| block, |
| success_block, |
| fail_block, |
| source_info, |
| op, |
| Operand::Move(actual), |
| Operand::Move(expected), |
| ); |
| } |
| |
| TestKind::Deref { temp, mutability } => { |
| let ty = place_ty.ty; |
| let target = target_block(TestBranch::Success); |
| self.call_deref(block, target, place, mutability, ty, temp, test.span); |
| } |
| |
| TestKind::Never => { |
| // Check that the place is initialized. |
| // FIXME(never_patterns): Also assert validity of the data at `place`. |
| self.cfg.push_fake_read( |
| block, |
| source_info, |
| FakeReadCause::ForMatchedPlace(None), |
| place, |
| ); |
| // A never pattern is only allowed on an uninhabited type, so validity of the data |
| // implies unreachability. |
| self.cfg.terminate(block, source_info, TerminatorKind::Unreachable); |
| } |
| } |
| } |
| |
| /// Perform `let temp = <ty as Deref>::deref(&place)`. |
| /// or `let temp = <ty as DerefMut>::deref_mut(&mut place)`. |
| pub(super) fn call_deref( |
| &mut self, |
| block: BasicBlock, |
| target_block: BasicBlock, |
| place: Place<'tcx>, |
| mutability: Mutability, |
| ty: Ty<'tcx>, |
| temp: Place<'tcx>, |
| span: Span, |
| ) { |
| let (trait_item, method) = match mutability { |
| Mutability::Not => (LangItem::Deref, sym::deref), |
| Mutability::Mut => (LangItem::DerefMut, sym::deref_mut), |
| }; |
| let borrow_kind = super::util::ref_pat_borrow_kind(mutability); |
| let source_info = self.source_info(span); |
| let re_erased = self.tcx.lifetimes.re_erased; |
| let trait_item = self.tcx.require_lang_item(trait_item, None); |
| let method = trait_method(self.tcx, trait_item, method, [ty]); |
| let ref_src = self.temp(Ty::new_ref(self.tcx, re_erased, ty, mutability), span); |
| // `let ref_src = &src_place;` |
| // or `let ref_src = &mut src_place;` |
| self.cfg.push_assign( |
| block, |
| source_info, |
| ref_src, |
| Rvalue::Ref(re_erased, borrow_kind, place), |
| ); |
| // `let temp = <Ty as Deref>::deref(ref_src);` |
| // or `let temp = <Ty as DerefMut>::deref_mut(ref_src);` |
| self.cfg.terminate( |
| block, |
| source_info, |
| TerminatorKind::Call { |
| func: Operand::Constant(Box::new(ConstOperand { |
| span, |
| user_ty: None, |
| const_: method, |
| })), |
| args: vec![Spanned { node: Operand::Move(ref_src), span }], |
| destination: temp, |
| target: Some(target_block), |
| unwind: UnwindAction::Continue, |
| call_source: CallSource::Misc, |
| fn_span: source_info.span, |
| }, |
| ); |
| } |
| |
| /// Compare using the provided built-in comparison operator |
| fn compare( |
| &mut self, |
| block: BasicBlock, |
| success_block: BasicBlock, |
| fail_block: BasicBlock, |
| source_info: SourceInfo, |
| op: BinOp, |
| left: Operand<'tcx>, |
| right: Operand<'tcx>, |
| ) { |
| let bool_ty = self.tcx.types.bool; |
| let result = self.temp(bool_ty, source_info.span); |
| |
| // result = op(left, right) |
| self.cfg.push_assign( |
| block, |
| source_info, |
| result, |
| Rvalue::BinaryOp(op, Box::new((left, right))), |
| ); |
| |
| // branch based on result |
| self.cfg.terminate( |
| block, |
| source_info, |
| TerminatorKind::if_(Operand::Move(result), success_block, fail_block), |
| ); |
| } |
| |
| /// Compare two values using `<T as std::compare::PartialEq>::eq`. |
| /// If the values are already references, just call it directly, otherwise |
| /// take a reference to the values first and then call it. |
| fn non_scalar_compare( |
| &mut self, |
| block: BasicBlock, |
| success_block: BasicBlock, |
| fail_block: BasicBlock, |
| source_info: SourceInfo, |
| value: Const<'tcx>, |
| mut val: Place<'tcx>, |
| mut ty: Ty<'tcx>, |
| ) { |
| let mut expect = self.literal_operand(source_info.span, value); |
| |
| // 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.kind() { |
| ty::Ref(region, rty, _) => match rty.kind() { |
| 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()); |
| 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.tcx; |
| // make both a slice |
| ty = Ty::new_imm_ref(tcx, *region, Ty::new_slice(tcx, *elem_ty)); |
| if opt_ref_ty.is_some() { |
| let temp = self.temp(ty, source_info.span); |
| self.cfg.push_assign( |
| block, |
| source_info, |
| temp, |
| Rvalue::Cast( |
| CastKind::PointerCoercion(PointerCoercion::Unsize), |
| Operand::Copy(val), |
| ty, |
| ), |
| ); |
| val = temp; |
| } |
| if opt_ref_test_ty.is_some() { |
| let slice = self.temp(ty, source_info.span); |
| self.cfg.push_assign( |
| block, |
| source_info, |
| slice, |
| Rvalue::Cast( |
| CastKind::PointerCoercion(PointerCoercion::Unsize), |
| expect, |
| ty, |
| ), |
| ); |
| expect = Operand::Move(slice); |
| } |
| } |
| } |
| |
| match *ty.kind() { |
| ty::Ref(_, deref_ty, _) => ty = deref_ty, |
| _ => { |
| // non_scalar_compare called on non-reference type |
| let temp = self.temp(ty, source_info.span); |
| self.cfg.push_assign(block, source_info, temp, Rvalue::Use(expect)); |
| let ref_ty = Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_erased, ty); |
| let ref_temp = self.temp(ref_ty, source_info.span); |
| |
| self.cfg.push_assign( |
| block, |
| source_info, |
| ref_temp, |
| Rvalue::Ref(self.tcx.lifetimes.re_erased, BorrowKind::Shared, temp), |
| ); |
| expect = Operand::Move(ref_temp); |
| |
| let ref_temp = self.temp(ref_ty, source_info.span); |
| self.cfg.push_assign( |
| block, |
| source_info, |
| ref_temp, |
| Rvalue::Ref(self.tcx.lifetimes.re_erased, BorrowKind::Shared, val), |
| ); |
| val = ref_temp; |
| } |
| } |
| |
| let eq_def_id = self.tcx.require_lang_item(LangItem::PartialEq, Some(source_info.span)); |
| let method = trait_method( |
| self.tcx, |
| eq_def_id, |
| sym::eq, |
| self.tcx.with_opt_host_effect_param(self.def_id, eq_def_id, [ty, ty]), |
| ); |
| |
| let bool_ty = self.tcx.types.bool; |
| let eq_result = self.temp(bool_ty, source_info.span); |
| let eq_block = self.cfg.start_new_block(); |
| self.cfg.terminate( |
| block, |
| source_info, |
| TerminatorKind::Call { |
| func: Operand::Constant(Box::new(ConstOperand { |
| span: source_info.span, |
| |
| // 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, |
| |
| const_: method, |
| })), |
| args: vec![ |
| Spanned { node: Operand::Copy(val), span: DUMMY_SP }, |
| Spanned { node: expect, span: DUMMY_SP }, |
| ], |
| destination: eq_result, |
| target: Some(eq_block), |
| unwind: UnwindAction::Continue, |
| call_source: CallSource::MatchCmp, |
| fn_span: source_info.span, |
| }, |
| ); |
| self.diverge_from(block); |
| |
| // check the result |
| self.cfg.terminate( |
| eq_block, |
| source_info, |
| TerminatorKind::if_(Operand::Move(eq_result), success_block, fail_block), |
| ); |
| } |
| |
| /// Given that we are performing `test` against `test_place`, this job |
| /// sorts out what the status of `candidate` will be after the test. See |
| /// `test_candidates` for the usage of this function. The candidate may |
| /// be modified to update its `match_pairs`. |
| /// |
| /// So, for example, if this candidate is `x @ Some(P0)` and the `Test` is |
| /// a variant test, then we would modify the candidate to be `(x as |
| /// Option).0 @ P0` and return the index corresponding to the variant |
| /// `Some`. |
| /// |
| /// However, in some cases, the test may just not be relevant to candidate. |
| /// For example, suppose we are testing whether `foo.x == 22`, but in one |
| /// match arm we have `Foo { x: _, ... }`... in that case, the test for |
| /// the value of `x` has no particular relevance to this candidate. In |
| /// such cases, this function just returns None without doing anything. |
| /// This is used by the overall `match_candidates` algorithm to structure |
| /// the match as a whole. See `match_candidates` for more details. |
| /// |
| /// FIXME(#29623). In some cases, we have some tricky choices to make. for |
| /// example, if we are testing that `x == 22`, but the candidate is `x @ |
| /// 13..55`, what should we do? In the event that the test is true, we know |
| /// that the candidate applies, but in the event of false, we don't know |
| /// that it *doesn't* apply. For now, we return false, indicate that the |
| /// test does not apply to this candidate, but it might be we can get |
| /// tighter match code if we do something a bit different. |
| pub(super) fn sort_candidate( |
| &mut self, |
| test_place: Place<'tcx>, |
| test: &Test<'tcx>, |
| candidate: &mut Candidate<'_, 'tcx>, |
| sorted_candidates: &FxIndexMap<TestBranch<'tcx>, Vec<&mut Candidate<'_, 'tcx>>>, |
| ) -> Option<TestBranch<'tcx>> { |
| // Find the match_pair for this place (if any). At present, |
| // afaik, there can be at most one. (In the future, if we |
| // adopted a more general `@` operator, there might be more |
| // than one, but it'd be very unusual to have two sides that |
| // both require tests; you'd expect one side to be simplified |
| // away.) |
| let (match_pair_index, match_pair) = candidate |
| .match_pairs |
| .iter() |
| .enumerate() |
| .find(|&(_, mp)| mp.place == Some(test_place))?; |
| |
| let fully_matched; |
| let ret = match (&test.kind, &match_pair.test_case) { |
| // If we are performing a variant switch, then this |
| // informs variant patterns, but nothing else. |
| ( |
| &TestKind::Switch { adt_def: tested_adt_def }, |
| &TestCase::Variant { adt_def, variant_index }, |
| ) => { |
| assert_eq!(adt_def, tested_adt_def); |
| fully_matched = true; |
| Some(TestBranch::Variant(variant_index)) |
| } |
| |
| // If we are performing a switch over integers, then this informs integer |
| // equality, but nothing else. |
| // |
| // FIXME(#29623) we could use PatKind::Range to rule |
| // things out here, in some cases. |
| (TestKind::SwitchInt, &TestCase::Constant { value }) |
| if is_switch_ty(match_pair.pattern.ty) => |
| { |
| // Beware: there might be some ranges sorted into the failure case; we must not add |
| // a success case that could be matched by one of these ranges. |
| let is_covering_range = |test_case: &TestCase<'_, 'tcx>| { |
| test_case.as_range().is_some_and(|range| { |
| matches!(range.contains(value, self.tcx, self.param_env), None | Some(true)) |
| }) |
| }; |
| let is_conflicting_candidate = |candidate: &&mut Candidate<'_, 'tcx>| { |
| candidate |
| .match_pairs |
| .iter() |
| .any(|mp| mp.place == Some(test_place) && is_covering_range(&mp.test_case)) |
| }; |
| if sorted_candidates |
| .get(&TestBranch::Failure) |
| .is_some_and(|candidates| candidates.iter().any(is_conflicting_candidate)) |
| { |
| fully_matched = false; |
| None |
| } else { |
| fully_matched = true; |
| let bits = value.eval_bits(self.tcx, self.param_env); |
| Some(TestBranch::Constant(value, bits)) |
| } |
| } |
| (TestKind::SwitchInt, TestCase::Range(range)) => { |
| fully_matched = false; |
| let not_contained = |
| sorted_candidates.keys().filter_map(|br| br.as_constant()).copied().all( |
| |val| matches!(range.contains(val, self.tcx, self.param_env), Some(false)), |
| ); |
| |
| not_contained.then(|| { |
| // No switch values are contained in the pattern range, |
| // so the pattern can be matched only if this test fails. |
| TestBranch::Failure |
| }) |
| } |
| |
| (TestKind::If, TestCase::Constant { value }) => { |
| fully_matched = true; |
| let value = value.try_eval_bool(self.tcx, self.param_env).unwrap_or_else(|| { |
| span_bug!(test.span, "expected boolean value but got {value:?}") |
| }); |
| Some(if value { TestBranch::Success } else { TestBranch::Failure }) |
| } |
| |
| ( |
| &TestKind::Len { len: test_len, op: BinOp::Eq }, |
| &TestCase::Slice { len, variable_length }, |
| ) => { |
| match (test_len.cmp(&(len as u64)), variable_length) { |
| (Ordering::Equal, false) => { |
| // on true, min_len = len = $actual_length, |
| // on false, len != $actual_length |
| fully_matched = true; |
| Some(TestBranch::Success) |
| } |
| (Ordering::Less, _) => { |
| // test_len < pat_len. If $actual_len = test_len, |
| // then $actual_len < pat_len and we don't have |
| // enough elements. |
| fully_matched = false; |
| Some(TestBranch::Failure) |
| } |
| (Ordering::Equal | Ordering::Greater, true) => { |
| // This can match both if $actual_len = test_len >= pat_len, |
| // and if $actual_len > test_len. We can't advance. |
| fully_matched = false; |
| None |
| } |
| (Ordering::Greater, false) => { |
| // test_len != pat_len, so if $actual_len = test_len, then |
| // $actual_len != pat_len. |
| fully_matched = false; |
| Some(TestBranch::Failure) |
| } |
| } |
| } |
| ( |
| &TestKind::Len { len: test_len, op: BinOp::Ge }, |
| &TestCase::Slice { len, variable_length }, |
| ) => { |
| // the test is `$actual_len >= test_len` |
| match (test_len.cmp(&(len as u64)), variable_length) { |
| (Ordering::Equal, true) => { |
| // $actual_len >= test_len = pat_len, |
| // so we can match. |
| fully_matched = true; |
| Some(TestBranch::Success) |
| } |
| (Ordering::Less, _) | (Ordering::Equal, false) => { |
| // test_len <= pat_len. If $actual_len < test_len, |
| // then it is also < pat_len, so the test passing is |
| // necessary (but insufficient). |
| fully_matched = false; |
| Some(TestBranch::Success) |
| } |
| (Ordering::Greater, false) => { |
| // test_len > pat_len. If $actual_len >= test_len > pat_len, |
| // then we know we won't have a match. |
| fully_matched = false; |
| Some(TestBranch::Failure) |
| } |
| (Ordering::Greater, true) => { |
| // test_len < pat_len, and is therefore less |
| // strict. This can still go both ways. |
| fully_matched = false; |
| None |
| } |
| } |
| } |
| |
| (TestKind::Range(test), &TestCase::Range(pat)) => { |
| if test.as_ref() == pat { |
| fully_matched = true; |
| Some(TestBranch::Success) |
| } else { |
| fully_matched = false; |
| // If the testing range does not overlap with pattern range, |
| // the pattern can be matched only if this test fails. |
| if !test.overlaps(pat, self.tcx, self.param_env)? { |
| Some(TestBranch::Failure) |
| } else { |
| None |
| } |
| } |
| } |
| (TestKind::Range(range), &TestCase::Constant { value }) => { |
| fully_matched = false; |
| if !range.contains(value, self.tcx, self.param_env)? { |
| // `value` is not contained in the testing range, |
| // so `value` can be matched only if this test fails. |
| Some(TestBranch::Failure) |
| } else { |
| None |
| } |
| } |
| |
| (TestKind::Eq { value: test_val, .. }, TestCase::Constant { value: case_val }) => { |
| if test_val == case_val { |
| fully_matched = true; |
| Some(TestBranch::Success) |
| } else { |
| fully_matched = false; |
| Some(TestBranch::Failure) |
| } |
| } |
| |
| (TestKind::Deref { temp: test_temp, .. }, TestCase::Deref { temp, .. }) |
| if test_temp == temp => |
| { |
| fully_matched = true; |
| Some(TestBranch::Success) |
| } |
| |
| (TestKind::Never, _) => { |
| fully_matched = true; |
| Some(TestBranch::Success) |
| } |
| |
| ( |
| TestKind::Switch { .. } |
| | TestKind::SwitchInt { .. } |
| | TestKind::If |
| | TestKind::Len { .. } |
| | TestKind::Range { .. } |
| | TestKind::Eq { .. } |
| | TestKind::Deref { .. }, |
| _, |
| ) => { |
| fully_matched = false; |
| None |
| } |
| }; |
| |
| if fully_matched { |
| // Replace the match pair by its sub-pairs. |
| let match_pair = candidate.match_pairs.remove(match_pair_index); |
| candidate.match_pairs.extend(match_pair.subpairs); |
| // Move or-patterns to the end. |
| candidate.match_pairs.sort_by_key(|pair| matches!(pair.test_case, TestCase::Or { .. })); |
| } |
| |
| ret |
| } |
| } |
| |
| fn is_switch_ty(ty: Ty<'_>) -> bool { |
| ty.is_integral() || ty.is_char() |
| } |
| |
| fn trait_method<'tcx>( |
| tcx: TyCtxt<'tcx>, |
| trait_def_id: DefId, |
| method_name: Symbol, |
| args: impl IntoIterator<Item: Into<GenericArg<'tcx>>>, |
| ) -> Const<'tcx> { |
| // The unhygienic comparison here is acceptable because this is only |
| // used on known traits. |
| let item = tcx |
| .associated_items(trait_def_id) |
| .filter_by_name_unhygienic(method_name) |
| .find(|item| item.kind == ty::AssocKind::Fn) |
| .expect("trait method not found"); |
| |
| let method_ty = Ty::new_fn_def(tcx, item.def_id, args); |
| |
| Const::zero_sized(method_ty) |
| } |