@@ -4,9 +4,9 @@ use std::assert_matches::assert_matches;
44use std:: borrow:: Cow ;
55
66use either:: { Left , Right } ;
7- use rustc_abi:: { self as abi, ExternAbi , FieldIdx , Integer } ;
7+ use rustc_abi:: { self as abi, ExternAbi , FieldIdx , Integer , VariantIdx } ;
88use rustc_middle:: ty:: layout:: { FnAbiOf , IntegerExt , LayoutOf , TyAndLayout } ;
9- use rustc_middle:: ty:: { self , AdtDef , Instance , Ty } ;
9+ use rustc_middle:: ty:: { self , AdtDef , Instance , Ty , VariantDef } ;
1010use rustc_middle:: { bug, mir, span_bug} ;
1111use rustc_span:: sym;
1212use rustc_target:: callconv:: { ArgAbi , FnAbi , PassMode } ;
@@ -92,29 +92,46 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
9292
9393 /// Unwrap types that are guaranteed a null-pointer-optimization
9494 fn unfold_npo ( & self , layout : TyAndLayout < ' tcx > ) -> InterpResult < ' tcx , TyAndLayout < ' tcx > > {
95- // Check if this is `Option` wrapping some type or if this is `Result` wrapping a 1-ZST and
96- // another type.
95+ // Check if this is an option-like type wrapping some type.
9796 let ty:: Adt ( def, args) = layout. ty . kind ( ) else {
9897 // Not an ADT, so definitely no NPO.
9998 return interp_ok ( layout) ;
10099 } ;
101- let inner = if self . tcx . is_diagnostic_item ( sym :: Option , def . did ( ) ) {
102- // The wrapped type is the only arg .
103- self . layout_of ( args [ 0 ] . as_type ( ) . unwrap ( ) ) ?
104- } else if self . tcx . is_diagnostic_item ( sym :: Result , def . did ( ) ) {
105- // We want to extract which (if any) of the args is not a 1-ZST.
106- let lhs = self . layout_of ( args [ 0 ] . as_type ( ) . unwrap ( ) ) ? ;
107- let rhs = self . layout_of ( args [ 1 ] . as_type ( ) . unwrap ( ) ) ? ;
108- if lhs . is_1zst ( ) {
109- rhs
110- } else if rhs . is_1zst ( ) {
111- lhs
112- } else {
113- return interp_ok ( layout ) ; // no NPO
100+ if def . variants ( ) . len ( ) != 2 {
101+ // Not a 2-variant enum, so no NPO .
102+ return interp_ok ( layout ) ;
103+ }
104+ assert ! ( def . is_enum ( ) ) ;
105+
106+ let all_fields_1zst = | variant : & VariantDef | -> InterpResult < ' tcx , _ > {
107+ for field in & variant . fields {
108+ let ty = field . ty ( * self . tcx , args ) ;
109+ let layout = self . layout_of ( ty ) ? ;
110+ if !layout . is_1zst ( ) {
111+ return interp_ok ( false ) ;
112+ }
114113 }
114+ interp_ok ( true )
115+ } ;
116+
117+ // If one variant consists entirely of 1-ZST, then the other variant
118+ // is the only "relevant" one for this check.
119+ let var0 = VariantIdx :: from_u32 ( 0 ) ;
120+ let var1 = VariantIdx :: from_u32 ( 1 ) ;
121+ let relevant_variant = if all_fields_1zst ( def. variant ( var0) ) ? {
122+ def. variant ( var1)
123+ } else if all_fields_1zst ( def. variant ( var1) ) ? {
124+ def. variant ( var0)
115125 } else {
116- return interp_ok ( layout) ; // no NPO
126+ // No varant is all-1-ZST, so no NPO.
127+ return interp_ok ( layout) ;
117128 } ;
129+ // The "relevant" variant must have exactly one field, and its type is the "inner" type.
130+ if relevant_variant. fields . len ( ) != 1 {
131+ return interp_ok ( layout) ;
132+ }
133+ let inner = relevant_variant. fields [ FieldIdx :: from_u32 ( 0 ) ] . ty ( * self . tcx , args) ;
134+ let inner = self . layout_of ( inner) ?;
118135
119136 // Check if the inner type is one of the NPO-guaranteed ones.
120137 // For that we first unpeel transparent *structs* (but not unions).
0 commit comments