summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_mir_build/src/build/matches
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-07 05:48:48 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-07 05:48:48 +0000
commitef24de24a82fe681581cc130f342363c47c0969a (patch)
tree0d494f7e1a38b95c92426f58fe6eaa877303a86c /compiler/rustc_mir_build/src/build/matches
parentReleasing progress-linux version 1.74.1+dfsg1-1~progress7.99u1. (diff)
downloadrustc-ef24de24a82fe681581cc130f342363c47c0969a.tar.xz
rustc-ef24de24a82fe681581cc130f342363c47c0969a.zip
Merging upstream version 1.75.0+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'compiler/rustc_mir_build/src/build/matches')
-rw-r--r--compiler/rustc_mir_build/src/build/matches/mod.rs86
-rw-r--r--compiler/rustc_mir_build/src/build/matches/simplify.rs52
-rw-r--r--compiler/rustc_mir_build/src/build/matches/test.rs87
3 files changed, 101 insertions, 124 deletions
diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs
index 6baf8c7d7..304870274 100644
--- a/compiler/rustc_mir_build/src/build/matches/mod.rs
+++ b/compiler/rustc_mir_build/src/build/matches/mod.rs
@@ -157,7 +157,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
/// [ 0. Pre-match ]
/// |
/// [ 1. Evaluate Scrutinee (expression being matched on) ]
- /// [ (fake read of scrutinee) ]
+ /// [ (PlaceMention of scrutinee) ]
/// |
/// [ 2. Decision tree -- check discriminants ] <--------+
/// | |
@@ -184,7 +184,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
///
/// We generate MIR in the following steps:
///
- /// 1. Evaluate the scrutinee and add the fake read of it ([Builder::lower_scrutinee]).
+ /// 1. Evaluate the scrutinee and add the PlaceMention of it ([Builder::lower_scrutinee]).
/// 2. Create the decision tree ([Builder::lower_match_tree]).
/// 3. Determine the fake borrows that are needed from the places that were
/// matched against and create the required temporaries for them
@@ -223,6 +223,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let fake_borrow_temps = self.lower_match_tree(
block,
scrutinee_span,
+ &scrutinee_place,
match_start_span,
match_has_guard,
&mut candidates,
@@ -238,7 +239,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
)
}
- /// Evaluate the scrutinee and add the fake read of it.
+ /// Evaluate the scrutinee and add the PlaceMention for it.
fn lower_scrutinee(
&mut self,
mut block: BasicBlock,
@@ -246,26 +247,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
scrutinee_span: Span,
) -> BlockAnd<PlaceBuilder<'tcx>> {
let scrutinee_place_builder = unpack!(block = self.as_place_builder(block, scrutinee));
- // Matching on a `scrutinee_place` with an uninhabited type doesn't
- // generate any memory reads by itself, and so if the place "expression"
- // contains unsafe operations like raw pointer dereferences or union
- // field projections, we wouldn't know to require an `unsafe` block
- // around a `match` equivalent to `std::intrinsics::unreachable()`.
- // See issue #47412 for this hole being discovered in the wild.
- //
- // HACK(eddyb) Work around the above issue by adding a dummy inspection
- // of `scrutinee_place`, specifically by applying `ReadForMatch`.
- //
- // NOTE: ReadForMatch also checks that the scrutinee is initialized.
- // This is currently needed to not allow matching on an uninitialized,
- // uninhabited value. If we get never patterns, those will check that
- // the place is initialized, and so this read would only be used to
- // check safety.
- let cause_matched_place = FakeReadCause::ForMatchedPlace(None);
- let source_info = self.source_info(scrutinee_span);
-
if let Some(scrutinee_place) = scrutinee_place_builder.try_to_place(self) {
- self.cfg.push_fake_read(block, source_info, cause_matched_place, scrutinee_place);
+ let source_info = self.source_info(scrutinee_span);
+ self.cfg.push_place_mention(block, source_info, scrutinee_place);
}
block.and(scrutinee_place_builder)
@@ -304,6 +288,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
&mut self,
block: BasicBlock,
scrutinee_span: Span,
+ scrutinee_place_builder: &PlaceBuilder<'tcx>,
match_start_span: Span,
match_has_guard: bool,
candidates: &mut [&mut Candidate<'pat, 'tcx>],
@@ -331,6 +316,33 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// otherwise block. Match checking will ensure this is actually
// unreachable.
let source_info = self.source_info(scrutinee_span);
+
+ // Matching on a `scrutinee_place` with an uninhabited type doesn't
+ // generate any memory reads by itself, and so if the place "expression"
+ // contains unsafe operations like raw pointer dereferences or union
+ // field projections, we wouldn't know to require an `unsafe` block
+ // around a `match` equivalent to `std::intrinsics::unreachable()`.
+ // See issue #47412 for this hole being discovered in the wild.
+ //
+ // HACK(eddyb) Work around the above issue by adding a dummy inspection
+ // of `scrutinee_place`, specifically by applying `ReadForMatch`.
+ //
+ // NOTE: ReadForMatch also checks that the scrutinee is initialized.
+ // This is currently needed to not allow matching on an uninitialized,
+ // uninhabited value. If we get never patterns, those will check that
+ // the place is initialized, and so this read would only be used to
+ // check safety.
+ let cause_matched_place = FakeReadCause::ForMatchedPlace(None);
+
+ if let Some(scrutinee_place) = scrutinee_place_builder.try_to_place(self) {
+ self.cfg.push_fake_read(
+ otherwise_block,
+ source_info,
+ cause_matched_place,
+ scrutinee_place,
+ );
+ }
+
self.cfg.terminate(otherwise_block, source_info, TerminatorKind::Unreachable);
}
@@ -599,13 +611,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
}
_ => {
- let place_builder = unpack!(block = self.as_place_builder(block, initializer));
-
- if let Some(place) = place_builder.try_to_place(self) {
- let source_info = self.source_info(initializer.span);
- self.cfg.push_place_mention(block, source_info, place);
- }
-
+ let place_builder =
+ unpack!(block = self.lower_scrutinee(block, initializer, initializer.span));
self.place_into_pattern(block, &irrefutable_pat, place_builder, true)
}
}
@@ -622,6 +629,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let fake_borrow_temps = self.lower_match_tree(
block,
irrefutable_pat.span,
+ &initializer,
irrefutable_pat.span,
false,
&mut [&mut candidate],
@@ -736,7 +744,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
self.cfg.push(block, Statement { source_info, kind: StatementKind::StorageLive(local_id) });
// Although there is almost always scope for given variable in corner cases
// like #92893 we might get variable with no scope.
- if let Some(region_scope) = self.region_scope_tree.var_scope(var.0.local_id) && schedule_drop {
+ if let Some(region_scope) = self.region_scope_tree.var_scope(var.0.local_id)
+ && schedule_drop
+ {
self.schedule_drop(span, region_scope, local_id, DropKind::Storage);
}
Place::from(local_id)
@@ -814,7 +824,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
}
}
- PatKind::Constant { .. } | PatKind::Range { .. } | PatKind::Wild => {}
+ PatKind::Constant { .. }
+ | PatKind::Range { .. }
+ | PatKind::Wild
+ | PatKind::Error(_) => {}
PatKind::Deref { ref subpattern } => {
self.visit_primary_bindings(subpattern, pattern_user_ty.deref(), f);
@@ -842,6 +855,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
self.visit_primary_bindings(subpattern, subpattern_user_ty, f)
}
+ PatKind::InlineConstant { ref subpattern, .. } => {
+ self.visit_primary_bindings(subpattern, pattern_user_ty, f)
+ }
+
PatKind::Leaf { ref subpatterns } => {
for subpattern in subpatterns {
let subpattern_user_ty = pattern_user_ty.clone().leaf(subpattern.field);
@@ -1018,7 +1035,7 @@ enum TestKind<'tcx> {
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(Box<PatRange<'tcx>>),
/// Test that the length of the slice is equal to `len`.
@@ -1798,7 +1815,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let fake_borrow_ty =
Ty::new_imm_ref(tcx, tcx.lifetimes.re_erased, fake_borrow_deref_ty);
let mut fake_borrow_temp = LocalDecl::new(fake_borrow_ty, temp_span);
- fake_borrow_temp.internal = self.local_decls[matched_place.local].internal;
fake_borrow_temp.local_info = ClearCrossCrate::Set(Box::new(LocalInfo::FakeBorrow));
let fake_borrow_temp = self.local_decls.push(fake_borrow_temp);
@@ -1833,6 +1849,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let fake_borrow_temps = self.lower_match_tree(
block,
pat.span,
+ &expr_place_builder,
pat.span,
false,
&mut [&mut guard_candidate, &mut otherwise_candidate],
@@ -2268,7 +2285,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
ty: var_ty,
user_ty: if user_ty.is_empty() { None } else { Some(Box::new(user_ty)) },
source_info,
- internal: false,
local_info: ClearCrossCrate::Set(Box::new(LocalInfo::User(BindingForm::Var(
VarBindingForm {
binding_mode,
@@ -2298,7 +2314,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
ty: Ty::new_imm_ref(tcx, tcx.lifetimes.re_erased, var_ty),
user_ty: None,
source_info,
- internal: false,
local_info: ClearCrossCrate::Set(Box::new(LocalInfo::User(
BindingForm::RefForGuard,
))),
@@ -2336,6 +2351,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let fake_borrow_temps = this.lower_match_tree(
block,
initializer_span,
+ &scrutinee,
pattern.span,
false,
&mut [&mut candidate, &mut wildcard],
diff --git a/compiler/rustc_mir_build/src/build/matches/simplify.rs b/compiler/rustc_mir_build/src/build/matches/simplify.rs
index 17ac1f4e0..6a40c8d84 100644
--- a/compiler/rustc_mir_build/src/build/matches/simplify.rs
+++ b/compiler/rustc_mir_build/src/build/matches/simplify.rs
@@ -15,11 +15,7 @@
use crate::build::expr::as_place::PlaceBuilder;
use crate::build::matches::{Ascription, Binding, Candidate, MatchPair};
use crate::build::Builder;
-use rustc_hir::RangeEnd;
use rustc_middle::thir::{self, *};
-use rustc_middle::ty;
-use rustc_middle::ty::layout::IntegerExt;
-use rustc_target::abi::{Integer, Size};
use std::mem;
@@ -148,7 +144,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
match_pair: MatchPair<'pat, 'tcx>,
candidate: &mut Candidate<'pat, 'tcx>,
) -> Result<(), MatchPair<'pat, 'tcx>> {
- let tcx = self.tcx;
match match_pair.pattern.kind {
PatKind::AscribeUserType {
ref subpattern,
@@ -168,7 +163,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
Ok(())
}
- PatKind::Wild => {
+ PatKind::Wild | PatKind::Error(_) => {
// nothing left to do
Ok(())
}
@@ -204,41 +199,16 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
Err(match_pair)
}
- PatKind::Range(box PatRange { lo, hi, end }) => {
- let (range, bias) = match *lo.ty().kind() {
- ty::Char => {
- (Some(('\u{0000}' as u128, '\u{10FFFF}' as u128, Size::from_bits(32))), 0)
- }
- ty::Int(ity) => {
- let size = Integer::from_int_ty(&tcx, ity).size();
- let max = size.truncate(u128::MAX);
- let bias = 1u128 << (size.bits() - 1);
- (Some((0, max, size)), bias)
- }
- ty::Uint(uty) => {
- let size = Integer::from_uint_ty(&tcx, uty).size();
- let max = size.truncate(u128::MAX);
- (Some((0, max, size)), 0)
- }
- _ => (None, 0),
- };
- if let Some((min, max, sz)) = range {
- // We want to compare ranges numerically, but the order of the bitwise
- // representation of signed integers does not match their numeric order. Thus,
- // to correct the ordering, we need to shift the range of signed integers to
- // correct the comparison. This is achieved by XORing with a bias (see
- // pattern/_match.rs for another pertinent example of this pattern).
- //
- // Also, for performance, it's important to only do the second `try_to_bits` if
- // necessary.
- let lo = lo.try_to_bits(sz).unwrap() ^ bias;
- if lo <= min {
- let hi = hi.try_to_bits(sz).unwrap() ^ bias;
- if hi > max || hi == max && end == RangeEnd::Included {
- // Irrefutable pattern match.
- return Ok(());
- }
- }
+ PatKind::InlineConstant { subpattern: ref pattern, def: _ } => {
+ candidate.match_pairs.push(MatchPair::new(match_pair.place, pattern, self));
+
+ Ok(())
+ }
+
+ PatKind::Range(ref range) => {
+ if let Some(true) = range.is_full_range(self.tcx) {
+ // Irrefutable pattern match.
+ return Ok(());
}
Err(match_pair)
}
diff --git a/compiler/rustc_mir_build/src/build/matches/test.rs b/compiler/rustc_mir_build/src/build/matches/test.rs
index 795d1db8e..bdd4f2011 100644
--- a/compiler/rustc_mir_build/src/build/matches/test.rs
+++ b/compiler/rustc_mir_build/src/build/matches/test.rs
@@ -8,7 +8,6 @@
use crate::build::expr::as_place::PlaceBuilder;
use crate::build::matches::{Candidate, MatchPair, Test, TestKind};
use crate::build::Builder;
-use crate::thir::pattern::compare_const_vals;
use rustc_data_structures::fx::FxIndexMap;
use rustc_hir::{LangItem, RangeEnd};
use rustc_index::bit_set::BitSet;
@@ -59,8 +58,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
},
PatKind::Range(ref range) => {
- assert_eq!(range.lo.ty(), match_pair.pattern.ty);
- assert_eq!(range.hi.ty(), match_pair.pattern.ty);
+ assert_eq!(range.ty, match_pair.pattern.ty);
Test { span: match_pair.pattern.span, kind: TestKind::Range(range.clone()) }
}
@@ -73,11 +71,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
PatKind::Or { .. } => bug!("or-patterns should have already been handled"),
PatKind::AscribeUserType { .. }
+ | PatKind::InlineConstant { .. }
| PatKind::Array { .. }
| PatKind::Wild
| PatKind::Binding { .. }
| PatKind::Leaf { .. }
- | PatKind::Deref { .. } => self.error_simplifiable(match_pair),
+ | PatKind::Deref { .. }
+ | PatKind::Error(_) => self.error_simplifiable(match_pair),
}
}
@@ -110,8 +110,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
| PatKind::Or { .. }
| PatKind::Binding { .. }
| PatKind::AscribeUserType { .. }
+ | PatKind::InlineConstant { .. }
| PatKind::Leaf { .. }
- | PatKind::Deref { .. } => {
+ | PatKind::Deref { .. }
+ | PatKind::Error(_) => {
// don't know how to add these patterns to a switch
false
}
@@ -236,18 +238,27 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
TestKind::Eq { value, ty } => {
let tcx = self.tcx;
- if let ty::Adt(def, _) = ty.kind() && Some(def.did()) == tcx.lang_items().string() {
+ 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");
+ bug!(
+ "matching on `String` went through without enabling string_deref_patterns"
+ );
}
let re_erased = tcx.lifetimes.re_erased;
- let ref_string = self.temp(Ty::new_imm_ref(tcx,re_erased, ty), test.span);
- let ref_str_ty = Ty::new_imm_ref(tcx,re_erased, tcx.types.str_);
+ let ref_string = self.temp(Ty::new_imm_ref(tcx, re_erased, ty), test.span);
+ 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 deref = tcx.require_lang_item(LangItem::Deref, None);
let method = trait_method(tcx, deref, sym::deref, [ty]);
let eq_block = self.cfg.start_new_block();
- self.cfg.push_assign(block, source_info, ref_string, Rvalue::Ref(re_erased, BorrowKind::Shared, place));
+ self.cfg.push_assign(
+ block,
+ source_info,
+ ref_string,
+ Rvalue::Ref(re_erased, BorrowKind::Shared, place),
+ );
self.cfg.terminate(
block,
source_info,
@@ -262,10 +273,17 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
target: Some(eq_block),
unwind: UnwindAction::Continue,
call_source: CallSource::Misc,
- fn_span: source_info.span
- }
+ fn_span: source_info.span,
+ },
+ );
+ self.non_scalar_compare(
+ eq_block,
+ make_target_blocks,
+ source_info,
+ value,
+ ref_str,
+ ref_str_ty,
);
- self.non_scalar_compare(eq_block, make_target_blocks, source_info, value, ref_str, ref_str_ty);
return;
}
if !ty.is_scalar() {
@@ -289,11 +307,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
}
}
- TestKind::Range(box PatRange { lo, hi, ref end }) => {
+ TestKind::Range(ref range) => {
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.
+ // FIXME: skip useless comparison when the range is half-open.
+ let lo = range.lo.to_const(range.ty, self.tcx);
+ let hi = range.hi.to_const(range.ty, self.tcx);
let lo = self.literal_operand(test.span, lo);
let hi = self.literal_operand(test.span, hi);
let val = Operand::Copy(place);
@@ -310,7 +331,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
lo,
val.clone(),
);
- let op = match *end {
+ let op = match range.end {
RangeEnd::Included => BinOp::Le,
RangeEnd::Excluded => BinOp::Lt,
};
@@ -678,34 +699,18 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
}
(TestKind::Range(test), PatKind::Range(pat)) => {
- use std::cmp::Ordering::*;
-
if test == pat {
self.candidate_without_match_pair(match_pair_index, candidate);
return Some(0);
}
- // For performance, it's important to only do the second
- // `compare_const_vals` if necessary.
- let no_overlap = if matches!(
- (compare_const_vals(self.tcx, test.hi, pat.lo, self.param_env)?, test.end),
- (Less, _) | (Equal, RangeEnd::Excluded) // test < pat
- ) || matches!(
- (compare_const_vals(self.tcx, test.lo, pat.hi, self.param_env)?, pat.end),
- (Greater, _) | (Equal, RangeEnd::Excluded) // test > pat
- ) {
- Some(1)
- } else {
- None
- };
-
// If the testing range does not overlap with pattern range,
// the pattern can be matched only if this test fails.
- no_overlap
+ if !test.overlaps(pat, self.tcx, self.param_env)? { Some(1) } else { None }
}
(TestKind::Range(range), &PatKind::Constant { value }) => {
- if let Some(false) = self.const_range_contains(&*range, value) {
+ 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(1)
@@ -797,27 +802,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
span_bug!(match_pair.pattern.span, "simplifiable pattern found: {:?}", match_pair.pattern)
}
- fn const_range_contains(&self, range: &PatRange<'tcx>, value: Const<'tcx>) -> Option<bool> {
- use std::cmp::Ordering::*;
-
- // For performance, it's important to only do the second
- // `compare_const_vals` if necessary.
- Some(
- matches!(compare_const_vals(self.tcx, range.lo, value, self.param_env)?, Less | Equal)
- && matches!(
- (compare_const_vals(self.tcx, value, range.hi, self.param_env)?, range.end),
- (Less, _) | (Equal, RangeEnd::Included)
- ),
- )
- }
-
fn values_not_contained_in_range(
&self,
range: &PatRange<'tcx>,
options: &FxIndexMap<Const<'tcx>, u128>,
) -> Option<bool> {
for &val in options.keys() {
- if self.const_range_contains(range, val)? {
+ if range.contains(val, self.tcx, self.param_env)? {
return Some(false);
}
}