diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs index 952c796f52e76..68655e2775a06 100644 --- a/compiler/rustc_middle/src/arena.rs +++ b/compiler/rustc_middle/src/arena.rs @@ -106,6 +106,7 @@ macro_rules! arena_types { [] tys: rustc_type_ir::WithCachedTypeInfo>, [] predicates: rustc_type_ir::WithCachedTypeInfo>, [] consts: rustc_middle::ty::ConstData<'tcx>, + [] pats: rustc_middle::thir::Pat<'tcx>, // Note that this deliberately duplicates items in the `rustc_hir::arena`, // since we need to allocate this type on both the `rustc_hir` arena diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs index cce10417e1b0c..1201f4c2b6728 100644 --- a/compiler/rustc_middle/src/ty/consts.rs +++ b/compiler/rustc_middle/src/ty/consts.rs @@ -24,7 +24,7 @@ use super::sty::ConstKind; /// Use this rather than `ConstData`, whenever possible. #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, HashStable)] #[rustc_pass_by_value] -pub struct Const<'tcx>(pub(super) Interned<'tcx, ConstData<'tcx>>); +pub struct Const<'tcx>(pub Interned<'tcx, ConstData<'tcx>>); /// Typed constant value. #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, HashStable, TyEncodable, TyDecodable)] diff --git a/compiler/rustc_middle/src/ty/consts/valtree.rs b/compiler/rustc_middle/src/ty/consts/valtree.rs index fb7bf78bafe31..7a8ef2f08a58b 100644 --- a/compiler/rustc_middle/src/ty/consts/valtree.rs +++ b/compiler/rustc_middle/src/ty/consts/valtree.rs @@ -63,6 +63,12 @@ impl<'tcx> ValTree<'tcx> { Self::Branch(interned) } + pub fn from_scalars(tcx: TyCtxt<'tcx>, scalars: impl IntoIter) -> Self { + let interned = tcx.arena.alloc_from_iter(scalars.iter().map(|&s| Self::Leaf(s))); + + Self::Branch(interned) + } + pub fn from_scalar_int(i: ScalarInt) -> Self { Self::Leaf(i) } diff --git a/compiler/rustc_mir_build/src/build/matches/simplify.rs b/compiler/rustc_mir_build/src/build/matches/simplify.rs index 17ac1f4e0cea6..231f2c50eec44 100644 --- a/compiler/rustc_mir_build/src/build/matches/simplify.rs +++ b/compiler/rustc_mir_build/src/build/matches/simplify.rs @@ -15,10 +15,13 @@ use crate::build::expr::as_place::PlaceBuilder; use crate::build::matches::{Ascription, Binding, Candidate, MatchPair}; use crate::build::Builder; +use rustc_data_structures::intern::Interned; use rustc_hir::RangeEnd; +use rustc_middle::mir::ConstantKind; use rustc_middle::thir::{self, *}; -use rustc_middle::ty; use rustc_middle::ty::layout::IntegerExt; +use rustc_middle::ty::Const; +use rustc_middle::ty::{self, Ty, ValTree}; use rustc_target::abi::{Integer, Size}; use std::mem; @@ -120,6 +123,38 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } + fn try_build_valtree(&self, elements: &Box<[Box>]>) -> Option> { + if elements.iter().all(|p| { + matches!( + p.kind, + PatKind::Constant { + value: ConstantKind::Ty(ty::Const(Interned( + ty::ConstData { kind: ty::ConstKind::Value(ty::ValTree::Leaf(_)), .. }, + _ + ))) + } + ) + }) { + // This ValTree represents the content of the simple array. + // We then create a new pattern that matches against this ValTree. + // This reduces a match against `[1, 2, 3, 4]` from 4 basic-blocks in the MIR to just 1 + Some(ValTree::from_scalars( + self.tcx, + &elements + .iter() + .map(|p| match p.kind { + PatKind::Constant { value: ConstantKind::Ty(c) } => { + c.to_valtree().unwrap_leaf() + } + _ => unreachable!(), + }) + .collect::>(), + )) + } else { + None + } + } + /// Given `candidate` that has a single or-pattern for its match-pairs, /// creates a fresh candidate for each of its input subpatterns passed via /// `pats`. @@ -243,22 +278,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { Err(match_pair) } - PatKind::Slice { ref prefix, ref slice, ref suffix } => { - if prefix.is_empty() && slice.is_some() && suffix.is_empty() { - // irrefutable - self.prefix_slice_suffix( - &mut candidate.match_pairs, - &match_pair.place, - prefix, - slice, - suffix, - ); - Ok(()) - } else { - Err(match_pair) - } - } - PatKind::Variant { adt_def, args, variant_index, ref subpatterns } => { let irrefutable = adt_def.variants().iter_enumerated().all(|(i, v)| { i == variant_index || { @@ -281,6 +300,51 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } + // Looks for simple array patterns such as `[1, 2, 3]` + // They cannot have slices (e.g `[1, .., 3, 4]` is not simple), and + // all the elements of the array must be leaf constants (so no strings!) + PatKind::Array { ref prefix, slice: None, ref suffix } + | PatKind::Slice { ref prefix, slice: None, ref suffix } + if let Some(_val) = self.try_build_valtree(prefix) + && !prefix.is_empty() + && suffix.is_empty() => + { + // This ValTree represents the content of the simple array. + // We then create a new pattern that matches against this ValTree. + // This reduces a match against `[1, 2, 3, 4]` from 4 basic-blocks in the MIR to just 1 + let val = self.try_build_valtree(prefix).unwrap(); // FIXME: false positive + + let el_ty = prefix.iter().next().unwrap().ty; + let (place, cnst_ty, pat_ty) = + if match_pair.pattern.ty.is_slice() { + ( + PlaceBuilder::from(match_pair.place.base()), + Ty::new_imm_ref(tcx, tcx.lifetimes.re_erased, Ty::new_array(tcx, el_ty, prefix.len() as u64)), + Ty::new_imm_ref(tcx, tcx.lifetimes.re_erased, Ty::new_slice(tcx, el_ty)) + ) + } else { + let arr_ty = Ty::new_array(tcx, el_ty, prefix.len() as u64); + ( + match_pair.place, + arr_ty, + arr_ty + ) + }; + + let cnst = Const::new(tcx, ty::ConstKind::Value(val), cnst_ty); + let new_pat = Pat { + ty: pat_ty, + span: match_pair.pattern.span, + kind: PatKind::Constant { value: ConstantKind::Ty(cnst) }, + }; + + let pat = tcx.arena.alloc(new_pat); + + candidate.match_pairs.push(MatchPair::new(place, pat, self)); + + Ok(()) + } + PatKind::Array { ref prefix, ref slice, ref suffix } => { self.prefix_slice_suffix( &mut candidate.match_pairs, @@ -292,6 +356,22 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { Ok(()) } + PatKind::Slice { ref prefix, ref slice, ref suffix } => { + if prefix.is_empty() && slice.is_some() && suffix.is_empty() { + // irrefutable + self.prefix_slice_suffix( + &mut candidate.match_pairs, + &match_pair.place, + prefix, + slice, + suffix, + ); + Ok(()) + } else { + Err(match_pair) + } + } + PatKind::Leaf { ref subpatterns } => { // tuple struct, match subpats (if any) candidate.match_pairs.extend(self.field_match_pairs(match_pair.place, subpatterns)); diff --git a/compiler/rustc_mir_build/src/build/matches/test.rs b/compiler/rustc_mir_build/src/build/matches/test.rs index 484e8490919d4..e41318816f447 100644 --- a/compiler/rustc_mir_build/src/build/matches/test.rs +++ b/compiler/rustc_mir_build/src/build/matches/test.rs @@ -94,10 +94,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { }; match match_pair.pattern.kind { - PatKind::Constant { value } => { + PatKind::Constant { + value + } if let Some(result) = value.try_eval_bits(self.tcx, self.param_env, switch_ty) => { options .entry(value) - .or_insert_with(|| value.eval_bits(self.tcx, self.param_env, switch_ty)); + .or_insert(result); true } PatKind::Variant { .. } => { @@ -108,6 +110,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.values_not_contained_in_range(&*range, options).unwrap_or(false) } PatKind::Slice { .. } + | PatKind::Constant { .. } | PatKind::Array { .. } | PatKind::Wild | PatKind::Or { .. }