Skip to content

Commit c51dd07

Browse files
committed
fix comments in dynamic size/align computation logic
1 parent e281163 commit c51dd07

File tree

2 files changed

+50
-44
lines changed

2 files changed

+50
-44
lines changed

compiler/rustc_codegen_ssa/src/glue.rs

+38-19
Original file line numberDiff line numberDiff line change
@@ -59,37 +59,41 @@ pub fn size_and_align_of_dst<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
5959
debug!("DST {} layout: {:?}", t, layout);
6060

6161
let i = layout.fields.count() - 1;
62-
let sized_size = layout.fields.offset(i).bytes();
62+
let unsized_offset_unadjusted = layout.fields.offset(i).bytes();
6363
let sized_align = layout.align.abi.bytes();
64-
debug!("DST {} statically sized prefix size: {} align: {}", t, sized_size, sized_align);
65-
let sized_size = bx.const_usize(sized_size);
64+
debug!(
65+
"DST {} offset of dyn field: {}, statically sized align: {}",
66+
t, unsized_offset_unadjusted, sized_align
67+
);
68+
let unsized_offset_unadjusted = bx.const_usize(unsized_offset_unadjusted);
6669
let sized_align = bx.const_usize(sized_align);
6770

6871
// Recurse to get the size of the dynamically sized field (must be
6972
// the last field).
7073
let field_ty = layout.field(bx, i).ty;
7174
let (unsized_size, mut unsized_align) = size_and_align_of_dst(bx, field_ty, info);
7275

73-
// FIXME (#26403, #27023): We should be adding padding
74-
// to `sized_size` (to accommodate the `unsized_align`
75-
// required of the unsized field that follows) before
76-
// summing it with `sized_size`. (Note that since #26403
77-
// is unfixed, we do not yet add the necessary padding
78-
// here. But this is where the add would go.)
76+
// # First compute the dynamic alignment
7977

80-
// Return the sum of sizes and max of aligns.
81-
let size = bx.add(sized_size, unsized_size);
82-
83-
// Packed types ignore the alignment of their fields.
78+
// Packed type alignment would have to be capped, but their tails always have known alignment.
79+
// Therefore, their alignment has already been taken into account when computing `sized_align`
80+
// and `unsized_offset_unadjusted`, so no further adjustment is needed.
8481
if let ty::Adt(def, _) = t.kind() {
8582
if def.repr().packed() {
83+
let unsized_tail =
84+
bx.tcx().struct_tail_with_normalize(field_ty, |ty| ty, || {});
85+
assert!(matches!(unsized_tail.kind(), ty::Slice(..) | ty::Str));
86+
// Therefore we do not need to adjust anything.
87+
// It's not actually correct to say that the unsized field has this alignment, but the
88+
// code below works correctly if we set this and it avoids having to compute
89+
// the actual alignment (which is `unsized_align.min(packed)`).
8690
unsized_align = sized_align;
8791
}
8892
}
8993

9094
// Choose max of two known alignments (combined value must
9195
// be aligned according to more restrictive of the two).
92-
let align = match (
96+
let full_align = match (
9397
bx.const_to_opt_u128(sized_align, false),
9498
bx.const_to_opt_u128(unsized_align, false),
9599
) {
@@ -104,6 +108,21 @@ pub fn size_and_align_of_dst<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
104108
}
105109
};
106110

111+
// # Then compute the dynamic size
112+
113+
// The full formula for the size would be:
114+
// let unsized_offset_adjusted = unsized_offset_unadjusted.align_to(unsized_align);
115+
// let full_size = (unsized_offset_adjusted + unsized_size).align_to(full_align);
116+
// However, `unsized_size` is a multiple of `unsized_align`.
117+
// Therefore, we can equivalently do the `align_to(unsized_align)` *after* adding `unsized_size`:
118+
// let full_size = (offset + unsized_size).align_to(unsized_align).align_to(full_align);
119+
// Furthermore, `align >= unsized_align`, and therefore we only need to do:
120+
// let full_size = (offset + unsized_size).align_to(full_align);
121+
// This formula also has the advantage of working correctly when the type is packed
122+
// and we set `unsized_align = sized_align`.
123+
124+
let full_size = bx.add(unsized_offset_unadjusted, unsized_size);
125+
107126
// Issue #27023: must add any necessary padding to `size`
108127
// (to make it a multiple of `align`) before returning it.
109128
//
@@ -115,12 +134,12 @@ pub fn size_and_align_of_dst<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
115134
//
116135
// `(size + (align-1)) & -align`
117136
let one = bx.const_usize(1);
118-
let addend = bx.sub(align, one);
119-
let add = bx.add(size, addend);
120-
let neg = bx.neg(align);
121-
let size = bx.and(add, neg);
137+
let addend = bx.sub(full_align, one);
138+
let add = bx.add(full_size, addend);
139+
let neg = bx.neg(full_align);
140+
let full_size = bx.and(add, neg);
122141

123-
(size, align)
142+
(full_size, full_align)
124143
}
125144
}
126145
}

compiler/rustc_const_eval/src/interpret/eval_context.rs

+12-25
Original file line numberDiff line numberDiff line change
@@ -684,14 +684,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
684684
assert!(layout.fields.count() > 0);
685685
trace!("DST layout: {:?}", layout);
686686

687-
let sized_size = layout.fields.offset(layout.fields.count() - 1);
687+
let unsized_offset_unadjusted = layout.fields.offset(layout.fields.count() - 1);
688688
let sized_align = layout.align.abi;
689-
trace!(
690-
"DST {} statically sized prefix size: {:?} align: {:?}",
691-
layout.ty,
692-
sized_size,
693-
sized_align
694-
);
695689

696690
// Recurse to get the size of the dynamically sized field (must be
697691
// the last field). Can't have foreign types here, how would we
@@ -705,36 +699,29 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
705699
return Ok(None);
706700
};
707701

708-
// FIXME (#26403, #27023): We should be adding padding
709-
// to `sized_size` (to accommodate the `unsized_align`
710-
// required of the unsized field that follows) before
711-
// summing it with `sized_size`. (Note that since #26403
712-
// is unfixed, we do not yet add the necessary padding
713-
// here. But this is where the add would go.)
714-
715-
// Return the sum of sizes and max of aligns.
716-
let size = sized_size + unsized_size; // `Size` addition
702+
// # First compute the dynamic alignment
717703

718-
// Packed types ignore the alignment of their fields.
704+
// Packed type alignment needs to be capped.
719705
if let ty::Adt(def, _) = layout.ty.kind() {
720-
if def.repr().packed() {
721-
unsized_align = sized_align;
706+
if let Some(packed) = def.repr().pack {
707+
unsized_align = unsized_align.min(packed);
722708
}
723709
}
724710

725711
// Choose max of two known alignments (combined value must
726712
// be aligned according to more restrictive of the two).
727-
let align = sized_align.max(unsized_align);
713+
let full_align = sized_align.max(unsized_align);
728714

729-
// Issue #27023: must add any necessary padding to `size`
730-
// (to make it a multiple of `align`) before returning it.
731-
let size = size.align_to(align);
715+
// # Then compute the dynamic size
716+
717+
let unsized_offset_adjusted = unsized_offset_unadjusted.align_to(unsized_align);
718+
let full_size = (unsized_offset_adjusted + unsized_size).align_to(full_align);
732719

733720
// Check if this brought us over the size limit.
734-
if size > self.max_size_of_val() {
721+
if full_size > self.max_size_of_val() {
735722
throw_ub!(InvalidMeta(InvalidMetaKind::TooBig));
736723
}
737-
Ok(Some((size, align)))
724+
Ok(Some((full_size, full_align)))
738725
}
739726
ty::Dynamic(_, _, ty::Dyn) => {
740727
let vtable = metadata.unwrap_meta().to_pointer(self)?;

0 commit comments

Comments
 (0)