1 //! Handling of enum discriminants
3 //! Adapted from https://github.com/rust-lang/rust/blob/d760df5aea483aae041c9a241e7acacf48f75035/src/librustc_codegen_ssa/mir/place.rs
5 use rustc_target::abi::{Int, TagEncoding, Variants};
9 pub(crate) fn codegen_set_discriminant<'tcx>(
10 fx: &mut FunctionCx<'_, 'tcx, impl Module>,
12 variant_index: VariantIdx,
14 let layout = place.layout();
15 if layout.for_variant(fx, variant_index).abi.is_uninhabited() {
18 match layout.variants {
19 Variants::Single { index } => {
20 assert_eq!(index, variant_index);
25 tag_encoding: TagEncoding::Direct,
28 let ptr = place.place_field(fx, mir::Field::new(tag_field));
31 .discriminant_for_variant(fx.tcx, variant_index)
35 let discr = CValue::const_val(fx, ptr.layout(), to);
36 ptr.write_cvalue(fx, discr);
49 if variant_index != dataful_variant {
50 let niche = place.place_field(fx, mir::Field::new(tag_field));
51 let niche_value = variant_index.as_u32() - niche_variants.start().as_u32();
52 let niche_value = u128::from(niche_value).wrapping_add(niche_start);
53 let niche_llval = CValue::const_val(fx, niche.layout(), niche_value.into());
54 niche.write_cvalue(fx, niche_llval);
60 pub(crate) fn codegen_get_discriminant<'tcx>(
61 fx: &mut FunctionCx<'_, 'tcx, impl Module>,
63 dest_layout: TyAndLayout<'tcx>,
65 let layout = value.layout();
67 if layout.abi == Abi::Uninhabited {
68 return trap_unreachable_ret_value(
71 "[panic] Tried to get discriminant for uninhabited type.",
75 let (tag_scalar, tag_field, tag_encoding) = match &layout.variants {
76 Variants::Single { index } => {
77 let discr_val = layout
79 .discriminant_for_variant(fx.tcx, *index)
80 .map_or(u128::from(index.as_u32()), |discr| discr.val);
81 return CValue::const_val(fx, dest_layout, discr_val.into());
88 } => (tag, *tag_field, tag_encoding),
91 let cast_to = fx.clif_type(dest_layout.ty).unwrap();
93 // Read the tag/niche-encoded discriminant from memory.
94 let tag = value.value_field(fx, mir::Field::new(tag_field));
95 let tag = tag.load_scalar(fx);
97 // Decode the discriminant (specifically if it's niche-encoded).
99 TagEncoding::Direct => {
100 let signed = match tag_scalar.value {
101 Int(_, signed) => signed,
104 let val = clif_intcast(fx, tag, cast_to, signed);
105 CValue::by_val(val, dest_layout)
112 // Rebase from niche values to discriminants, and check
113 // whether the result is in range for the niche variants.
115 // We first compute the "relative discriminant" (wrt `niche_variants`),
116 // that is, if `n = niche_variants.end() - niche_variants.start()`,
117 // we remap `niche_start..=niche_start + n` (which may wrap around)
118 // to (non-wrap-around) `0..=n`, to be able to check whether the
119 // discriminant corresponds to a niche variant with one comparison.
120 // We also can't go directly to the (variant index) discriminant
121 // and check that it is in the range `niche_variants`, because
122 // that might not fit in the same type, on top of needing an extra
123 // comparison (see also the comment on `let niche_discr`).
124 let relative_discr = if niche_start == 0 {
127 // FIXME handle niche_start > i64::MAX
130 .iadd_imm(tag, -i64::try_from(niche_start).unwrap())
132 let relative_max = niche_variants.end().as_u32() - niche_variants.start().as_u32();
136 IntCC::UnsignedLessThanOrEqual,
138 i128::from(relative_max),
142 // NOTE(eddyb) this addition needs to be performed on the final
143 // type, in case the niche itself can't represent all variant
144 // indices (e.g. `u8` niche with more than `256` variants,
145 // but enough uninhabited variants so that the remaining variants
146 // fit in the niche).
147 // In other words, `niche_variants.end - niche_variants.start`
148 // is representable in the niche, but `niche_variants.end`
149 // might not be, in extreme cases.
151 let relative_discr = if relative_max == 0 {
152 // HACK(eddyb) since we have only one niche, we know which
153 // one it is, and we can avoid having a dynamic value here.
154 fx.bcx.ins().iconst(cast_to, 0)
156 clif_intcast(fx, relative_discr, cast_to, false)
160 .iadd_imm(relative_discr, i64::from(niche_variants.start().as_u32()))
163 let dataful_variant = fx
166 .iconst(cast_to, i64::from(dataful_variant.as_u32()));
167 let discr = fx.bcx.ins().select(is_niche, niche_discr, dataful_variant);
168 CValue::by_val(discr, dest_layout)