|
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