]> git.lizzy.rs Git - rust.git/blob - src/discriminant.rs
Rollup merge of #78674 - tmiasko:inline-substs-for-mir-body, r=oli-obk
[rust.git] / src / discriminant.rs
1 //! Handling of enum discriminants
2 //!
3 //! Adapted from <https://github.com/rust-lang/rust/blob/d760df5aea483aae041c9a241e7acacf48f75035/src/librustc_codegen_ssa/mir/place.rs>
4
5 use rustc_target::abi::{Int, TagEncoding, Variants};
6
7 use crate::prelude::*;
8
9 pub(crate) fn codegen_set_discriminant<'tcx>(
10     fx: &mut FunctionCx<'_, 'tcx, impl Module>,
11     place: CPlace<'tcx>,
12     variant_index: VariantIdx,
13 ) {
14     let layout = place.layout();
15     if layout.for_variant(fx, variant_index).abi.is_uninhabited() {
16         return;
17     }
18     match layout.variants {
19         Variants::Single { index } => {
20             assert_eq!(index, variant_index);
21         }
22         Variants::Multiple {
23             tag: _,
24             tag_field,
25             tag_encoding: TagEncoding::Direct,
26             variants: _,
27         } => {
28             let ptr = place.place_field(fx, mir::Field::new(tag_field));
29             let to = layout
30                 .ty
31                 .discriminant_for_variant(fx.tcx, variant_index)
32                 .unwrap()
33                 .val
34                 .into();
35             let discr = CValue::const_val(fx, ptr.layout(), to);
36             ptr.write_cvalue(fx, discr);
37         }
38         Variants::Multiple {
39             tag: _,
40             tag_field,
41             tag_encoding:
42                 TagEncoding::Niche {
43                     dataful_variant,
44                     ref niche_variants,
45                     niche_start,
46                 },
47             variants: _,
48         } => {
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);
55             }
56         }
57     }
58 }
59
60 pub(crate) fn codegen_get_discriminant<'tcx>(
61     fx: &mut FunctionCx<'_, 'tcx, impl Module>,
62     value: CValue<'tcx>,
63     dest_layout: TyAndLayout<'tcx>,
64 ) -> CValue<'tcx> {
65     let layout = value.layout();
66
67     if layout.abi == Abi::Uninhabited {
68         return trap_unreachable_ret_value(
69             fx,
70             dest_layout,
71             "[panic] Tried to get discriminant for uninhabited type.",
72         );
73     }
74
75     let (tag_scalar, tag_field, tag_encoding) = match &layout.variants {
76         Variants::Single { index } => {
77             let discr_val = layout
78                 .ty
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());
82         }
83         Variants::Multiple {
84             tag,
85             tag_field,
86             tag_encoding,
87             variants: _,
88         } => (tag, *tag_field, tag_encoding),
89     };
90
91     let cast_to = fx.clif_type(dest_layout.ty).unwrap();
92
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);
96
97     // Decode the discriminant (specifically if it's niche-encoded).
98     match *tag_encoding {
99         TagEncoding::Direct => {
100             let signed = match tag_scalar.value {
101                 Int(_, signed) => signed,
102                 _ => false,
103             };
104             let val = clif_intcast(fx, tag, cast_to, signed);
105             CValue::by_val(val, dest_layout)
106         }
107         TagEncoding::Niche {
108             dataful_variant,
109             ref niche_variants,
110             niche_start,
111         } => {
112             // Rebase from niche values to discriminants, and check
113             // whether the result is in range for the niche variants.
114
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 {
125                 tag
126             } else {
127                 // FIXME handle niche_start > i64::MAX
128                 fx.bcx
129                     .ins()
130                     .iadd_imm(tag, -i64::try_from(niche_start).unwrap())
131             };
132             let relative_max = niche_variants.end().as_u32() - niche_variants.start().as_u32();
133             let is_niche = {
134                 codegen_icmp_imm(
135                     fx,
136                     IntCC::UnsignedLessThanOrEqual,
137                     relative_discr,
138                     i128::from(relative_max),
139                 )
140             };
141
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.
150             let niche_discr = {
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)
155                 } else {
156                     clif_intcast(fx, relative_discr, cast_to, false)
157                 };
158                 fx.bcx
159                     .ins()
160                     .iadd_imm(relative_discr, i64::from(niche_variants.start().as_u32()))
161             };
162
163             let dataful_variant = fx
164                 .bcx
165                 .ins()
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)
169         }
170     }
171 }