@@ -239,14 +239,24 @@ where
239239 }
240240
241241 SwitchInt { ref targets, ref values, ref discr, .. } => {
242- self . propagate_bits_into_switch_int_successors (
243- in_out,
244- ( bb, bb_data) ,
245- dirty_list,
246- discr,
247- & * values,
248- & * targets,
249- ) ;
242+ // If this is a switch on an enum discriminant, a custom effect may be applied
243+ // along each outgoing edge.
244+ if let Some ( place) = discr. place ( ) {
245+ let enum_def = switch_on_enum_discriminant ( self . tcx , self . body , bb_data, place) ;
246+ if let Some ( enum_def) = enum_def {
247+ self . propagate_bits_into_enum_discriminant_switch_successors (
248+ in_out, bb, enum_def, place, dirty_list, & * values, & * targets,
249+ ) ;
250+
251+ return ;
252+ }
253+ }
254+
255+ // Otherwise, it's just a normal `SwitchInt`, and every successor sees the same
256+ // exit state.
257+ for target in targets. iter ( ) . copied ( ) {
258+ self . propagate_bits_into_entry_set_for ( & in_out, target, dirty_list) ;
259+ }
250260 }
251261
252262 Call { cleanup, ref destination, ref func, ref args, .. } => {
@@ -293,64 +303,72 @@ where
293303 }
294304 }
295305
296- fn propagate_bits_into_switch_int_successors (
306+ fn propagate_bits_into_enum_discriminant_switch_successors (
297307 & mut self ,
298308 in_out : & mut BitSet < A :: Idx > ,
299- ( bb, bb_data) : ( BasicBlock , & mir:: BasicBlockData < ' tcx > ) ,
309+ bb : BasicBlock ,
310+ enum_def : & ' tcx ty:: AdtDef ,
311+ enum_place : & mir:: Place < ' tcx > ,
300312 dirty_list : & mut WorkQueue < BasicBlock > ,
301- switch_on : & mir:: Operand < ' tcx > ,
302313 values : & [ u128 ] ,
303314 targets : & [ BasicBlock ] ,
304315 ) {
305- match bb_data. statements . last ( ) . map ( |stmt| & stmt. kind ) {
306- // Look at the last statement to see if it is an assignment of an enum discriminant to
307- // the local that determines the target of a `SwitchInt` like so:
308- // _42 = discriminant(..)
309- // SwitchInt(_42, ..)
310- Some ( mir:: StatementKind :: Assign ( box ( lhs, mir:: Rvalue :: Discriminant ( enum_) ) ) )
311- if Some ( lhs) == switch_on. place ( ) =>
312- {
313- let adt = match enum_. ty ( self . body , self . tcx ) . ty . kind {
314- ty:: Adt ( def, _) => def,
315- _ => bug ! ( "Switch on discriminant of non-ADT" ) ,
316- } ;
317-
318- // MIR building adds discriminants to the `values` array in the same order as they
319- // are yielded by `AdtDef::discriminants`. We rely on this to match each
320- // discriminant in `values` to its corresponding variant in linear time.
321- let mut tmp = BitSet :: new_empty ( in_out. domain_size ( ) ) ;
322- let mut discriminants = adt. discriminants ( self . tcx ) ;
323- for ( value, target) in values. iter ( ) . zip ( targets. iter ( ) . copied ( ) ) {
324- let ( variant_idx, _) =
325- discriminants. find ( |& ( _, discr) | discr. val == * value) . expect (
326- "Order of `AdtDef::discriminants` differed \
327- from that of `SwitchInt::values`",
328- ) ;
316+ // MIR building adds discriminants to the `values` array in the same order as they
317+ // are yielded by `AdtDef::discriminants`. We rely on this to match each
318+ // discriminant in `values` to its corresponding variant in linear time.
319+ let mut tmp = BitSet :: new_empty ( in_out. domain_size ( ) ) ;
320+ let mut discriminants = enum_def. discriminants ( self . tcx ) ;
321+ for ( value, target) in values. iter ( ) . zip ( targets. iter ( ) . copied ( ) ) {
322+ let ( variant_idx, _) = discriminants. find ( |& ( _, discr) | discr. val == * value) . expect (
323+ "Order of `AdtDef::discriminants` differed from that of `SwitchInt::values`" ,
324+ ) ;
329325
330- tmp. overwrite ( in_out) ;
331- self . analysis . apply_discriminant_switch_effect (
332- & mut tmp,
333- bb,
334- enum_ ,
335- adt ,
336- variant_idx,
337- ) ;
338- self . propagate_bits_into_entry_set_for ( & tmp, target, dirty_list) ;
339- }
326+ tmp. overwrite ( in_out) ;
327+ self . analysis . apply_discriminant_switch_effect (
328+ & mut tmp,
329+ bb,
330+ enum_place ,
331+ enum_def ,
332+ variant_idx,
333+ ) ;
334+ self . propagate_bits_into_entry_set_for ( & tmp, target, dirty_list) ;
335+ }
340336
341- std:: mem:: drop ( tmp) ;
337+ std:: mem:: drop ( tmp) ;
342338
343- // Propagate dataflow state along the "otherwise" edge.
344- let otherwise = targets. last ( ) . copied ( ) . unwrap ( ) ;
345- self . propagate_bits_into_entry_set_for ( & in_out, otherwise, dirty_list) ;
346- }
339+ // Propagate dataflow state along the "otherwise" edge.
340+ let otherwise = targets. last ( ) . copied ( ) . unwrap ( ) ;
341+ self . propagate_bits_into_entry_set_for ( & in_out, otherwise, dirty_list) ;
342+ }
343+ }
347344
348- _ => {
349- for target in targets. iter ( ) . copied ( ) {
350- self . propagate_bits_into_entry_set_for ( & in_out, target, dirty_list) ;
351- }
345+ /// Look at the last statement of a block that ends with to see if it is an assignment of an enum
346+ /// discriminant to the local that determines the target of a `SwitchInt` like so:
347+ /// _42 = discriminant(..)
348+ /// SwitchInt(_42, ..)
349+ fn switch_on_enum_discriminant (
350+ tcx : TyCtxt < ' tcx > ,
351+ body : & mir:: Body < ' tcx > ,
352+ block : & mir:: BasicBlockData < ' tcx > ,
353+ switch_on : & mir:: Place < ' tcx > ,
354+ ) -> Option < & ' tcx ty:: AdtDef > {
355+ match block. statements . last ( ) . map ( |stmt| & stmt. kind ) {
356+ Some ( mir:: StatementKind :: Assign ( box ( lhs, mir:: Rvalue :: Discriminant ( discriminated) ) ) )
357+ if lhs == switch_on =>
358+ {
359+ match & discriminated. ty ( body, tcx) . ty . kind {
360+ ty:: Adt ( def, _) => Some ( def) ,
361+
362+ // `Rvalue::Discriminant` is also used to get the active yield point for a
363+ // generator, but we do not need edge-specific effects in that case. This may
364+ // change in the future.
365+ ty:: Generator ( ..) => None ,
366+
367+ t => bug ! ( "`discriminant` called on unexpected type {:?}" , t) ,
352368 }
353369 }
370+
371+ _ => None ,
354372 }
355373}
356374
0 commit comments