Change:
Execute with Miri: The problem is that and do not take the reduced field alignment into account and calculate the offset as-if the standard alignment was in place.
The labels are incorrect. Neither does this require nightly, nor is it related to const-eval. This is I-unsound on stable.
label I-prioritize (Looks like can't set .)
Error: Label can only be set by Rust team members Please let know if you're having trouble with this bot.
Error: Parsing relabel command in failed: ...'const-eval' ' "I-unsoun'... Please let know if you're having trouble with this bot.
Assigning as discussed as part of the and removing .
label -I-prioritize +P-medium
labels -T-types Discussed in types team on-site -- we think this is a MIR building bug, in any case, not part of T-types. Throwing hot back to compiler team.
It was actually a bug in the codegen backend, introduced when support for was -- the existing code has logic that assumed "if it is packed then alignment is 1", and that was never re-visited.
Ah, scratch that -- there was a different related bug (the sizeofval / alignofval computation for packed structs with tail was wrong), caused by what I said above. This here was caused by the codegen backend just never considering packed types when computing the offset of the unsized field. Almost always this happened to work since the unsized field is a slice/str and so its alignment (and therefore its offset) is dynamically known, but when dyn trait tails are involved this starts to go wrong. This got fixed by ; will add a test.

Discussion:
debug!("DST {} layout: {:?}", t, layout); let i = layout.fields.count() - 1; <del> let sized_size = layout.fields.offset(i).bytes(); </del> <ins> let unsized_offset_unadjusted = layout.fields.offset(i).bytes(); </ins> let sized_align = layout.align.abi.bytes(); <del> debug!("DST {} statically sized prefix size: {} align: {}", t, sized_size, sized_align); let sized_size = bx.const_usize(sized_size); </del> <ins> debug!( "DST {} offset of dyn field: {}, statically sized align: {}", t, unsized_offset_unadjusted, sized_align ); let unsized_offset_unadjusted = bx.const_usize(unsized_offset_unadjusted); </ins> let sized_align = bx.const_usize(sized_align); // Recurse to get the size of the dynamically sized field (must be

let field_ty = layout.field(bx, i).ty; let (unsized_size, mut unsized_align) = size_and_align_of_dst(bx, field_ty, info); <del> // FIXME (#26403, #27023): We should be adding padding // to `sized_size` (to accommodate the `unsized_align` // required of the unsized field that follows) before // summing it with `sized_size`. (Note that since #26403 // is unfixed, we do not yet add the necessary padding // here. But this is where the add would go.) // Return the sum of sizes and max of aligns. let size = bx.add(sized_size, unsized_size); // Packed types ignore the alignment of their fields. if let ty::Adt(def, _) = t.kind() { if def.repr().packed() { unsized_align = sized_align; </del> <ins> // # First compute the dynamic alignment // For packed types, we need to cap the alignment. if let ty::Adt(def, _) = t.kind() && let Some(packed) = def.repr().pack { if packed.bytes() == 1 { // We know this will be capped to 1. unsized_align = bx.const_usize(1); } else { // We have to dynamically compute `min(unsized_align, packed)`. let packed = bx.const_usize(packed.bytes()); let cmp = bx.icmp(IntPredicate::IntULT, unsized_align, packed); unsized_align = bx.select(cmp, unsized_align, packed); </ins> } } // Choose max of two known alignments (combined value must // be aligned according to more restrictive of the two). <del> let align = match ( </del> <ins> let full_align = match ( </ins> bx.const_to_opt_u128(sized_align, false), bx.const_to_opt_u128(unsized_align, false), ) {

} }; <ins> // # Then compute the dynamic size // The full formula for the size would be: // let unsized_offset_adjusted = unsized_offset_unadjusted.align_to(unsized_align); // let full_size = (unsized_offset_adjusted + unsized_size).align_to(full_align); // However, `unsized_size` is a multiple of `unsized_align`. // Therefore, we can equivalently do the `align_to(unsized_align)` *after* adding `unsized_size`: // let full_size = (unsized_offset_unadjusted + unsized_size).align_to(unsized_align).align_to(full_align); // Furthermore, `align >= unsized_align`, and therefore we only need to do: // let full_size = (unsized_offset_unadjusted + unsized_size).align_to(full_align); let full_size = bx.add(unsized_offset_unadjusted, unsized_size); </ins> // Issue #27023: must add any necessary padding to `size` // (to make it a multiple of `align`) before returning it. //

// //   `(size + (align-1)) & -align` let one = bx.const_usize(1); <del> let addend = bx.sub(align, one); let add = bx.add(size, addend); let neg = bx.neg(align); let size = bx.and(add, neg); </del> <ins> let addend = bx.sub(full_align, one); let add = bx.add(full_size, addend); let neg = bx.neg(full_align); let full_size = bx.and(add, neg); </ins> <del> (size, align) </del> <ins> (full_size, full_align) </ins> } _ => bug!("size_and_align_of_dst: {t} not supported"), }

assert!(layout.fields.count() > 0); trace!("DST layout: {:?}", layout); <del> let sized_size = layout.fields.offset(layout.fields.count() - 1); </del> <ins> let unsized_offset_unadjusted = layout.fields.offset(layout.fields.count() - 1); </ins> let sized_align = layout.align.abi; <del> trace!( "DST {} statically sized prefix size: {:?} align: {:?}", layout.ty, sized_size, sized_align ); </del> // Recurse to get the size of the dynamically sized field (must be // the last field). Can't have foreign types here, how would we

return Ok(None); }; <del> // FIXME (#26403, #27023): We should be adding padding // to `sized_size` (to accommodate the `unsized_align` // required of the unsized field that follows) before // summing it with `sized_size`. (Note that since #26403 // is unfixed, we do not yet add the necessary padding // here. But this is where the add would go.) // Return the sum of sizes and max of aligns. let size = sized_size + unsized_size; // `Size` addition </del> <ins> // # First compute the dynamic alignment </ins> <del> // Packed types ignore the alignment of their fields. </del> <ins> // Packed type alignment needs to be capped. </ins> if let ty::Adt(def, _) = layout.ty.kind() { <del> if def.repr().packed() { unsized_align = sized_align; </del> <ins> if let Some(packed) = def.repr().pack { unsized_align = unsized_align.min(packed); </ins> } } // Choose max of two known alignments (combined value must // be aligned according to more restrictive of the two). <del> let align = sized_align.max(unsized_align); </del> <ins> let full_align = sized_align.max(unsized_align); // # Then compute the dynamic size </ins> <del> // Issue #27023: must add any necessary padding to `size` // (to make it a multiple of `align`) before returning it. let size = size.align_to(align); </del> <ins> let unsized_offset_adjusted = unsized_offset_unadjusted.align_to(unsized_align); let full_size = (unsized_offset_adjusted + unsized_size).align_to(full_align); // Just for our sanitiy's sake, assert that this is equal to what codegen would compute. assert_eq!( full_size, (unsized_offset_unadjusted + unsized_size).align_to(full_align) ); </ins> // Check if this brought us over the size limit. <del> if size > self.max_size_of_val() { </del> <ins> if full_size > self.max_size_of_val() { </ins> throw_ub!(InvalidMeta(InvalidMetaKind::TooBig)); } <del> Ok(Some((size, align))) </del> <ins> Ok(Some((full_size, full_align))) </ins> } ty::Dynamic(_, _, ty::Dyn) => { let vtable = metadata.unwrap_meta().to_pointer(self)?;

<ins> // run-pass use std::ptr::addr_of; // When the unsized tail is a `dyn Trait`, its alignments is only dynamically known. This means the // packed(2) needs to be applied at runtime: the actual alignment of the field is `min(2, // usual_alignment)`. Here we check that we do this right by comparing size, alignment, and field // offset before and after unsizing. fn main() { #[repr(C, packed(2))] struct Packed<T: ?Sized>(u8, core::mem::ManuallyDrop<T>); let p = Packed(0, core::mem::ManuallyDrop::new(1)); let p: &Packed<usize> = &p; let sized = (core::mem::size_of_val(p), core::mem::align_of_val(p)); let sized_offset = unsafe { addr_of!(p.1).cast::<u8>().offset_from(addr_of!(p.0)) }; let p: &Packed<dyn Send> = p; let un_sized = (core::mem::size_of_val(p), core::mem::align_of_val(p)); let un_sized_offset = unsafe { addr_of!(p.1).cast::<u8>().offset_from(addr_of!(p.0)) }; assert_eq!(sized, un_sized); assert_eq!(sized_offset, un_sized_offset); } </ins>