Skip to content

Commit 18f18c5

Browse files
committed
Rework and optimize match MIR generation
1 parent e6f12c8 commit 18f18c5

File tree

12 files changed

+502
-96
lines changed

12 files changed

+502
-96
lines changed

Diff for: compiler/rustc_const_eval/src/check_consts/check.rs

+4
Original file line numberDiff line numberDiff line change
@@ -809,6 +809,10 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
809809
// Otherwise, it's really misleading to call something "conditionally"
810810
// const when it's very obviously not conditionally const.
811811
if trait_is_const && has_const_conditions == Some(ConstConditionsHold::Yes) {
812+
if tcx.is_lang_item(trait_did, LangItem::PatternConstEq) {
813+
return;
814+
}
815+
812816
// Trait calls are always conditionally-const.
813817
self.check_op(ops::ConditionallyConstCall {
814818
callee,

Diff for: compiler/rustc_hir/src/lang_items.rs

+5
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,11 @@ language_item_table! {
417417

418418
String, sym::String, string, Target::Struct, GenericRequirement::None;
419419
CStr, sym::CStr, c_str, Target::Struct, GenericRequirement::None;
420+
421+
// FIXME(xacrimon): Used for lowering of match/if let statements. will made obsolete by const PartialEq.
422+
PatternConstEq, sym::PatternConstEq, pattern_const_eq, Target::Trait, GenericRequirement::None;
423+
AggregateRawPtr, sym::aggregate_raw_ptr, aggregate_raw_ptr, Target::Fn, GenericRequirement::None;
424+
Offset, sym::offset, offset, Target::Fn, GenericRequirement::None;
420425
}
421426

422427
pub enum GenericRequirement {

Diff for: compiler/rustc_middle/src/arena.rs

+1
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ macro_rules! arena_types {
114114
[decode] specialization_graph: rustc_middle::traits::specialization_graph::Graph,
115115
[] crate_inherent_impls: rustc_middle::ty::CrateInherentImpls,
116116
[] hir_owner_nodes: rustc_hir::OwnerNodes<'tcx>,
117+
[] thir_pat: rustc_middle::thir::Pat<'tcx>,
117118
]);
118119
)
119120
}

Diff for: compiler/rustc_mir_build/src/builder/matches/match_pair.rs

+135-18
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use rustc_middle::ty::{self, Ty, TypeVisitableExt};
44

55
use crate::builder::Builder;
66
use crate::builder::expr::as_place::{PlaceBase, PlaceBuilder};
7+
use crate::builder::matches::util::Range;
78
use crate::builder::matches::{FlatPat, MatchPairTree, TestCase};
89

910
impl<'a, 'tcx> Builder<'a, 'tcx> {
@@ -33,6 +34,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
3334
/// Used internally by [`MatchPairTree::for_pattern`].
3435
fn prefix_slice_suffix<'pat>(
3536
&mut self,
37+
src_path: &'pat Pat<'tcx>,
3638
match_pairs: &mut Vec<MatchPairTree<'pat, 'tcx>>,
3739
place: &PlaceBuilder<'tcx>,
3840
prefix: &'pat [Box<Pat<'tcx>>],
@@ -54,11 +56,29 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
5456
((prefix.len() + suffix.len()).try_into().unwrap(), false)
5557
};
5658

57-
match_pairs.extend(prefix.iter().enumerate().map(|(idx, subpattern)| {
58-
let elem =
59-
ProjectionElem::ConstantIndex { offset: idx as u64, min_length, from_end: false };
60-
MatchPairTree::for_pattern(place.clone_project(elem), subpattern, self)
61-
}));
59+
if self.should_optimize_subslice(prefix) {
60+
let elem_ty = prefix[0].ty;
61+
let prefix_valtree = self.simplify_const_pattern_slice_into_valtree(prefix);
62+
let match_pair = self.valtree_to_match_pair(
63+
src_path,
64+
prefix_valtree,
65+
place.clone(),
66+
elem_ty,
67+
Range::from_start(0..prefix.len() as u64),
68+
opt_slice.is_some() || !suffix.is_empty(),
69+
);
70+
71+
match_pairs.push(match_pair);
72+
} else {
73+
match_pairs.extend(prefix.iter().enumerate().map(|(idx, subpattern)| {
74+
let elem = ProjectionElem::ConstantIndex {
75+
offset: idx as u64,
76+
min_length,
77+
from_end: false,
78+
};
79+
MatchPairTree::for_pattern(place.clone_project(elem), subpattern, self)
80+
}));
81+
}
6282

6383
if let Some(subslice_pat) = opt_slice {
6484
let suffix_len = suffix.len() as u64;
@@ -70,16 +90,99 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
7090
match_pairs.push(MatchPairTree::for_pattern(subslice, subslice_pat, self));
7191
}
7292

73-
match_pairs.extend(suffix.iter().rev().enumerate().map(|(idx, subpattern)| {
74-
let end_offset = (idx + 1) as u64;
75-
let elem = ProjectionElem::ConstantIndex {
76-
offset: if exact_size { min_length - end_offset } else { end_offset },
77-
min_length,
78-
from_end: !exact_size,
79-
};
80-
let place = place.clone_project(elem);
81-
MatchPairTree::for_pattern(place, subpattern, self)
82-
}));
93+
if self.should_optimize_subslice(suffix) {
94+
let elem_ty = suffix[0].ty;
95+
let suffix_valtree = self.simplify_const_pattern_slice_into_valtree(suffix);
96+
let match_pair = self.valtree_to_match_pair(
97+
src_path,
98+
suffix_valtree,
99+
place.clone(),
100+
elem_ty,
101+
Range::from_end(0..suffix.len() as u64),
102+
true,
103+
);
104+
105+
match_pairs.push(match_pair);
106+
} else {
107+
match_pairs.extend(suffix.iter().rev().enumerate().map(|(idx, subpattern)| {
108+
let end_offset = (idx + 1) as u64;
109+
let elem = ProjectionElem::ConstantIndex {
110+
offset: if exact_size { min_length - end_offset } else { end_offset },
111+
min_length,
112+
from_end: !exact_size,
113+
};
114+
let place = place.clone_project(elem);
115+
MatchPairTree::for_pattern(place, subpattern, self)
116+
}));
117+
}
118+
}
119+
120+
fn should_optimize_subslice(&self, subslice: &[Box<Pat<'tcx>>]) -> bool {
121+
subslice.len() > 1 && subslice.iter().all(|p| self.is_constant_pattern(p))
122+
}
123+
124+
fn is_constant_pattern(&self, pat: &Pat<'tcx>) -> bool {
125+
if let PatKind::Constant { value } = pat.kind
126+
&& let Const::Ty(_, const_) = value
127+
&& let ty::ConstKind::Value(_, valtree) = const_.kind()
128+
&& let ty::ValTree::Leaf(_) = valtree
129+
{
130+
true
131+
} else {
132+
false
133+
}
134+
}
135+
136+
fn extract_leaf(&self, pat: &Pat<'tcx>) -> ty::ValTree<'tcx> {
137+
if let PatKind::Constant { value } = pat.kind
138+
&& let Const::Ty(_, const_) = value
139+
&& let ty::ConstKind::Value(_, valtree) = const_.kind()
140+
&& matches!(valtree, ty::ValTree::Leaf(_))
141+
{
142+
valtree
143+
} else {
144+
unreachable!()
145+
}
146+
}
147+
148+
fn simplify_const_pattern_slice_into_valtree(
149+
&self,
150+
subslice: &[Box<Pat<'tcx>>],
151+
) -> ty::ValTree<'tcx> {
152+
let leaves = subslice.iter().map(|p| self.extract_leaf(p));
153+
let interned = self.tcx.arena.alloc_from_iter(leaves);
154+
ty::ValTree::Branch(interned)
155+
}
156+
157+
fn valtree_to_match_pair<'pat>(
158+
&mut self,
159+
source_pattern: &'pat Pat<'tcx>,
160+
valtree: ty::ValTree<'tcx>,
161+
place: PlaceBuilder<'tcx>,
162+
elem_ty: Ty<'tcx>,
163+
range: Range,
164+
subsliced: bool,
165+
) -> MatchPairTree<'pat, 'tcx> {
166+
let tcx = self.tcx;
167+
let const_ty =
168+
Ty::new_imm_ref(tcx, tcx.lifetimes.re_erased, Ty::new_array(tcx, elem_ty, range.len()));
169+
170+
let pat_ty = if subsliced { Ty::new_slice(tcx, elem_ty) } else { source_pattern.ty };
171+
let ty_const = ty::Const::new(tcx, ty::ConstKind::Value(const_ty, valtree));
172+
let value = Const::Ty(const_ty, ty_const);
173+
let test_case = TestCase::Constant { value, range: subsliced.then_some(range) };
174+
let pattern = tcx.arena.alloc(Pat {
175+
ty: pat_ty,
176+
span: source_pattern.span,
177+
kind: PatKind::Constant { value },
178+
});
179+
180+
MatchPairTree {
181+
place: Some(place.to_place(self)),
182+
test_case,
183+
subpairs: Vec::new(),
184+
pattern,
185+
}
83186
}
84187
}
85188

@@ -129,7 +232,7 @@ impl<'pat, 'tcx> MatchPairTree<'pat, 'tcx> {
129232
}
130233
}
131234

132-
PatKind::Constant { value } => TestCase::Constant { value },
235+
PatKind::Constant { value } => TestCase::Constant { value, range: None },
133236

134237
PatKind::AscribeUserType {
135238
ascription: thir::Ascription { ref annotation, variance },
@@ -192,11 +295,25 @@ impl<'pat, 'tcx> MatchPairTree<'pat, 'tcx> {
192295
}
193296

194297
PatKind::Array { ref prefix, ref slice, ref suffix } => {
195-
cx.prefix_slice_suffix(&mut subpairs, &place_builder, prefix, slice, suffix);
298+
cx.prefix_slice_suffix(
299+
pattern,
300+
&mut subpairs,
301+
&place_builder,
302+
prefix,
303+
slice,
304+
suffix,
305+
);
196306
default_irrefutable()
197307
}
198308
PatKind::Slice { ref prefix, ref slice, ref suffix } => {
199-
cx.prefix_slice_suffix(&mut subpairs, &place_builder, prefix, slice, suffix);
309+
cx.prefix_slice_suffix(
310+
pattern,
311+
&mut subpairs,
312+
&place_builder,
313+
prefix,
314+
slice,
315+
suffix,
316+
);
200317

201318
if prefix.is_empty() && slice.is_some() && suffix.is_empty() {
202319
default_irrefutable()

Diff for: compiler/rustc_mir_build/src/builder/matches/mod.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use tracing::{debug, instrument};
1919

2020
use crate::builder::ForGuard::{self, OutsideGuard, RefWithinGuard};
2121
use crate::builder::expr::as_place::PlaceBuilder;
22+
use crate::builder::matches::util::Range;
2223
use crate::builder::scope::DropKind;
2324
use crate::builder::{
2425
BlockAnd, BlockAndExtension, Builder, GuardFrame, GuardFrameLocal, LocalsForNode,
@@ -1237,7 +1238,7 @@ struct Ascription<'tcx> {
12371238
enum TestCase<'pat, 'tcx> {
12381239
Irrefutable { binding: Option<Binding<'tcx>>, ascription: Option<Ascription<'tcx>> },
12391240
Variant { adt_def: ty::AdtDef<'tcx>, variant_index: VariantIdx },
1240-
Constant { value: mir::Const<'tcx> },
1241+
Constant { value: mir::Const<'tcx>, range: Option<Range> },
12411242
Range(&'pat PatRange<'tcx>),
12421243
Slice { len: usize, variable_length: bool },
12431244
Deref { temp: Place<'tcx>, mutability: Mutability },
@@ -1313,6 +1314,7 @@ enum TestKind<'tcx> {
13131314
/// `ty`,
13141315
Eq {
13151316
value: Const<'tcx>,
1317+
range: Option<Range>,
13161318
// Integer types are handled by `SwitchInt`, and constants with ADT
13171319
// types are converted back into patterns, so this can only be `&str`,
13181320
// `&[T]`, `f32` or `f64`.

0 commit comments

Comments
 (0)