@@ -239,14 +239,24 @@ where
239
239
}
240
240
241
241
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
+ }
250
260
}
251
261
252
262
Call { cleanup, ref destination, ref func, ref args, .. } => {
@@ -293,64 +303,72 @@ where
293
303
}
294
304
}
295
305
296
- fn propagate_bits_into_switch_int_successors (
306
+ fn propagate_bits_into_enum_discriminant_switch_successors (
297
307
& mut self ,
298
308
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 > ,
300
312
dirty_list : & mut WorkQueue < BasicBlock > ,
301
- switch_on : & mir:: Operand < ' tcx > ,
302
313
values : & [ u128 ] ,
303
314
targets : & [ BasicBlock ] ,
304
315
) {
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
+ ) ;
329
325
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
+ }
340
336
341
- std:: mem:: drop ( tmp) ;
337
+ std:: mem:: drop ( tmp) ;
342
338
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
+ }
347
344
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) ,
352
368
}
353
369
}
370
+
371
+ _ => None ,
354
372
}
355
373
}
356
374
0 commit comments