11//! Removes assignments to ZST places.
22
33use crate :: transform:: MirPass ;
4- use rustc_middle:: mir:: { Body , StatementKind } ;
4+ use rustc_middle:: mir:: tcx:: PlaceTy ;
5+ use rustc_middle:: mir:: { Body , LocalDecls , Place , StatementKind } ;
56use rustc_middle:: ty:: { self , Ty , TyCtxt } ;
67
78pub struct RemoveZsts ;
@@ -28,6 +29,9 @@ impl<'tcx> MirPass<'tcx> for RemoveZsts {
2829 if !layout. is_zst ( ) {
2930 continue ;
3031 }
32+ if involves_a_union ( place, local_decls, tcx) {
33+ continue ;
34+ }
3135 if tcx. consider_optimizing ( || {
3236 format ! (
3337 "RemoveZsts - Place: {:?} SourceInfo: {:?}" ,
@@ -55,3 +59,31 @@ fn maybe_zst(ty: Ty<'_>) -> bool {
5559 _ => false ,
5660 }
5761}
62+
63+ /// Miri lazily allocates memory for locals on assignment,
64+ /// so we must preserve writes to unions and union fields,
65+ /// or it will ICE on reads of those fields.
66+ fn involves_a_union < ' tcx > (
67+ place : Place < ' tcx > ,
68+ local_decls : & LocalDecls < ' tcx > ,
69+ tcx : TyCtxt < ' tcx > ,
70+ ) -> bool {
71+ let mut place_ty = PlaceTy :: from_ty ( local_decls[ place. local ] . ty ) ;
72+ if is_union ( place_ty. ty ) {
73+ return true ;
74+ }
75+ for elem in place. projection {
76+ place_ty = place_ty. projection_ty ( tcx, elem) ;
77+ if is_union ( place_ty. ty ) {
78+ return true ;
79+ }
80+ }
81+ return false ;
82+ }
83+
84+ fn is_union ( ty : Ty < ' _ > ) -> bool {
85+ match ty. kind ( ) {
86+ ty:: Adt ( def, _) if def. is_union ( ) => true ,
87+ _ => false ,
88+ }
89+ }
0 commit comments