@@ -5,11 +5,11 @@ use std::borrow::Cow;
5
5
6
6
use either:: { Left , Right } ;
7
7
use 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 } ;
9
9
use rustc_middle:: { bug, mir, span_bug} ;
10
10
use rustc_span:: sym;
11
11
use rustc_target:: abi:: call:: { ArgAbi , FnAbi , PassMode } ;
12
- use rustc_target:: abi:: { self , FieldIdx , Integer } ;
12
+ use rustc_target:: abi:: { self , FieldIdx , Integer , VariantIdx } ;
13
13
use rustc_target:: spec:: abi:: Abi ;
14
14
use tracing:: { info, instrument, trace} ;
15
15
@@ -93,29 +93,46 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
93
93
94
94
/// Unwrap types that are guaranteed a null-pointer-optimization
95
95
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.
98
97
let ty:: Adt ( def, args) = layout. ty . kind ( ) else {
99
98
// Not an ADT, so definitely no NPO.
100
99
return interp_ok ( layout) ;
101
100
} ;
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
+ }
115
114
}
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)
116
126
} else {
117
- return interp_ok ( layout) ; // no NPO
127
+ // No varant is all-1-ZST, so no NPO.
128
+ return interp_ok ( layout) ;
118
129
} ;
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) ?;
119
136
120
137
// Check if the inner type is one of the NPO-guaranteed ones.
121
138
// For that we first unpeel transparent *structs* (but not unions).
0 commit comments