@@ -8,6 +8,9 @@ use rustc_middle::mir::*;
88use rustc_middle:: span_bug;
99use rustc_middle:: thir:: * ;
1010use rustc_middle:: ty:: CanonicalUserTypeAnnotation ;
11+ use rustc_middle:: ty:: util:: Discr ;
12+ use rustc_pattern_analysis:: constructor:: Constructor ;
13+ use rustc_pattern_analysis:: rustc:: RustcPatCtxt ;
1114use rustc_span:: source_map:: Spanned ;
1215use tracing:: { debug, instrument} ;
1316
@@ -244,6 +247,29 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
244247 ExprKind :: LoopMatch { state, region_scope, ref arms, .. } => {
245248 // FIXME add diagram
246249
250+ let dropless_arena = rustc_arena:: DroplessArena :: default ( ) ;
251+ let typeck_results = this. tcx . typeck ( this. def_id ) ;
252+ let lint_level = this. tcx . local_def_id_to_hir_id ( this. def_id ) ;
253+
254+ // the PatCtxt is normally used in pattern exhaustiveness checking, but reused here
255+ // because it performs normalization and const evaluation.
256+ let cx = RustcPatCtxt {
257+ tcx : this. tcx ,
258+ typeck_results,
259+ module : this. tcx . parent_module ( lint_level) . to_def_id ( ) ,
260+ // FIXME(#132279): We're in a body, should handle opaques.
261+ typing_env : rustc_middle:: ty:: TypingEnv :: non_body_analysis (
262+ this. tcx ,
263+ this. def_id ,
264+ ) ,
265+ dropless_arena : & dropless_arena,
266+ match_lint_level : lint_level,
267+ whole_match_span : Some ( rustc_span:: Span :: default ( ) ) ,
268+ scrut_span : rustc_span:: Span :: default ( ) ,
269+ refutable : true ,
270+ known_valid_scrutinee : true ,
271+ } ;
272+
247273 let loop_block = this. cfg . start_new_block ( ) ;
248274
249275 // Start the loop.
@@ -264,59 +290,139 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
264290 this. diverge_from ( loop_block) ;
265291
266292 let state_place = unpack ! ( body_block = this. as_place( body_block, state) ) ;
293+ let state_ty = this. thir . exprs [ state] . ty ;
267294
268- unpack ! (
269- body_block = this. in_scope( ( region_scope, source_info) , LintLevel :: Inherited , move |this| {
270- let unreachable_block = this. cfg. start_new_block( ) ;
271- this. cfg. terminate( unreachable_block, source_info, TerminatorKind :: Unreachable ) ;
295+ // the type of the value that is switched on by the `SwitchInt`
296+ let discr_ty = match state_ty {
297+ ty if ty. is_enum ( ) => ty. discriminant_ty ( this. tcx ) ,
298+ ty if ty. is_integral ( ) => ty,
299+ _ => todo ! ( ) ,
300+ } ;
272301
273- let mut arm_blocks = arms . iter ( ) . map ( | & arm| {
274- let block = this . cfg . start_new_block ( ) ;
275- match & this . thir [ arm ] . pattern . kind {
276- PatKind :: Variant { adt_def , args : _ , variant_index , subpatterns } => {
277- assert! ( subpatterns . is_empty ( ) ) ;
302+ let rvalue = match state_ty {
303+ ty if ty . is_enum ( ) => Rvalue :: Discriminant ( state_place ) ,
304+ ty if ty . is_integral ( ) => Rvalue :: Use ( Operand :: Copy ( state_place ) ) ,
305+ _ => todo ! ( ) ,
306+ } ;
278307
279- let discr = adt_def. discriminants( this. tcx) . find( |( var, _discr) | var == variant_index) . unwrap( ) . 1 ;
308+ // block and arm of the wildcard pattern (if any)
309+ let mut otherwise = None ;
280310
281- ( discr, block, arm)
282- }
283- _ => panic!( ) ,
284- }
285- } ) . collect:: <Vec <_>>( ) ;
286- arm_blocks. sort_by_cached_key( |& ( discr, _, _) |{
287- match & this. thir[ arms[ 0 ] ] . pattern. kind {
288- PatKind :: Variant { adt_def, ..} => {
289- adt_def. discriminants( this. tcx) . position( |( _, i) | discr. val == i. val) . unwrap( )
311+ unpack ! (
312+ body_block = this. in_scope(
313+ ( region_scope, source_info) ,
314+ LintLevel :: Inherited ,
315+ move |this| {
316+ let mut arm_blocks = Vec :: with_capacity( arms. len( ) ) ;
317+ for & arm in arms {
318+ let pat = & this. thir[ arm] . pattern;
319+ let deconstructed_pat = cx. lower_pat( pat) ;
320+
321+ match deconstructed_pat. ctor( ) {
322+ Constructor :: Variant ( variant_index) => {
323+ let PatKind :: Variant { adt_def, .. } = pat. kind else {
324+ unreachable!( )
325+ } ;
326+
327+ let discr = adt_def
328+ . discriminant_for_variant( this. tcx, * variant_index) ;
329+
330+ let block = this. cfg. start_new_block( ) ;
331+ arm_blocks. push( ( discr, block, arm) )
332+ }
333+ Constructor :: IntRange ( int_range) => {
334+ assert!( int_range. is_singleton( ) ) ;
335+ let Some ( value) = int_range. lo. as_finite_uint( ) else {
336+ todo!( )
337+ } ;
338+
339+ let discr =
340+ Discr { val: value, ty: * * deconstructed_pat. ty( ) } ;
341+
342+ let block = this. cfg. start_new_block( ) ;
343+ arm_blocks. push( ( discr, block, arm) )
344+ }
345+ Constructor :: Wildcard => {
346+ otherwise = Some ( ( this. cfg. start_new_block( ) , arm) ) ;
347+ }
348+ other => todo!( "{:?}" , other) ,
290349 }
291- _ => panic!( )
292350 }
293- } ) ;
294351
295- let targets = SwitchTargets :: new(
296- arm_blocks. iter( ) . map( |& ( discr, block, _arm) | ( discr. val, block) ) ,
297- unreachable_block,
298- ) ;
299- this. in_const_continuable_scope( loop_block, targets. clone( ) , state_place, |this| {
300- let discr_ty = match & this. thir[ arms[ 0 ] ] . pattern. kind {
301- PatKind :: Variant { adt_def, ..} => {
302- adt_def. discriminants( this. tcx) . next( ) . unwrap( ) . 1 . ty
352+ arm_blocks. sort_by_cached_key( |& ( discr, _, _) | {
353+ match & this. thir[ arms[ 0 ] ] . pattern. kind {
354+ PatKind :: Variant { adt_def, .. } => adt_def
355+ . discriminants( this. tcx)
356+ . position( |( _, i) | discr. val == i. val)
357+ . unwrap( ) ,
358+ PatKind :: ExpandedConstant { .. } => 0 ,
359+ PatKind :: Constant { .. } => 0 ,
360+ other => todo!( "{:?}" , other) ,
303361 }
304- _ => panic!( )
305- } ;
306- let discr = this. temp( discr_ty, source_info. span) ;
307- this. cfg. push_assign( body_block, source_info, discr, Rvalue :: Discriminant ( state_place) ) ;
308- let discr = Operand :: Copy ( discr) ;
309- this. cfg. terminate( body_block, source_info, TerminatorKind :: SwitchInt { discr, targets } ) ;
310-
311- for ( _discr, mut block, arm) in arm_blocks {
312- let empty_place = this. get_unit_temp( ) ;
313- unpack!( block = this. expr_into_dest( empty_place, block, this. thir[ arm] . body) ) ;
314- this. cfg. terminate( block, source_info, TerminatorKind :: Unreachable ) ;
315- }
362+ } ) ;
363+
364+ let targets = SwitchTargets :: new(
365+ arm_blocks
366+ . iter( )
367+ . map( |& ( discr, block, _arm) | ( discr. val, block) ) ,
368+ if let Some ( ( block, _) ) = otherwise {
369+ block
370+ } else {
371+ let unreachable_block = this. cfg. start_new_block( ) ;
372+ this. cfg. terminate(
373+ unreachable_block,
374+ source_info,
375+ TerminatorKind :: Unreachable ,
376+ ) ;
377+ unreachable_block
378+ } ,
379+ ) ;
380+
381+ this. in_const_continuable_scope(
382+ loop_block,
383+ targets. clone( ) ,
384+ state_place,
385+ |this| {
386+ let discr = this. temp( discr_ty, source_info. span) ;
387+ this. cfg. push_assign(
388+ body_block,
389+ source_info,
390+ discr,
391+ rvalue,
392+ ) ;
393+ let discr = Operand :: Copy ( discr) ;
394+ this. cfg. terminate(
395+ body_block,
396+ source_info,
397+ TerminatorKind :: SwitchInt { discr, targets } ,
398+ ) ;
316399
317- None
318- } )
319- } )
400+ let it = arm_blocks
401+ . into_iter( )
402+ . map( |( _, block, arm) | ( block, arm) )
403+ . chain( otherwise) ;
404+
405+ for ( mut block, arm) in it {
406+ let empty_place = this. get_unit_temp( ) ;
407+ unpack!(
408+ block = this. expr_into_dest(
409+ empty_place,
410+ block,
411+ this. thir[ arm] . body
412+ )
413+ ) ;
414+ this. cfg. terminate(
415+ block,
416+ source_info,
417+ TerminatorKind :: Unreachable ,
418+ ) ;
419+ }
420+
421+ None
422+ } ,
423+ )
424+ }
425+ )
320426 ) ;
321427
322428 this. cfg . goto ( body_block, source_info, loop_block) ;
0 commit comments