Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 7af5de3

Browse files
committedOct 14, 2024·
make enum size not depend on the order of variants
1 parent c0838c8 commit 7af5de3

File tree

7 files changed

+323
-140
lines changed

7 files changed

+323
-140
lines changed
 

‎compiler/rustc_abi/src/layout.rs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
192192
pub fn layout_of_struct_or_enum<
193193
'a,
194194
FieldIdx: Idx,
195-
VariantIdx: Idx,
195+
VariantIdx: Idx + PartialOrd,
196196
F: Deref<Target = &'a LayoutS<FieldIdx, VariantIdx>> + fmt::Debug + Copy,
197197
>(
198198
&self,
@@ -464,7 +464,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
464464
fn layout_of_enum<
465465
'a,
466466
FieldIdx: Idx,
467-
VariantIdx: Idx,
467+
VariantIdx: Idx + PartialOrd,
468468
F: Deref<Target = &'a LayoutS<FieldIdx, VariantIdx>> + fmt::Debug + Copy,
469469
>(
470470
&self,
@@ -524,8 +524,16 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
524524
let niche_variants = all_indices.clone().find(|v| needs_disc(*v)).unwrap()
525525
..=all_indices.rev().find(|v| needs_disc(*v)).unwrap();
526526

527-
let count =
528-
(niche_variants.end().index() as u128 - niche_variants.start().index() as u128) + 1;
527+
let count = {
528+
let niche_variants_len = (niche_variants.end().index() as u128
529+
- niche_variants.start().index() as u128)
530+
+ 1;
531+
if niche_variants.contains(&largest_variant_index) {
532+
niche_variants_len - 1
533+
} else {
534+
niche_variants_len
535+
}
536+
};
529537

530538
// Use the largest niche in the largest variant.
531539
let niche = variant_layouts[largest_variant_index].largest_niche?;

‎compiler/rustc_abi/src/lib.rs

Lines changed: 50 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1475,15 +1475,59 @@ pub enum TagEncoding<VariantIdx: Idx> {
14751475
Direct,
14761476

14771477
/// Niche (values invalid for a type) encoding the discriminant:
1478-
/// Discriminant and variant index coincide.
1478+
/// Discriminant and variant index doesn't always coincide.
1479+
///
14791480
/// The variant `untagged_variant` contains a niche at an arbitrary
14801481
/// offset (field `tag_field` of the enum), which for a variant with
1481-
/// discriminant `d` is set to
1482-
/// `(d - niche_variants.start).wrapping_add(niche_start)`.
1482+
/// discriminant `d` is set to `d.wrapping_add(niche_start)`.
14831483
///
1484-
/// For example, `Option<(usize, &T)>` is represented such that
1485-
/// `None` has a null pointer for the second tuple field, and
1486-
/// `Some` is the identity function (with a non-null reference).
1484+
/// As for how to compute the discriminant, we have an optimization here that we allocate discriminant
1485+
/// value starting from the variant after the `untagged_variant` when the `untagged_variant` is
1486+
/// contained in `niche_variants`' range. Thus the `untagged_variant` won't be allocated with a
1487+
/// unneeded discriminant. Motivation for this is issue #117238.
1488+
/// For example,
1489+
/// ```rust
1490+
/// enum {
1491+
/// A, // 1
1492+
/// B, // 2
1493+
/// C(bool), // untagged_variant, no discriminant
1494+
/// D, // has a discriminant of 0
1495+
/// }
1496+
/// ```
1497+
/// The algorithm is as follows:
1498+
/// ```rust
1499+
/// // We ignore leading and trailing variants that don't need discriminants.
1500+
/// adjusted_len = niche_variants.end - niche_variants.start + 1
1501+
/// adjusted_index = variant_index - niche_variants.start
1502+
/// d = if niche_variants.contains(untagged_variant) {
1503+
/// adjusted_untagged_index = untagged_variant - niche_variants.start
1504+
/// (adjusted_index + adjusted_len - adjusted_untagged_index) % adjusted_len - 1
1505+
/// } else {
1506+
/// adjusted_index
1507+
/// }
1508+
/// tag_value = d.wrapping_add(niche_start)
1509+
/// ```
1510+
/// To load variant index from tag value:
1511+
/// ```rust
1512+
/// adjusted_len = niche_variants.end - niche_variants.start + 1
1513+
/// d = tag_value.wrapping_sub(niche_start)
1514+
/// variant_index = if niche_variants.contains(untagged_variant) {
1515+
/// if d < adjusted_len - 1 {
1516+
/// adjusted_untagged_index = untagged_variant - niche_variants.start
1517+
/// (d + 1 + adjusted_untagged_index) % adjusted_len + niche_variants.start
1518+
/// } else {
1519+
/// // When the discriminant is larger than the number of variants having
1520+
/// // discriminant, we know it represents the untagged_variant.
1521+
/// untagged_variant
1522+
/// }
1523+
/// } else {
1524+
/// if d < adjusted_len {
1525+
/// d + niche_variants.start
1526+
/// } else {
1527+
/// untagged_variant
1528+
/// }
1529+
/// }
1530+
/// ```
14871531
Niche {
14881532
untagged_variant: VariantIdx,
14891533
niche_variants: RangeInclusive<VariantIdx>,

‎compiler/rustc_codegen_cranelift/src/discriminant.rs

Lines changed: 105 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,20 @@ pub(crate) fn codegen_set_discriminant<'tcx>(
5151
variants: _,
5252
} => {
5353
if variant_index != untagged_variant {
54+
let discr_len = niche_variants.end().index() - niche_variants.start().index() + 1;
55+
let adj_idx = variant_index.index() - niche_variants.start().index();
56+
5457
let niche = place.place_field(fx, FieldIdx::new(tag_field));
5558
let niche_type = fx.clif_type(niche.layout().ty).unwrap();
56-
let niche_value = variant_index.as_u32() - niche_variants.start().as_u32();
57-
let niche_value = (niche_value as u128).wrapping_add(niche_start);
59+
60+
let discr = if niche_variants.contains(&untagged_variant) {
61+
let adj_untagged_idx =
62+
untagged_variant.index() - niche_variants.start().index();
63+
(adj_idx + discr_len - adj_untagged_idx) % discr_len - 1;
64+
} else {
65+
adj_idx
66+
};
67+
let niche_value = (discr as u128).wrapping_add(niche_start);
5868
let niche_value = match niche_type {
5969
types::I128 => {
6070
let lsb = fx.bcx.ins().iconst(types::I64, niche_value as u64 as i64);
@@ -130,72 +140,103 @@ pub(crate) fn codegen_get_discriminant<'tcx>(
130140
dest.write_cvalue(fx, res);
131141
}
132142
TagEncoding::Niche { untagged_variant, ref niche_variants, niche_start } => {
133-
let relative_max = niche_variants.end().as_u32() - niche_variants.start().as_u32();
134-
135-
// We have a subrange `niche_start..=niche_end` inside `range`.
136-
// If the value of the tag is inside this subrange, it's a
137-
// "niche value", an increment of the discriminant. Otherwise it
138-
// indicates the untagged variant.
139-
// A general algorithm to extract the discriminant from the tag
140-
// is:
141-
// relative_tag = tag - niche_start
142-
// is_niche = relative_tag <= (ule) relative_max
143-
// discr = if is_niche {
144-
// cast(relative_tag) + niche_variants.start()
145-
// } else {
146-
// untagged_variant
147-
// }
148-
// However, we will likely be able to emit simpler code.
149-
150-
let (is_niche, tagged_discr, delta) = if relative_max == 0 {
151-
// Best case scenario: only one tagged variant. This will
152-
// likely become just a comparison and a jump.
153-
// The algorithm is:
154-
// is_niche = tag == niche_start
155-
// discr = if is_niche {
156-
// niche_start
157-
// } else {
158-
// untagged_variant
159-
// }
160-
let is_niche = codegen_icmp_imm(fx, IntCC::Equal, tag, niche_start as i128);
143+
// See the algorithm explanation in the definition of `TagEncoding::Niche`.
144+
let discr_len = niche_variants.end().index() - niche_variants.start().index() + 1;
145+
146+
let niche_start = match fx.bcx.func.dfg.value_type(tag) {
147+
types::I128 => {
148+
let lsb = fx.bcx.ins().iconst(types::I64, niche_start as u64 as i64);
149+
let msb = fx.bcx.ins().iconst(types::I64, (niche_start >> 64) as u64 as i64);
150+
fx.bcx.ins().iconcat(lsb, msb)
151+
}
152+
ty => fx.bcx.ins().iconst(ty, niche_start as i64),
153+
};
154+
155+
let (is_niche, tagged_discr) = if discr_len == 1 {
156+
// Special case where we only have a single tagged variant.
157+
// The untagged variant can't be contained in niche_variant's range in this case.
158+
// Thus the discriminant of the only tagged variant is 0 and its variant index
159+
// is the start of niche_variants.
160+
let is_niche = codegen_icmp_imm(fx, IntCC::Equal, tag, niche_start);
161161
let tagged_discr =
162162
fx.bcx.ins().iconst(cast_to, niche_variants.start().as_u32() as i64);
163-
(is_niche, tagged_discr, 0)
163+
(is_niche, tagged_discr)
164164
} else {
165-
// The special cases don't apply, so we'll have to go with
166-
// the general algorithm.
167-
let niche_start = match fx.bcx.func.dfg.value_type(tag) {
168-
types::I128 => {
169-
let lsb = fx.bcx.ins().iconst(types::I64, niche_start as u64 as i64);
170-
let msb =
171-
fx.bcx.ins().iconst(types::I64, (niche_start >> 64) as u64 as i64);
172-
fx.bcx.ins().iconcat(lsb, msb)
173-
}
174-
ty => fx.bcx.ins().iconst(ty, niche_start as i64),
175-
};
176-
let relative_discr = fx.bcx.ins().isub(tag, niche_start);
177-
let cast_tag = clif_intcast(fx, relative_discr, cast_to, false);
178-
let is_niche = crate::common::codegen_icmp_imm(
179-
fx,
180-
IntCC::UnsignedLessThanOrEqual,
181-
relative_discr,
182-
i128::from(relative_max),
183-
);
184-
(is_niche, cast_tag, niche_variants.start().as_u32() as u128)
185-
};
165+
// General case.
166+
let discr = fx.bcx.ins().isub(tag, niche_start);
167+
let tagged_discr = clif_intcast(fx, relative_discr, cast_to, false);
168+
if niche_variants.contains(&untagged_variant) {
169+
let is_niche = crate::common::codegen_icmp_imm(
170+
fx,
171+
IntCC::UnsignedLessThan,
172+
discr,
173+
i128::from(discr_len - 1),
174+
);
175+
let adj_untagged_idx =
176+
untagged_variant.index() - niche_variants.start().index();
177+
let untagged_delta = 1 + adj_untagged_idx;
178+
let untagged_delta = match cast_to {
179+
types::I128 => {
180+
let lsb = fx.bcx.ins().iconst(types::I64, untagged_delta as u64 as i64);
181+
let msb = fx
182+
.bcx
183+
.ins()
184+
.iconst(types::I64, (untagged_delta >> 64) as u64 as i64);
185+
fx.bcx.ins().iconcat(lsb, msb)
186+
}
187+
ty => fx.bcx.ins().iconst(ty, untagged_delta as i64),
188+
};
189+
let tagged_discr = fx.bcx.ins().iadd(tagged_discr, untagged_delta);
186190

187-
let tagged_discr = if delta == 0 {
188-
tagged_discr
189-
} else {
190-
let delta = match cast_to {
191-
types::I128 => {
192-
let lsb = fx.bcx.ins().iconst(types::I64, delta as u64 as i64);
193-
let msb = fx.bcx.ins().iconst(types::I64, (delta >> 64) as u64 as i64);
194-
fx.bcx.ins().iconcat(lsb, msb)
195-
}
196-
ty => fx.bcx.ins().iconst(ty, delta as i64),
197-
};
198-
fx.bcx.ins().iadd(tagged_discr, delta)
191+
let discr_len = match cast_to {
192+
types::I128 => {
193+
let lsb = fx.bcx.ins().iconst(types::I64, discr_len as u64 as i64);
194+
let msb =
195+
fx.bcx.ins().iconst(types::I64, (discr_len >> 64) as u64 as i64);
196+
fx.bcx.ins().iconcat(lsb, msb)
197+
}
198+
ty => fx.bcx.ins().iconst(ty, discr_len as i64),
199+
};
200+
let tagged_discr = fx.bcx.ins().urem(tagged_discr, discr_len);
201+
202+
let niche_variants_start = niche_variants.start().index();
203+
let niche_variants_start = match cast_to {
204+
types::I128 => {
205+
let lsb =
206+
fx.bcx.ins().iconst(types::I64, niche_variants_start as u64 as i64);
207+
let msb = fx
208+
.bcx
209+
.ins()
210+
.iconst(types::I64, (niche_variants_start >> 64) as u64 as i64);
211+
fx.bcx.ins().iconcat(lsb, msb)
212+
}
213+
ty => fx.bcx.ins().iconst(ty, niche_variants_start as i64),
214+
};
215+
let tagged_discr = fx.bcx.ins().iadd(tagged_discr, niche_variants_start);
216+
(is_niche, tagged_discr)
217+
} else {
218+
let is_niche = crate::common::codegen_icmp_imm(
219+
fx,
220+
IntCC::UnsignedLessThan,
221+
discr,
222+
i128::from(discr_len - 1),
223+
);
224+
let niche_variants_start = niche_variants.start().index();
225+
let niche_variants_start = match cast_to {
226+
types::I128 => {
227+
let lsb =
228+
fx.bcx.ins().iconst(types::I64, niche_variants_start as u64 as i64);
229+
let msb = fx
230+
.bcx
231+
.ins()
232+
.iconst(types::I64, (niche_variants_start >> 64) as u64 as i64);
233+
fx.bcx.ins().iconcat(lsb, msb)
234+
}
235+
ty => fx.bcx.ins().iconst(ty, niche_variants_start as i64),
236+
};
237+
let tagged_discr = fx.bcx.ins().iadd(tagged_discr, niche_variants_start);
238+
(is_niche, tagged_discr)
239+
}
199240
};
200241

201242
let untagged_variant = if cast_to == types::I128 {

‎compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -391,9 +391,17 @@ fn compute_discriminant_value<'ll, 'tcx>(
391391

392392
DiscrResult::Range(min, max)
393393
} else {
394-
let value = (variant_index.as_u32() as u128)
395-
.wrapping_sub(niche_variants.start().as_u32() as u128)
396-
.wrapping_add(niche_start);
394+
let discr_len = niche_variants.end().index() - niche_variants.start().index() + 1;
395+
let adj_idx = variant_index.index() - niche_variants.start().index();
396+
397+
let discr = if niche_variants.contains(&untagged_variant) {
398+
let adj_untagged_idx =
399+
untagged_variant.index() - niche_variants.start().index();
400+
(adj_idx + discr_len - adj_untagged_idx) % discr_len - 1
401+
} else {
402+
adj_idx
403+
};
404+
let value = (discr as u128).wrapping_add(niche_start);
397405
let value = tag.size(cx).truncate(value);
398406
DiscrResult::Value(value)
399407
}

‎compiler/rustc_codegen_ssa/src/mir/place.rs

Lines changed: 55 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -287,54 +287,53 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
287287
_ => (tag_imm, bx.cx().immediate_backend_type(tag_op.layout)),
288288
};
289289

290-
let relative_max = niche_variants.end().as_u32() - niche_variants.start().as_u32();
291-
292-
// We have a subrange `niche_start..=niche_end` inside `range`.
293-
// If the value of the tag is inside this subrange, it's a
294-
// "niche value", an increment of the discriminant. Otherwise it
295-
// indicates the untagged variant.
296-
// A general algorithm to extract the discriminant from the tag
297-
// is:
298-
// relative_tag = tag - niche_start
299-
// is_niche = relative_tag <= (ule) relative_max
300-
// discr = if is_niche {
301-
// cast(relative_tag) + niche_variants.start()
302-
// } else {
303-
// untagged_variant
304-
// }
305-
// However, we will likely be able to emit simpler code.
306-
let (is_niche, tagged_discr, delta) = if relative_max == 0 {
307-
// Best case scenario: only one tagged variant. This will
308-
// likely become just a comparison and a jump.
309-
// The algorithm is:
310-
// is_niche = tag == niche_start
311-
// discr = if is_niche {
312-
// niche_start
313-
// } else {
314-
// untagged_variant
315-
// }
316-
let niche_start = bx.cx().const_uint_big(tag_llty, niche_start);
290+
// See the algorithm explanation in the definition of `TagEncoding::Niche`.
291+
let discr_len = niche_variants.end().index() - niche_variants.start().index() + 1;
292+
let niche_start = bx.cx().const_uint_big(tag_llty, niche_start);
293+
let (is_niche, tagged_discr) = if discr_len == 1 {
294+
// Special case where we only have a single tagged variant.
295+
// The untagged variant can't be contained in niche_variant's range in this case.
296+
// Thus the discriminant of the only tagged variant is 0 and its variant index
297+
// is the start of niche_variants.
317298
let is_niche = bx.icmp(IntPredicate::IntEQ, tag, niche_start);
318299
let tagged_discr =
319300
bx.cx().const_uint(cast_to, niche_variants.start().as_u32() as u64);
320-
(is_niche, tagged_discr, 0)
301+
(is_niche, tagged_discr)
321302
} else {
322-
// The special cases don't apply, so we'll have to go with
323-
// the general algorithm.
324-
let relative_discr = bx.sub(tag, bx.cx().const_uint_big(tag_llty, niche_start));
325-
let cast_tag = bx.intcast(relative_discr, cast_to, false);
326-
let is_niche = bx.icmp(
327-
IntPredicate::IntULE,
328-
relative_discr,
329-
bx.cx().const_uint(tag_llty, relative_max as u64),
330-
);
331-
(is_niche, cast_tag, niche_variants.start().as_u32() as u128)
332-
};
333-
334-
let tagged_discr = if delta == 0 {
335-
tagged_discr
336-
} else {
337-
bx.add(tagged_discr, bx.cx().const_uint_big(cast_to, delta))
303+
// General case.
304+
let discr = bx.sub(tag, niche_start);
305+
let tagged_discr = bx.intcast(discr, cast_to, false);
306+
if niche_variants.contains(&untagged_variant) {
307+
let is_niche = bx.icmp(
308+
IntPredicate::IntULT,
309+
discr,
310+
bx.cx().const_uint(tag_llty, (discr_len - 1) as u64),
311+
);
312+
let adj_untagged_idx =
313+
untagged_variant.index() - niche_variants.start().index();
314+
let tagged_discr = bx.add(
315+
tagged_discr,
316+
bx.cx().const_uint_big(cast_to, (1 + adj_untagged_idx) as u128),
317+
);
318+
let tagged_discr = bx
319+
.urem(tagged_discr, bx.cx().const_uint_big(cast_to, discr_len as u128));
320+
let tagged_discr = bx.add(
321+
tagged_discr,
322+
bx.cx().const_uint_big(cast_to, niche_variants.start().index() as u128),
323+
);
324+
(is_niche, tagged_discr)
325+
} else {
326+
let is_niche = bx.icmp(
327+
IntPredicate::IntULT,
328+
discr,
329+
bx.cx().const_uint(tag_llty, discr_len as u64),
330+
);
331+
let tagged_discr = bx.add(
332+
tagged_discr,
333+
bx.cx().const_uint_big(cast_to, niche_variants.start().index() as u128),
334+
);
335+
(is_niche, tagged_discr)
336+
}
338337
};
339338

340339
let discr = bx.select(
@@ -384,10 +383,20 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
384383
..
385384
} => {
386385
if variant_index != untagged_variant {
386+
let discr_len =
387+
niche_variants.end().index() - niche_variants.start().index() + 1;
388+
let adj_idx = variant_index.index() - niche_variants.start().index();
389+
387390
let niche = self.project_field(bx, tag_field);
388391
let niche_llty = bx.cx().immediate_backend_type(niche.layout);
389-
let niche_value = variant_index.as_u32() - niche_variants.start().as_u32();
390-
let niche_value = (niche_value as u128).wrapping_add(niche_start);
392+
let discr = if niche_variants.contains(&untagged_variant) {
393+
let adj_untagged_idx =
394+
untagged_variant.index() - niche_variants.start().index();
395+
(adj_idx + discr_len - adj_untagged_idx) % discr_len - 1
396+
} else {
397+
adj_idx
398+
};
399+
let niche_value = (discr as u128).wrapping_add(niche_start);
391400
// FIXME(eddyb): check the actual primitive type here.
392401
let niche_llval = if niche_value == 0 {
393402
// HACK(eddyb): using `c_null` as it works on all types.

‎compiler/rustc_const_eval/src/interpret/discriminant.rs

Lines changed: 57 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -165,31 +165,59 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
165165
untagged_variant
166166
}
167167
Ok(tag_bits) => {
168+
// See the algorithm explanation in the definition of `TagEncoding::Niche`.
169+
let discr_len = (variants_end - variants_start)
170+
.checked_add(1)
171+
.expect("the number of niche variants fits into u32");
172+
168173
let tag_bits = tag_bits.to_bits(tag_layout.size);
169174
// We need to use machine arithmetic to get the relative variant idx:
170-
// variant_index_relative = tag_val - niche_start_val
171175
let tag_val = ImmTy::from_uint(tag_bits, tag_layout);
172176
let niche_start_val = ImmTy::from_uint(niche_start, tag_layout);
173177
let variant_index_relative_val =
174178
self.binary_op(mir::BinOp::Sub, &tag_val, &niche_start_val)?;
175179
let variant_index_relative =
176180
variant_index_relative_val.to_scalar().to_bits(tag_val.layout.size)?;
177181
// Check if this is in the range that indicates an actual discriminant.
178-
if variant_index_relative <= u128::from(variants_end - variants_start) {
179-
let variant_index_relative = u32::try_from(variant_index_relative)
180-
.expect("we checked that this fits into a u32");
181-
// Then computing the absolute variant idx should not overflow any more.
182-
let variant_index = VariantIdx::from_u32(
183-
variants_start
184-
.checked_add(variant_index_relative)
185-
.expect("overflow computing absolute variant idx"),
186-
);
187-
let variants =
188-
ty.ty_adt_def().expect("tagged layout for non adt").variants();
189-
assert!(variant_index < variants.next_index());
190-
variant_index
182+
if niche_variants.contains(&untagged_variant) {
183+
if variant_index_relative < u128::from(discr_len) {
184+
let adj_untagged_idx = untagged_variant.as_u32() - variants_start;
185+
let variant_index_relative = u32::try_from(variant_index_relative)
186+
.expect("we checked that this fits into a u32");
187+
let variant_index_to_modulo = variant_index_relative
188+
.checked_add(1)
189+
.expect("overflow computing absolute variant idx")
190+
.checked_add(adj_untagged_idx)
191+
.expect("overflow computing absolute variant idx");
192+
let variant_index = VariantIdx::from_u32(
193+
variants_start
194+
.checked_add(variant_index_to_modulo % discr_len)
195+
.expect("overflow computing absolute variant idx"),
196+
);
197+
let variants =
198+
ty.ty_adt_def().expect("tagged layout for non adt").variants();
199+
assert!(variant_index < variants.next_index());
200+
variant_index
201+
} else {
202+
untagged_variant
203+
}
191204
} else {
192-
untagged_variant
205+
if variant_index_relative < u128::from(discr_len) {
206+
let variant_index_relative = u32::try_from(variant_index_relative)
207+
.expect("we checked that this fits into a u32");
208+
// Then computing the absolute variant idx should not overflow any more.
209+
let variant_index = VariantIdx::from_u32(
210+
variants_start
211+
.checked_add(variant_index_relative)
212+
.expect("overflow computing absolute variant idx"),
213+
);
214+
let variants =
215+
ty.ty_adt_def().expect("tagged layout for non adt").variants();
216+
assert!(variant_index < variants.next_index());
217+
variant_index
218+
} else {
219+
untagged_variant
220+
}
193221
}
194222
}
195223
};
@@ -285,13 +313,25 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
285313
..
286314
} => {
287315
assert!(variant_index != untagged_variant);
316+
let discr_len = (niche_variants.end().as_u32() - niche_variants.start().as_u32())
317+
.checked_add(1)
318+
.expect("the number of niche variants fits into u32");
288319
let variants_start = niche_variants.start().as_u32();
289-
let variant_index_relative = variant_index
320+
let adj_idx = variant_index
290321
.as_u32()
291322
.checked_sub(variants_start)
292323
.expect("overflow computing relative variant idx");
324+
325+
let variant_index_relative = if niche_variants.contains(&untagged_variant) {
326+
let adj_untagged_idx = untagged_variant.as_u32() - variants_start;
327+
let adj_idx_to_modulo = adj_idx
328+
.checked_add(discr_len - adj_untagged_idx)
329+
.expect("overflow computing relative variant idx");
330+
adj_idx_to_modulo % discr_len - 1
331+
} else {
332+
adj_idx
333+
};
293334
// We need to use machine arithmetic when taking into account `niche_start`:
294-
// tag_val = variant_index_relative + niche_start_val
295335
let tag_layout = self.layout_of(tag_layout.primitive().to_int_ty(*self.tcx))?;
296336
let niche_start_val = ImmTy::from_uint(niche_start, tag_layout);
297337
let variant_index_relative_val =
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
//@ run-pass
2+
3+
#![feature(rustc_attrs)]
4+
#![allow(internal_features)]
5+
#![allow(dead_code)]
6+
7+
#[rustc_layout_scalar_valid_range_start(2)]
8+
struct U8WithTwoNiches(u8);
9+
10+
// 1 bytes.
11+
enum Order1 {
12+
A(U8WithTwoNiches),
13+
B,
14+
C,
15+
}
16+
17+
enum Order2 {
18+
A,
19+
B(U8WithTwoNiches),
20+
C,
21+
}
22+
23+
enum Order3 {
24+
A,
25+
B,
26+
C(U8WithTwoNiches),
27+
}
28+
29+
fn main() {
30+
assert_eq!(std::mem::size_of::<Order1>(), 1);
31+
assert_eq!(std::mem::size_of::<Order2>(), 1);
32+
assert_eq!(std::mem::size_of::<Order3>(), 1);
33+
}

0 commit comments

Comments
 (0)
Please sign in to comment.