|
1 |
| -use rustc_abi::{Align, BackendRepr, FieldsShape, Size, TagEncoding, VariantIdx, Variants}; |
| 1 | +use rustc_abi::{ |
| 2 | + Align, BackendRepr, FieldIdx, FieldsShape, Size, TagEncoding, VariantIdx, Variants, |
| 3 | +}; |
2 | 4 | use rustc_middle::mir::PlaceTy;
|
3 | 5 | use rustc_middle::mir::interpret::Scalar;
|
4 | 6 | use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, LayoutOf, TyAndLayout};
|
@@ -239,53 +241,17 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
|
239 | 241 | bx: &mut Bx,
|
240 | 242 | variant_index: VariantIdx,
|
241 | 243 | ) {
|
242 |
| - if self.layout.for_variant(bx.cx(), variant_index).is_uninhabited() { |
243 |
| - // We play it safe by using a well-defined `abort`, but we could go for immediate UB |
244 |
| - // if that turns out to be helpful. |
245 |
| - bx.abort(); |
246 |
| - return; |
247 |
| - } |
248 |
| - match self.layout.variants { |
249 |
| - Variants::Empty => unreachable!("we already handled uninhabited types"), |
250 |
| - Variants::Single { index } => assert_eq!(index, variant_index), |
251 |
| - |
252 |
| - Variants::Multiple { tag_encoding: TagEncoding::Direct, tag_field, .. } => { |
253 |
| - let ptr = self.project_field(bx, tag_field.as_usize()); |
254 |
| - let to = |
255 |
| - self.layout.ty.discriminant_for_variant(bx.tcx(), variant_index).unwrap().val; |
256 |
| - bx.store_to_place( |
257 |
| - bx.cx().const_uint_big(bx.cx().backend_type(ptr.layout), to), |
258 |
| - ptr.val, |
259 |
| - ); |
| 244 | + match codegen_tag_value(bx.cx(), variant_index, self.layout) { |
| 245 | + Err(UninhabitedVariantError) => { |
| 246 | + // We play it safe by using a well-defined `abort`, but we could go for immediate UB |
| 247 | + // if that turns out to be helpful. |
| 248 | + bx.abort(); |
260 | 249 | }
|
261 |
| - Variants::Multiple { |
262 |
| - tag_encoding: |
263 |
| - TagEncoding::Niche { untagged_variant, ref niche_variants, niche_start }, |
264 |
| - tag_field, |
265 |
| - .. |
266 |
| - } => { |
267 |
| - if variant_index != untagged_variant { |
268 |
| - let niche = self.project_field(bx, tag_field.as_usize()); |
269 |
| - let niche_llty = bx.cx().immediate_backend_type(niche.layout); |
270 |
| - let BackendRepr::Scalar(scalar) = niche.layout.backend_repr else { |
271 |
| - bug!("expected a scalar placeref for the niche"); |
272 |
| - }; |
273 |
| - // We are supposed to compute `niche_value.wrapping_add(niche_start)` wrapping |
274 |
| - // around the `niche`'s type. |
275 |
| - // The easiest way to do that is to do wrapping arithmetic on `u128` and then |
276 |
| - // masking off any extra bits that occur because we did the arithmetic with too many bits. |
277 |
| - let niche_value = variant_index.as_u32() - niche_variants.start().as_u32(); |
278 |
| - let niche_value = (niche_value as u128).wrapping_add(niche_start); |
279 |
| - let niche_value = niche_value & niche.layout.size.unsigned_int_max(); |
280 |
| - |
281 |
| - let niche_llval = bx.cx().scalar_to_backend( |
282 |
| - Scalar::from_uint(niche_value, niche.layout.size), |
283 |
| - scalar, |
284 |
| - niche_llty, |
285 |
| - ); |
286 |
| - OperandValue::Immediate(niche_llval).store(bx, niche); |
287 |
| - } |
| 250 | + Ok(Some((tag_field, imm))) => { |
| 251 | + let tag_place = self.project_field(bx, tag_field.as_usize()); |
| 252 | + OperandValue::Immediate(imm).store(bx, tag_place); |
288 | 253 | }
|
| 254 | + Ok(None) => {} |
289 | 255 | }
|
290 | 256 | }
|
291 | 257 |
|
@@ -471,3 +437,73 @@ fn round_up_const_value_to_alignment<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
471 | 437 | let offset = bx.and(neg_value, align_minus_1);
|
472 | 438 | bx.add(value, offset)
|
473 | 439 | }
|
| 440 | + |
| 441 | +/// Calculates the value that needs to be stored to mark the discriminant. |
| 442 | +/// |
| 443 | +/// This might be `None` for a `struct` or a niched variant (like `Some(&3)`). |
| 444 | +/// |
| 445 | +/// If it's `Some`, it returns the value to store and the field in which to |
| 446 | +/// store it. Note that this value is *not* the same as the discriminant, in |
| 447 | +/// general, as it might be a niche value or have a different size. |
| 448 | +/// |
| 449 | +/// It might also be an `Err` because the variant is uninhabited. |
| 450 | +pub(super) fn codegen_tag_value<'tcx, V>( |
| 451 | + cx: &impl CodegenMethods<'tcx, Value = V>, |
| 452 | + variant_index: VariantIdx, |
| 453 | + layout: TyAndLayout<'tcx>, |
| 454 | +) -> Result<Option<(FieldIdx, V)>, UninhabitedVariantError> { |
| 455 | + // By checking uninhabited-ness first we don't need to worry about types |
| 456 | + // like `(u32, !)` which are single-variant but weird. |
| 457 | + if layout.for_variant(cx, variant_index).is_uninhabited() { |
| 458 | + return Err(UninhabitedVariantError); |
| 459 | + } |
| 460 | + |
| 461 | + Ok(match layout.variants { |
| 462 | + Variants::Empty => unreachable!("we already handled uninhabited types"), |
| 463 | + Variants::Single { index } => { |
| 464 | + assert_eq!(index, variant_index); |
| 465 | + None |
| 466 | + } |
| 467 | + |
| 468 | + Variants::Multiple { tag_encoding: TagEncoding::Direct, tag_field, .. } => { |
| 469 | + let discr = layout.ty.discriminant_for_variant(cx.tcx(), variant_index); |
| 470 | + let to = discr.unwrap().val; |
| 471 | + let tag_layout = layout.field(cx, tag_field.as_usize()); |
| 472 | + let tag_llty = cx.immediate_backend_type(tag_layout); |
| 473 | + let imm = cx.const_uint_big(tag_llty, to); |
| 474 | + Some((tag_field, imm)) |
| 475 | + } |
| 476 | + Variants::Multiple { |
| 477 | + tag_encoding: TagEncoding::Niche { untagged_variant, ref niche_variants, niche_start }, |
| 478 | + tag_field, |
| 479 | + .. |
| 480 | + } => { |
| 481 | + if variant_index != untagged_variant { |
| 482 | + let niche_layout = layout.field(cx, tag_field.as_usize()); |
| 483 | + let niche_llty = cx.immediate_backend_type(niche_layout); |
| 484 | + let BackendRepr::Scalar(scalar) = niche_layout.backend_repr else { |
| 485 | + bug!("expected a scalar placeref for the niche"); |
| 486 | + }; |
| 487 | + // We are supposed to compute `niche_value.wrapping_add(niche_start)` wrapping |
| 488 | + // around the `niche`'s type. |
| 489 | + // The easiest way to do that is to do wrapping arithmetic on `u128` and then |
| 490 | + // masking off any extra bits that occur because we did the arithmetic with too many bits. |
| 491 | + let niche_value = variant_index.as_u32() - niche_variants.start().as_u32(); |
| 492 | + let niche_value = (niche_value as u128).wrapping_add(niche_start); |
| 493 | + let niche_value = niche_value & niche_layout.size.unsigned_int_max(); |
| 494 | + |
| 495 | + let niche_llval = cx.scalar_to_backend( |
| 496 | + Scalar::from_uint(niche_value, niche_layout.size), |
| 497 | + scalar, |
| 498 | + niche_llty, |
| 499 | + ); |
| 500 | + Some((tag_field, niche_llval)) |
| 501 | + } else { |
| 502 | + None |
| 503 | + } |
| 504 | + } |
| 505 | + }) |
| 506 | +} |
| 507 | + |
| 508 | +#[derive(Debug)] |
| 509 | +pub(super) struct UninhabitedVariantError; |
0 commit comments