@@ -4,15 +4,56 @@ use rustc_abi::FieldIdx;
44use rustc_middle:: mir:: * ;
55use rustc_middle:: span_bug;
66use rustc_middle:: thir:: * ;
7- use rustc_middle:: ty:: { self , Ty , TypeVisitableExt } ;
7+ use rustc_middle:: ty:: { self , Ty , TyCtxt , TypeVisitableExt } ;
8+ use rustc_span:: sym;
89
910use crate :: builder:: Builder ;
1011use crate :: builder:: expr:: as_place:: { PlaceBase , PlaceBuilder } ;
1112use crate :: builder:: matches:: {
1213 FlatPat , MatchPairTree , PatConstKind , PatternExtraData , SliceLenOp , TestableCase ,
1314} ;
1415
16+ /// Checks whether every pattern in `elements` is a `PatKind::Constant` and,
17+ /// if so, reconstructs a single aggregate `ty::Value` that represents the whole
18+ /// array or slice. Returns `None` when any element is not a constant or the
19+ /// sequence is too short to benefit from an aggregate comparison.
20+ fn try_reconstruct_aggregate_constant < ' tcx > (
21+ tcx : TyCtxt < ' tcx > ,
22+ aggregate_ty : Ty < ' tcx > ,
23+ elements : & [ Pat < ' tcx > ] ,
24+ ) -> Option < ty:: Value < ' tcx > > {
25+ // A single element (or empty array) is not worth an aggregate comparison.
26+ if elements. len ( ) <= 1 {
27+ return None ;
28+ }
29+ let branches = elements
30+ . iter ( )
31+ . map ( |pat| {
32+ if let PatKind :: Constant { value } = pat. kind {
33+ Some ( ty:: Const :: new_value ( tcx, value. valtree , value. ty ) )
34+ } else {
35+ None
36+ }
37+ } )
38+ . collect :: < Option < Vec < _ > > > ( ) ?;
39+ let valtree = ty:: ValTree :: from_branches ( tcx, branches) ;
40+ Some ( ty:: Value { ty : aggregate_ty, valtree } )
41+ }
42+
1543impl < ' a , ' tcx > Builder < ' a , ' tcx > {
44+ /// Check if we can use aggregate `PartialEq::eq` comparisons for constant array/slice patterns.
45+ /// This is not possible in const contexts unless `#![feature(const_cmp, const_trait_impl)]` are enabled,
46+ /// because`PartialEq` is not const-stable.
47+ fn can_use_aggregate_eq ( & self ) -> bool {
48+ let const_partial_eq_enabled = {
49+ let features = self . tcx . features ( ) ;
50+ features. enabled ( sym:: const_trait_impl) && features. enabled ( sym:: const_cmp)
51+ } ;
52+ let in_const_context = self . tcx . is_const_fn ( self . def_id . to_def_id ( ) )
53+ || !self . tcx . hir_body_owner_kind ( self . def_id ) . is_fn_or_closure ( ) ;
54+ !in_const_context || const_partial_eq_enabled
55+ }
56+
1657 /// Builds and pushes [`MatchPairTree`] subtrees, one for each pattern in
1758 /// `subpatterns`, representing the fields of a [`PatKind::Variant`] or
1859 /// [`PatKind::Leaf`].
@@ -239,15 +280,31 @@ impl<'tcx> MatchPairTree<'tcx> {
239280 _ => None ,
240281 } ;
241282 if let Some ( array_len) = array_len {
242- cx. prefix_slice_suffix (
243- & mut subpairs,
244- extra_data,
245- & place_builder,
246- Some ( array_len) ,
247- prefix,
248- slice,
249- suffix,
250- ) ;
283+ // When all elements are constants and there is no `..`
284+ // subpattern, compare the whole array at once via
285+ // `PartialEq::eq` rather than element by element.
286+ if slice. is_none ( )
287+ && suffix. is_empty ( )
288+ && cx. can_use_aggregate_eq ( )
289+ && let Some ( aggregate_value) =
290+ try_reconstruct_aggregate_constant ( cx. tcx , pattern. ty , prefix)
291+ {
292+ Some ( TestableCase :: Constant {
293+ value : aggregate_value,
294+ kind : PatConstKind :: Aggregate ,
295+ } )
296+ } else {
297+ cx. prefix_slice_suffix (
298+ & mut subpairs,
299+ extra_data,
300+ & place_builder,
301+ Some ( array_len) ,
302+ prefix,
303+ slice,
304+ suffix,
305+ ) ;
306+ None
307+ }
251308 } else {
252309 // If the array length couldn't be determined, ignore the
253310 // subpatterns and delayed-assert that compilation will fail.
@@ -258,37 +315,61 @@ impl<'tcx> MatchPairTree<'tcx> {
258315 pattern. ty
259316 ) ,
260317 ) ;
318+ None
261319 }
262-
263- None
264320 }
265321 PatKind :: Slice { ref prefix, ref slice, ref suffix } => {
266- cx. prefix_slice_suffix (
267- & mut subpairs,
268- extra_data,
269- & place_builder,
270- None ,
271- prefix,
272- slice,
273- suffix,
274- ) ;
275-
276- if prefix. is_empty ( ) && slice. is_some ( ) && suffix. is_empty ( ) {
277- // This pattern is shaped like `[..]`. It can match a slice
278- // of any length, so no length test is needed.
279- None
280- } else {
281- // Any other shape of slice pattern requires a length test.
282- // Slice patterns with a `..` subpattern require a minimum
283- // length; those without `..` require an exact length.
284- Some ( TestableCase :: Slice {
285- len : u64:: try_from ( prefix. len ( ) + suffix. len ( ) ) . unwrap ( ) ,
286- op : if slice. is_some ( ) {
287- SliceLenOp :: GreaterOrEqual
288- } else {
289- SliceLenOp :: Equal
322+ // When there is no `..`, all elements are constants, and
323+ // there are at least two of them, collapse the individual
324+ // element subpairs into a single aggregate comparison that
325+ // is performed after the length check.
326+ if slice. is_none ( )
327+ && suffix. is_empty ( )
328+ && cx. can_use_aggregate_eq ( )
329+ && let Some ( aggregate_value) =
330+ try_reconstruct_aggregate_constant ( cx. tcx , pattern. ty , prefix)
331+ {
332+ subpairs. push ( MatchPairTree {
333+ place,
334+ testable_case : TestableCase :: Constant {
335+ value : aggregate_value,
336+ kind : PatConstKind :: Aggregate ,
290337 } ,
338+ subpairs : Vec :: new ( ) ,
339+ pattern_span : pattern. span ,
340+ } ) ;
341+ Some ( TestableCase :: Slice {
342+ len : u64:: try_from ( prefix. len ( ) ) . unwrap ( ) ,
343+ op : SliceLenOp :: Equal ,
291344 } )
345+ } else {
346+ cx. prefix_slice_suffix (
347+ & mut subpairs,
348+ extra_data,
349+ & place_builder,
350+ None ,
351+ prefix,
352+ slice,
353+ suffix,
354+ ) ;
355+
356+ if prefix. is_empty ( ) && slice. is_some ( ) && suffix. is_empty ( ) {
357+ // This pattern is shaped like `[..]`. It can match
358+ // a slice of any length, so no length test is needed.
359+ None
360+ } else {
361+ // Any other shape of slice pattern requires a length test.
362+ // Slice patterns with a `..` subpattern require a minimum
363+ // length; those without `..` require an exact length.
364+ Some ( TestableCase :: Slice {
365+ len : u64:: try_from ( prefix. len ( ) + suffix. len ( ) ) . unwrap ( ) ,
366+ op : if slice. is_some ( ) {
367+ SliceLenOp :: GreaterOrEqual
368+ } else {
369+ SliceLenOp :: Equal
370+ } ,
371+ } )
372+ }
292373 }
293374 }
294375
0 commit comments