Skip to content

Commit 30ae640

Browse files
committed
properly handle arrays and wide pointers in naive_layout_of
1 parent cb8b1d1 commit 30ae640

File tree

2 files changed

+116
-81
lines changed

2 files changed

+116
-81
lines changed

compiler/rustc_ty_utils/src/layout.rs

Lines changed: 115 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -154,9 +154,19 @@ fn naive_layout_of_uncached<'tcx>(
154154
ty::Never => NaiveLayout::EMPTY,
155155

156156
// Potentially-wide pointers.
157-
ty::Ref(_, _, _) | ty::RawPtr(_) => {
158-
// TODO(reference_niches): handle wide pointers
159-
scalar(Pointer(AddressSpace::DATA))
157+
ty::Ref(_, pointee, _) | ty::RawPtr(ty::TypeAndMut { ty: pointee, .. }) => {
158+
let data_ptr = scalar(Pointer(AddressSpace::DATA));
159+
160+
if let Some(metadata) = ptr_metadata_scalar(cx, pointee)? {
161+
// Effectively a (ptr, meta) tuple.
162+
data_ptr
163+
.concat(&scalar(metadata.primitive()), cx)
164+
.ok_or_else(|| error(cx, LayoutError::SizeOverflow(ty)))?
165+
.pad_to_align()
166+
} else {
167+
// No metadata, this is a thin pointer.
168+
data_ptr
169+
}
160170
}
161171

162172
ty::Dynamic(_, _, ty::DynStar) => {
@@ -165,10 +175,15 @@ fn naive_layout_of_uncached<'tcx>(
165175
}
166176

167177
// Arrays and slices.
168-
ty::Array(element, _count) => {
178+
ty::Array(element, count) => {
179+
let count = compute_array_count(cx, count)
180+
.ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?;
169181
let element = cx.naive_layout_of(element)?;
170182
NaiveLayout {
171-
min_size: Size::ZERO, // TODO(reference_niches): proper array size
183+
min_size: element
184+
.min_size
185+
.checked_mul(count, cx)
186+
.ok_or_else(|| error(cx, LayoutError::SizeOverflow(ty)))?,
172187
min_align: element.min_align,
173188
}
174189
}
@@ -311,72 +326,13 @@ fn layout_of_uncached<'tcx>(
311326
data_ptr.valid_range_mut().start = 1;
312327
}
313328

314-
let pointee = tcx.normalize_erasing_regions(param_env, pointee);
315-
if pointee.is_sized(tcx, param_env) {
316-
return Ok(tcx.mk_layout(LayoutS::scalar(cx, data_ptr)));
317-
}
318-
319-
let metadata = if let Some(metadata_def_id) = tcx.lang_items().metadata_type()
320-
// Projection eagerly bails out when the pointee references errors,
321-
// fall back to structurally deducing metadata.
322-
&& !pointee.references_error()
323-
{
324-
let pointee_metadata = Ty::new_projection(tcx,metadata_def_id, [pointee]);
325-
let metadata_ty = match tcx.try_normalize_erasing_regions(
326-
param_env,
327-
pointee_metadata,
328-
) {
329-
Ok(metadata_ty) => metadata_ty,
330-
Err(mut err) => {
331-
// Usually `<Ty as Pointee>::Metadata` can't be normalized because
332-
// its struct tail cannot be normalized either, so try to get a
333-
// more descriptive layout error here, which will lead to less confusing
334-
// diagnostics.
335-
match tcx.try_normalize_erasing_regions(
336-
param_env,
337-
tcx.struct_tail_without_normalization(pointee),
338-
) {
339-
Ok(_) => {},
340-
Err(better_err) => {
341-
err = better_err;
342-
}
343-
}
344-
return Err(error(cx, LayoutError::NormalizationFailure(pointee, err)));
345-
},
346-
};
347-
348-
let metadata_layout = cx.layout_of(metadata_ty)?;
349-
// If the metadata is a 1-zst, then the pointer is thin.
350-
if metadata_layout.is_zst() && metadata_layout.align.abi.bytes() == 1 {
351-
return Ok(tcx.mk_layout(LayoutS::scalar(cx, data_ptr)));
352-
}
353-
354-
let Abi::Scalar(metadata) = metadata_layout.abi else {
355-
return Err(error(cx, LayoutError::Unknown(pointee)));
356-
};
357-
358-
metadata
329+
if let Some(metadata) = ptr_metadata_scalar(cx, pointee)? {
330+
// Effectively a (ptr, meta) tuple.
331+
tcx.mk_layout(cx.scalar_pair(data_ptr, metadata))
359332
} else {
360-
let unsized_part = tcx.struct_tail_erasing_lifetimes(pointee, param_env);
361-
362-
match unsized_part.kind() {
363-
ty::Foreign(..) => {
364-
return Ok(tcx.mk_layout(LayoutS::scalar(cx, data_ptr)));
365-
}
366-
ty::Slice(_) | ty::Str => scalar_unit(Int(dl.ptr_sized_integer(), false)),
367-
ty::Dynamic(..) => {
368-
let mut vtable = scalar_unit(Pointer(AddressSpace::DATA));
369-
vtable.valid_range_mut().start = 1;
370-
vtable
371-
}
372-
_ => {
373-
return Err(error(cx, LayoutError::Unknown(pointee)));
374-
}
375-
}
376-
};
377-
378-
// Effectively a (ptr, meta) tuple.
379-
tcx.mk_layout(cx.scalar_pair(data_ptr, metadata))
333+
// No metadata, this is a thin pointer.
334+
tcx.mk_layout(LayoutS::scalar(cx, data_ptr))
335+
}
380336
}
381337

382338
ty::Dynamic(_, _, ty::DynStar) => {
@@ -388,16 +344,8 @@ fn layout_of_uncached<'tcx>(
388344
}
389345

390346
// Arrays and slices.
391-
ty::Array(element, mut count) => {
392-
if count.has_projections() {
393-
count = tcx.normalize_erasing_regions(param_env, count);
394-
if count.has_projections() {
395-
return Err(error(cx, LayoutError::Unknown(ty)));
396-
}
397-
}
398-
399-
let count = count
400-
.try_eval_target_usize(tcx, param_env)
347+
ty::Array(element, count) => {
348+
let count = compute_array_count(cx, count)
401349
.ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?;
402350
let element = cx.layout_of(element)?;
403351
let size = element
@@ -733,6 +681,93 @@ fn layout_of_uncached<'tcx>(
733681
})
734682
}
735683

684+
fn compute_array_count<'tcx>(
685+
cx: &LayoutCx<'tcx, TyCtxt<'tcx>>,
686+
mut count: ty::Const<'tcx>,
687+
) -> Option<u64> {
688+
let LayoutCx { tcx, param_env } = *cx;
689+
if count.has_projections() {
690+
count = tcx.normalize_erasing_regions(param_env, count);
691+
if count.has_projections() {
692+
return None;
693+
}
694+
}
695+
696+
count.try_eval_target_usize(tcx, param_env)
697+
}
698+
699+
fn ptr_metadata_scalar<'tcx>(
700+
cx: &LayoutCx<'tcx, TyCtxt<'tcx>>,
701+
pointee: Ty<'tcx>,
702+
) -> Result<Option<Scalar>, &'tcx LayoutError<'tcx>> {
703+
let dl = cx.data_layout();
704+
let scalar_unit = |value: Primitive| {
705+
let size = value.size(dl);
706+
assert!(size.bits() <= 128);
707+
Scalar::Initialized { value, valid_range: WrappingRange::full(size) }
708+
};
709+
710+
let LayoutCx { tcx, param_env } = *cx;
711+
712+
let pointee = tcx.normalize_erasing_regions(param_env, pointee);
713+
if pointee.is_sized(tcx, param_env) {
714+
return Ok(None);
715+
}
716+
717+
if let Some(metadata_def_id) = tcx.lang_items().metadata_type()
718+
// Projection eagerly bails out when the pointee references errors,
719+
// fall back to structurally deducing metadata.
720+
&& !pointee.references_error()
721+
{
722+
let pointee_metadata = Ty::new_projection(tcx,metadata_def_id, [pointee]);
723+
let metadata_ty = match tcx.try_normalize_erasing_regions(
724+
param_env,
725+
pointee_metadata,
726+
) {
727+
Ok(metadata_ty) => metadata_ty,
728+
Err(mut err) => {
729+
// Usually `<Ty as Pointee>::Metadata` can't be normalized because
730+
// its struct tail cannot be normalized either, so try to get a
731+
// more descriptive layout error here, which will lead to less confusing
732+
// diagnostics.
733+
match tcx.try_normalize_erasing_regions(
734+
param_env,
735+
tcx.struct_tail_without_normalization(pointee),
736+
) {
737+
Ok(_) => {},
738+
Err(better_err) => {
739+
err = better_err;
740+
}
741+
}
742+
return Err(error(cx, LayoutError::NormalizationFailure(pointee, err)));
743+
},
744+
};
745+
746+
let metadata_layout = cx.layout_of(metadata_ty)?;
747+
748+
if metadata_layout.is_zst() && metadata_layout.align.abi.bytes() == 1 {
749+
Ok(None) // If the metadata is a 1-zst, then the pointer is thin.
750+
} else if let Abi::Scalar(metadata) = metadata_layout.abi {
751+
Ok(Some(metadata))
752+
} else {
753+
Err(error(cx, LayoutError::Unknown(pointee)))
754+
}
755+
} else {
756+
let unsized_part = tcx.struct_tail_erasing_lifetimes(pointee, param_env);
757+
758+
match unsized_part.kind() {
759+
ty::Foreign(..) => Ok(None),
760+
ty::Slice(_) | ty::Str => Ok(Some(scalar_unit(Int(dl.ptr_sized_integer(), false)))),
761+
ty::Dynamic(..) => {
762+
let mut vtable = scalar_unit(Pointer(AddressSpace::DATA));
763+
vtable.valid_range_mut().start = 1;
764+
Ok(Some(vtable))
765+
}
766+
_ => Err(error(cx, LayoutError::Unknown(pointee))),
767+
}
768+
}
769+
}
770+
736771
/// Overlap eligibility and variant assignment for each GeneratorSavedLocal.
737772
#[derive(Clone, Debug, PartialEq)]
738773
enum SavedLocalEligibility {

tests/ui/layout/cannot-transmute-unnormalizable-type.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ LL | std::mem::transmute::<Option<()>, Option<&Other>>(None);
1111
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1212
|
1313
= note: source type: `Option<()>` (8 bits)
14-
= note: target type: `Option<&Other>` (unable to determine layout for `<() as Trait>::RefTarget` because `<() as Trait>::RefTarget` cannot be normalized)
14+
= note: target type: `Option<&Other>` (unable to determine layout for `Other` because `<() as Trait>::RefTarget` cannot be normalized)
1515

1616
error: aborting due to 2 previous errors
1717

0 commit comments

Comments
 (0)