]> git.lizzy.rs Git - rust.git/blob - src/discriminant.rs
Limit publicness to crate where possible and remove unused imports
[rust.git] / src / discriminant.rs
1 //! Adapted from https://github.com/rust-lang/rust/blob/d760df5aea483aae041c9a241e7acacf48f75035/src/librustc_codegen_ssa/mir/place.rs
2
3 use crate::prelude::*;
4
5 pub(crate) fn codegen_set_discriminant<'tcx>(
6     fx: &mut FunctionCx<'_, 'tcx, impl Backend>,
7     place: CPlace<'tcx>,
8     variant_index: VariantIdx,
9 ) {
10     let layout = place.layout();
11     if layout.for_variant(fx, variant_index).abi.is_uninhabited() {
12         return;
13     }
14     match layout.variants {
15         layout::Variants::Single { index } => {
16             assert_eq!(index, variant_index);
17         }
18         layout::Variants::Multiple {
19             discr: _,
20             discr_index,
21             discr_kind: layout::DiscriminantKind::Tag,
22             variants: _,
23         } => {
24             let ptr = place.place_field(fx, mir::Field::new(discr_index));
25             let to = layout
26                 .ty
27                 .discriminant_for_variant(fx.tcx, variant_index)
28                 .unwrap()
29                 .val;
30             let discr = CValue::const_val(fx, ptr.layout(), to);
31             ptr.write_cvalue(fx, discr);
32         }
33         layout::Variants::Multiple {
34             discr: _,
35             discr_index,
36             discr_kind:
37                 layout::DiscriminantKind::Niche {
38                     dataful_variant,
39                     ref niche_variants,
40                     niche_start,
41                 },
42             variants: _,
43         } => {
44             if variant_index != dataful_variant {
45                 let niche = place.place_field(fx, mir::Field::new(discr_index));
46                 let niche_value = variant_index.as_u32() - niche_variants.start().as_u32();
47                 let niche_value = u128::from(niche_value).wrapping_add(niche_start);
48                 let niche_llval = CValue::const_val(fx, niche.layout(), niche_value);
49                 niche.write_cvalue(fx, niche_llval);
50             }
51         }
52     }
53 }
54
55 pub(crate) fn codegen_get_discriminant<'tcx>(
56     fx: &mut FunctionCx<'_, 'tcx, impl Backend>,
57     value: CValue<'tcx>,
58     dest_layout: TyLayout<'tcx>,
59 ) -> CValue<'tcx> {
60     let layout = value.layout();
61
62     if layout.abi == layout::Abi::Uninhabited {
63         return trap_unreachable_ret_value(
64             fx,
65             dest_layout,
66             "[panic] Tried to get discriminant for uninhabited type.",
67         );
68     }
69
70     let (discr_scalar, discr_index, discr_kind) = match &layout.variants {
71         layout::Variants::Single { index } => {
72             let discr_val = layout
73                 .ty
74                 .discriminant_for_variant(fx.tcx, *index)
75                 .map_or(u128::from(index.as_u32()), |discr| discr.val);
76             return CValue::const_val(fx, dest_layout, discr_val);
77         }
78         layout::Variants::Multiple {
79             discr,
80             discr_index,
81             discr_kind,
82             variants: _,
83         } => (discr, *discr_index, discr_kind),
84     };
85
86     let cast_to = fx.clif_type(dest_layout.ty).unwrap();
87
88     // Read the tag/niche-encoded discriminant from memory.
89     let encoded_discr = value.value_field(fx, mir::Field::new(discr_index));
90     let encoded_discr = encoded_discr.load_scalar(fx);
91
92     // Decode the discriminant (specifically if it's niche-encoded).
93     match *discr_kind {
94         layout::DiscriminantKind::Tag => {
95             let signed = match discr_scalar.value {
96                 layout::Int(_, signed) => signed,
97                 _ => false,
98             };
99             let val = clif_intcast(fx, encoded_discr, cast_to, signed);
100             return CValue::by_val(val, dest_layout);
101         }
102         layout::DiscriminantKind::Niche {
103             dataful_variant,
104             ref niche_variants,
105             niche_start,
106         } => {
107             // Rebase from niche values to discriminants, and check
108             // whether the result is in range for the niche variants.
109
110             // We first compute the "relative discriminant" (wrt `niche_variants`),
111             // that is, if `n = niche_variants.end() - niche_variants.start()`,
112             // we remap `niche_start..=niche_start + n` (which may wrap around)
113             // to (non-wrap-around) `0..=n`, to be able to check whether the
114             // discriminant corresponds to a niche variant with one comparison.
115             // We also can't go directly to the (variant index) discriminant
116             // and check that it is in the range `niche_variants`, because
117             // that might not fit in the same type, on top of needing an extra
118             // comparison (see also the comment on `let niche_discr`).
119             let relative_discr = if niche_start == 0 {
120                 encoded_discr
121             } else {
122                 // FIXME handle niche_start > i64::MAX
123                 fx.bcx
124                     .ins()
125                     .iadd_imm(encoded_discr, -i64::try_from(niche_start).unwrap())
126             };
127             let relative_max = niche_variants.end().as_u32() - niche_variants.start().as_u32();
128             let is_niche = {
129                 codegen_icmp_imm(
130                     fx,
131                     IntCC::UnsignedLessThanOrEqual,
132                     relative_discr,
133                     i128::from(relative_max),
134                 )
135             };
136
137             // NOTE(eddyb) this addition needs to be performed on the final
138             // type, in case the niche itself can't represent all variant
139             // indices (e.g. `u8` niche with more than `256` variants,
140             // but enough uninhabited variants so that the remaining variants
141             // fit in the niche).
142             // In other words, `niche_variants.end - niche_variants.start`
143             // is representable in the niche, but `niche_variants.end`
144             // might not be, in extreme cases.
145             let niche_discr = {
146                 let relative_discr = if relative_max == 0 {
147                     // HACK(eddyb) since we have only one niche, we know which
148                     // one it is, and we can avoid having a dynamic value here.
149                     fx.bcx.ins().iconst(cast_to, 0)
150                 } else {
151                     clif_intcast(fx, relative_discr, cast_to, false)
152                 };
153                 fx.bcx
154                     .ins()
155                     .iadd_imm(relative_discr, i64::from(niche_variants.start().as_u32()))
156             };
157
158             let dataful_variant = fx
159                 .bcx
160                 .ins()
161                 .iconst(cast_to, i64::from(dataful_variant.as_u32()));
162             let discr = fx.bcx.ins().select(is_niche, niche_discr, dataful_variant);
163             CValue::by_val(discr, dest_layout)
164         }
165     }
166 }