@@ -805,19 +805,19 @@ pub enum Variants {
805805 variants : Vec < CachedLayout > ,
806806 } ,
807807
808- /// Two cases distinguished by a niche (a value invalid for a type):
808+ /// Multiple cases distinguished by a niche (values invalid for a type):
809809 /// the variant `dataful_variant` contains a niche at an arbitrary
810- /// offset (field 0 of the enum), which is set to `niche_value`
811- /// for the other variant .
810+ /// offset (field 0 of the enum), which for a variant with discriminant
811+ /// `d` is set to `(d - niche_variants.start).wrapping_add(niche_start)` .
812812 ///
813813 /// For example, `Option<(usize, &T)>` is represented such that
814814 /// `None` has a null pointer for the second tuple field, and
815815 /// `Some` is the identity function (with a non-null reference).
816816 NicheFilling {
817817 dataful_variant : usize ,
818- niche_variant : usize ,
818+ niche_variants : RangeInclusive < usize > ,
819819 niche : Scalar ,
820- niche_value : u128 ,
820+ niche_start : u128 ,
821821 variants : Vec < CachedLayout > ,
822822 }
823823}
@@ -1372,11 +1372,11 @@ impl<'a, 'tcx> CachedLayout {
13721372 } ) . collect :: < Result < Vec < _ > , _ > > ( )
13731373 } ) . collect :: < Result < Vec < _ > , _ > > ( ) ?;
13741374
1375- let ( inh_first, inh_second, inh_third ) = {
1375+ let ( inh_first, inh_second) = {
13761376 let mut inh_variants = ( 0 ..variants. len ( ) ) . filter ( |& v| {
13771377 variants[ v] . iter ( ) . all ( |f| f. abi != Abi :: Uninhabited )
13781378 } ) ;
1379- ( inh_variants. next ( ) , inh_variants. next ( ) , inh_variants . next ( ) )
1379+ ( inh_variants. next ( ) , inh_variants. next ( ) )
13801380 } ;
13811381 if inh_first. is_none ( ) {
13821382 // Uninhabited because it has no variants, or only uninhabited ones.
@@ -1472,68 +1472,94 @@ impl<'a, 'tcx> CachedLayout {
14721472 let no_explicit_discriminants = def. variants . iter ( ) . enumerate ( )
14731473 . all ( |( i, v) | v. discr == ty:: VariantDiscr :: Relative ( i) ) ;
14741474
1475- if inh_second. is_some ( ) && inh_third. is_none ( ) &&
1476- !def. repr . inhibit_enum_layout_opt ( ) &&
1477- no_explicit_discriminants {
1478- // Nullable pointer optimization
1479- let ( a, b) = ( inh_first. unwrap ( ) , inh_second. unwrap ( ) ) ;
1480- for & ( i, other) in & [ ( a, b) , ( b, a) ] {
1481- if !variants[ other] . iter ( ) . all ( |f| f. is_zst ( ) ) {
1482- continue ;
1475+ // Niche-filling enum optimization.
1476+ if !def. repr . inhibit_enum_layout_opt ( ) && no_explicit_discriminants {
1477+ let mut dataful_variant = None ;
1478+ let mut niche_variants = usize:: max_value ( ) ..=0 ;
1479+
1480+ // Find one non-ZST variant.
1481+ ' variants: for ( v, fields) in variants. iter ( ) . enumerate ( ) {
1482+ for f in fields {
1483+ if f. abi == Abi :: Uninhabited {
1484+ continue ' variants;
1485+ }
1486+ if !f. is_zst ( ) {
1487+ if dataful_variant. is_none ( ) {
1488+ dataful_variant = Some ( v) ;
1489+ continue ' variants;
1490+ } else {
1491+ dataful_variant = None ;
1492+ break ' variants;
1493+ }
1494+ }
1495+ }
1496+ if niche_variants. start > v {
1497+ niche_variants. start = v;
14831498 }
1499+ niche_variants. end = v;
1500+ }
1501+
1502+ if niche_variants. start > niche_variants. end {
1503+ dataful_variant = None ;
1504+ }
14841505
1506+ if let Some ( i) = dataful_variant {
1507+ let count = ( niche_variants. end - niche_variants. start + 1 ) as u128 ;
14851508 for ( field_index, field) in variants[ i] . iter ( ) . enumerate ( ) {
1486- if let Some ( ( offset, niche, niche_value) ) = field. find_niche ( cx) ? {
1487- let st = variants. iter ( ) . enumerate ( ) . map ( |( j, v) | {
1488- let mut st = univariant_uninterned ( v,
1489- & def. repr , StructKind :: AlwaysSized ) ?;
1490- st. variants = Variants :: Single { index : j } ;
1491- Ok ( st)
1492- } ) . collect :: < Result < Vec < _ > , _ > > ( ) ?;
1493-
1494- let offset = st[ i] . fields . offset ( field_index) + offset;
1495- let CachedLayout {
1496- size,
1497- mut align,
1498- mut primitive_align,
1499- ..
1500- } = st[ i] ;
1501-
1502- let mut niche_align = niche. value . align ( dl) ;
1503- let abi = if offset. bytes ( ) == 0 && niche. value . size ( dl) == size {
1504- Abi :: Scalar ( niche. clone ( ) )
1505- } else {
1506- let mut packed = st[ i] . abi . is_packed ( ) ;
1507- if offset. abi_align ( niche_align) != offset {
1508- packed = true ;
1509- niche_align = dl. i8_align ;
1510- }
1511- Abi :: Aggregate {
1512- sized : true ,
1513- packed
1514- }
1509+ let ( offset, niche, niche_start) =
1510+ match field. find_niche ( cx, count) ? {
1511+ Some ( niche) => niche,
1512+ None => continue
15151513 } ;
1516- align = align. max ( niche_align) ;
1517- primitive_align = primitive_align. max ( niche_align) ;
1518-
1519- return Ok ( tcx. intern_layout ( CachedLayout {
1520- variants : Variants :: NicheFilling {
1521- dataful_variant : i,
1522- niche_variant : other,
1523- niche,
1524- niche_value,
1525- variants : st,
1526- } ,
1527- fields : FieldPlacement :: Arbitrary {
1528- offsets : vec ! [ offset] ,
1529- memory_index : vec ! [ 0 ]
1530- } ,
1531- abi,
1532- size,
1533- align,
1534- primitive_align
1535- } ) ) ;
1536- }
1514+ let st = variants. iter ( ) . enumerate ( ) . map ( |( j, v) | {
1515+ let mut st = univariant_uninterned ( v,
1516+ & def. repr , StructKind :: AlwaysSized ) ?;
1517+ st. variants = Variants :: Single { index : j } ;
1518+ Ok ( st)
1519+ } ) . collect :: < Result < Vec < _ > , _ > > ( ) ?;
1520+
1521+ let offset = st[ i] . fields . offset ( field_index) + offset;
1522+ let CachedLayout {
1523+ size,
1524+ mut align,
1525+ mut primitive_align,
1526+ ..
1527+ } = st[ i] ;
1528+
1529+ let mut niche_align = niche. value . align ( dl) ;
1530+ let abi = if offset. bytes ( ) == 0 && niche. value . size ( dl) == size {
1531+ Abi :: Scalar ( niche. clone ( ) )
1532+ } else {
1533+ let mut packed = st[ i] . abi . is_packed ( ) ;
1534+ if offset. abi_align ( niche_align) != offset {
1535+ packed = true ;
1536+ niche_align = dl. i8_align ;
1537+ }
1538+ Abi :: Aggregate {
1539+ sized : true ,
1540+ packed
1541+ }
1542+ } ;
1543+ align = align. max ( niche_align) ;
1544+ primitive_align = primitive_align. max ( niche_align) ;
1545+
1546+ return Ok ( tcx. intern_layout ( CachedLayout {
1547+ variants : Variants :: NicheFilling {
1548+ dataful_variant : i,
1549+ niche_variants,
1550+ niche,
1551+ niche_start,
1552+ variants : st,
1553+ } ,
1554+ fields : FieldPlacement :: Arbitrary {
1555+ offsets : vec ! [ offset] ,
1556+ memory_index : vec ! [ 0 ]
1557+ } ,
1558+ abi,
1559+ size,
1560+ align,
1561+ primitive_align
1562+ } ) ) ;
15371563 }
15381564 }
15391565 }
@@ -2267,50 +2293,50 @@ impl<'a, 'tcx> TyLayout<'tcx> {
22672293 }
22682294
22692295 /// Find the offset of a niche leaf field, starting from
2270- /// the given type and recursing through aggregates.
2296+ /// the given type and recursing through aggregates, which
2297+ /// has at least `count` consecutive invalid values.
22712298 /// The tuple is `(offset, scalar, niche_value)`.
22722299 // FIXME(eddyb) traverse already optimized enums.
2273- fn find_niche < C > ( & self , cx : C )
2300+ fn find_niche < C > ( & self , cx : C , count : u128 )
22742301 -> Result < Option < ( Size , Scalar , u128 ) > , LayoutError < ' tcx > >
22752302 where C : LayoutOf < Ty < ' tcx > , TyLayout = Result < Self , LayoutError < ' tcx > > > +
22762303 HasTyCtxt < ' tcx >
22772304 {
22782305 let scalar_component = |scalar : & Scalar , offset| {
2279- // FIXME(eddyb) support negative/wrap-around discriminant ranges.
2280- let Scalar { value, ref valid_range } = * scalar;
2281- if valid_range. start < valid_range. end {
2282- let bits = value. size ( cx) . bits ( ) ;
2283- assert ! ( bits <= 128 ) ;
2284- let max_value = !0u128 >> ( 128 - bits) ;
2285- if valid_range. start > 0 {
2286- let niche = valid_range. start - 1 ;
2287- Ok ( Some ( ( offset, Scalar {
2288- value,
2289- valid_range : niche..=valid_range. end
2290- } , niche) ) )
2291- } else if valid_range. end < max_value {
2292- let niche = valid_range. end + 1 ;
2293- Ok ( Some ( ( offset, Scalar {
2294- value,
2295- valid_range : valid_range. start ..=niche
2296- } , niche) ) )
2297- } else {
2298- Ok ( None )
2299- }
2306+ let Scalar { value, valid_range : ref v } = * scalar;
2307+
2308+ let bits = value. size ( cx) . bits ( ) ;
2309+ assert ! ( bits <= 128 ) ;
2310+ let max_value = !0u128 >> ( 128 - bits) ;
2311+
2312+ // Find out how many values are outside the valid range.
2313+ let niches = if v. start <= v. end {
2314+ v. start + ( max_value - v. end )
23002315 } else {
2301- Ok ( None )
2316+ v. start - v. end - 1
2317+ } ;
2318+
2319+ // Give up if we can't fit `count` consecutive niches.
2320+ if count > niches {
2321+ return None ;
23022322 }
2323+
2324+ let niche_start = v. end . wrapping_add ( 1 ) & max_value;
2325+ let niche_end = v. end . wrapping_add ( count) & max_value;
2326+ Some ( ( offset, Scalar {
2327+ value,
2328+ valid_range : v. start ..=niche_end
2329+ } , niche_start) )
23032330 } ;
23042331
23052332 match self . abi {
23062333 Abi :: Scalar ( ref scalar) => {
2307- return scalar_component ( scalar, Size :: from_bytes ( 0 ) ) ;
2334+ return Ok ( scalar_component ( scalar, Size :: from_bytes ( 0 ) ) ) ;
23082335 }
23092336 Abi :: ScalarPair ( ref a, ref b) => {
2310- if let Some ( result) = scalar_component ( a, Size :: from_bytes ( 0 ) ) ? {
2311- return Ok ( Some ( result) ) ;
2312- }
2313- return scalar_component ( b, a. value . size ( cx) . abi_align ( b. value . align ( cx) ) ) ;
2337+ return Ok ( scalar_component ( a, Size :: from_bytes ( 0 ) ) . or_else ( || {
2338+ scalar_component ( b, a. value . size ( cx) . abi_align ( b. value . align ( cx) ) )
2339+ } ) ) ;
23142340 }
23152341 _ => { }
23162342 }
@@ -2323,13 +2349,13 @@ impl<'a, 'tcx> TyLayout<'tcx> {
23232349 return Ok ( None ) ;
23242350 }
23252351 }
2326- if let FieldPlacement :: Array { count , .. } = self . fields {
2327- if count > 0 {
2328- return self . field ( cx, 0 ) ?. find_niche ( cx) ;
2352+ if let FieldPlacement :: Array { .. } = self . fields {
2353+ if self . fields . count ( ) > 0 {
2354+ return self . field ( cx, 0 ) ?. find_niche ( cx, count ) ;
23292355 }
23302356 }
23312357 for i in 0 ..self . fields . count ( ) {
2332- let r = self . field ( cx, i) ?. find_niche ( cx) ?;
2358+ let r = self . field ( cx, i) ?. find_niche ( cx, count ) ?;
23332359 if let Some ( ( offset, scalar, niche_value) ) = r {
23342360 let offset = self . fields . offset ( i) + offset;
23352361 return Ok ( Some ( ( offset, scalar, niche_value) ) ) ;
@@ -2359,15 +2385,16 @@ impl<'gcx> HashStable<StableHashingContext<'gcx>> for Variants {
23592385 }
23602386 NicheFilling {
23612387 dataful_variant,
2362- niche_variant ,
2388+ niche_variants : RangeInclusive { start , end } ,
23632389 ref niche,
2364- niche_value ,
2390+ niche_start ,
23652391 ref variants,
23662392 } => {
23672393 dataful_variant. hash_stable ( hcx, hasher) ;
2368- niche_variant. hash_stable ( hcx, hasher) ;
2394+ start. hash_stable ( hcx, hasher) ;
2395+ end. hash_stable ( hcx, hasher) ;
23692396 niche. hash_stable ( hcx, hasher) ;
2370- niche_value . hash_stable ( hcx, hasher) ;
2397+ niche_start . hash_stable ( hcx, hasher) ;
23712398 variants. hash_stable ( hcx, hasher) ;
23722399 }
23732400 }
0 commit comments