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