Skip to content

Commit cb8b1d1

Browse files
committed
add naive_layout_of query
1 parent e2a7ba2 commit cb8b1d1

File tree

19 files changed

+294
-49
lines changed

19 files changed

+294
-49
lines changed

compiler/rustc_middle/src/query/erase.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,11 @@ impl EraseType
111111
>()];
112112
}
113113

114+
impl EraseType for Result<ty::layout::TyAndNaiveLayout<'_>, &ty::layout::LayoutError<'_>> {
115+
type Result =
116+
[u8; size_of::<Result<ty::layout::TyAndNaiveLayout<'_>, &ty::layout::LayoutError<'_>>>()];
117+
}
118+
114119
impl EraseType for Result<ty::Const<'_>, mir::interpret::LitToConstError> {
115120
type Result = [u8; size_of::<Result<ty::Const<'static>, mir::interpret::LitToConstError>>()];
116121
}

compiler/rustc_middle/src/query/mod.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1394,6 +1394,17 @@ rustc_queries! {
13941394
desc { "computing layout of `{}`", key.value }
13951395
}
13961396

1397+
/// Computes the naive layout estimate of a type. Note that this implicitly
1398+
/// executes in "reveal all" mode, and will normalize the input type.
1399+
///
1400+
/// Unlike `layout_of`, this doesn't recurse behind reference types.
1401+
query naive_layout_of(
1402+
key: ty::ParamEnvAnd<'tcx, Ty<'tcx>>
1403+
) -> Result<ty::layout::TyAndNaiveLayout<'tcx>, &'tcx ty::layout::LayoutError<'tcx>> {
1404+
depth_limit
1405+
desc { "computing layout (naive) of `{}`", key.value }
1406+
}
1407+
13971408
/// Compute a `FnAbi` suitable for indirect calls, i.e. to `fn` pointers.
13981409
///
13991410
/// NB: this doesn't handle virtual calls - those should use `fn_abi_of_instance`

compiler/rustc_middle/src/ty/layout.rs

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -621,6 +621,61 @@ impl<T, E> MaybeResult<T> for Result<T, E> {
621621

622622
pub type TyAndLayout<'tcx> = rustc_target::abi::TyAndLayout<'tcx, Ty<'tcx>>;
623623

624+
#[derive(Copy, Clone, Debug, HashStable)]
625+
pub struct TyAndNaiveLayout<'tcx> {
626+
pub ty: Ty<'tcx>,
627+
pub layout: NaiveLayout,
628+
}
629+
630+
impl std::ops::Deref for TyAndNaiveLayout<'_> {
631+
type Target = NaiveLayout;
632+
fn deref(&self) -> &Self::Target {
633+
&self.layout
634+
}
635+
}
636+
637+
impl std::ops::DerefMut for TyAndNaiveLayout<'_> {
638+
fn deref_mut(&mut self) -> &mut Self::Target {
639+
&mut self.layout
640+
}
641+
}
642+
643+
/// A naive underestimation of the layout of a type.
644+
#[derive(Copy, Clone, Debug, HashStable)]
645+
pub struct NaiveLayout {
646+
pub min_size: Size,
647+
pub min_align: Align,
648+
}
649+
650+
impl NaiveLayout {
651+
pub const EMPTY: Self = Self { min_size: Size::ZERO, min_align: Align::ONE };
652+
653+
pub fn is_underestimate_of(&self, layout: Layout<'_>) -> bool {
654+
self.min_size <= layout.size() && self.min_align <= layout.align().abi
655+
}
656+
657+
#[must_use]
658+
pub fn pad_to_align(self) -> Self {
659+
Self { min_size: self.min_size.align_to(self.min_align), min_align: self.min_align }
660+
}
661+
662+
#[must_use]
663+
pub fn concat<C: HasDataLayout>(&self, other: &Self, cx: &C) -> Option<Self> {
664+
Some(Self {
665+
min_size: self.min_size.checked_add(other.min_size, cx)?,
666+
min_align: std::cmp::max(self.min_align, other.min_align),
667+
})
668+
}
669+
670+
#[must_use]
671+
pub fn union(&self, other: &Self) -> Self {
672+
Self {
673+
min_size: std::cmp::max(self.min_size, other.min_size),
674+
min_align: std::cmp::max(self.min_align, other.min_align),
675+
}
676+
}
677+
}
678+
624679
/// Trait for contexts that want to be able to compute layouts of types.
625680
/// This automatically gives access to `LayoutOf`, through a blanket `impl`.
626681
pub trait LayoutOfHelpers<'tcx>: HasDataLayout + HasTyCtxt<'tcx> + HasParamEnv<'tcx> {
@@ -673,6 +728,18 @@ pub trait LayoutOf<'tcx>: LayoutOfHelpers<'tcx> {
673728
.map_err(|err| self.handle_layout_err(*err, span, ty)),
674729
)
675730
}
731+
732+
/// Computes the naive layout estimate of a type. Note that this implicitly
733+
/// executes in "reveal all" mode, and will normalize the input type.
734+
///
735+
/// Unlike `layout_of`, this doesn't recurse behind reference types.
736+
#[inline]
737+
fn naive_layout_of(
738+
&self,
739+
ty: Ty<'tcx>,
740+
) -> Result<TyAndNaiveLayout<'tcx>, &'tcx LayoutError<'tcx>> {
741+
self.tcx().naive_layout_of(self.param_env().and(ty))
742+
}
676743
}
677744

678745
impl<'tcx, C: LayoutOfHelpers<'tcx>> LayoutOf<'tcx> for C {}

compiler/rustc_query_system/src/query/job.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,8 @@ impl QueryJobId {
176176
while let Some(id) = current_id {
177177
let info = query_map.get(&id).unwrap();
178178
// FIXME: This string comparison should probably not be done.
179-
if format!("{:?}", info.query.dep_kind) == "layout_of" {
179+
let query_name = format!("{:?}", info.query.dep_kind);
180+
if query_name == "layout_of" || query_name == "naive_layout_of" {
180181
depth += 1;
181182
last_layout = Some((info.clone(), depth));
182183
}

compiler/rustc_ty_utils/src/layout.rs

Lines changed: 178 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ use rustc_index::{IndexSlice, IndexVec};
55
use rustc_middle::mir::{GeneratorLayout, GeneratorSavedLocal};
66
use rustc_middle::query::Providers;
77
use rustc_middle::ty::layout::{
8-
IntegerExt, LayoutCx, LayoutError, LayoutOf, TyAndLayout, MAX_SIMD_LANES,
8+
IntegerExt, LayoutCx, LayoutError, LayoutOf, NaiveLayout, TyAndLayout, TyAndNaiveLayout,
9+
MAX_SIMD_LANES,
910
};
1011
use rustc_middle::ty::{
1112
self, AdtDef, EarlyBinder, GenericArgsRef, ReprOptions, Ty, TyCtxt, TypeVisitableExt,
@@ -24,14 +25,14 @@ use crate::errors::{
2425
use crate::layout_sanity_check::sanity_check_layout;
2526

2627
pub fn provide(providers: &mut Providers) {
27-
*providers = Providers { layout_of, ..*providers };
28+
*providers = Providers { layout_of, naive_layout_of, ..*providers };
2829
}
2930

3031
#[instrument(skip(tcx, query), level = "debug")]
31-
fn layout_of<'tcx>(
32+
fn naive_layout_of<'tcx>(
3233
tcx: TyCtxt<'tcx>,
3334
query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>,
34-
) -> Result<TyAndLayout<'tcx>, &'tcx LayoutError<'tcx>> {
35+
) -> Result<TyAndNaiveLayout<'tcx>, &'tcx LayoutError<'tcx>> {
3536
let (param_env, ty) = query.into_parts();
3637
debug!(?ty);
3738

@@ -53,16 +54,43 @@ fn layout_of<'tcx>(
5354

5455
if ty != unnormalized_ty {
5556
// Ensure this layout is also cached for the normalized type.
56-
return tcx.layout_of(param_env.and(ty));
57+
return tcx.naive_layout_of(param_env.and(ty));
5758
}
5859

5960
let cx = LayoutCx { tcx, param_env };
61+
let layout = naive_layout_of_uncached(&cx, ty)?;
62+
Ok(TyAndNaiveLayout { ty, layout })
63+
}
6064

65+
#[instrument(skip(tcx, query), level = "debug")]
66+
fn layout_of<'tcx>(
67+
tcx: TyCtxt<'tcx>,
68+
query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>,
69+
) -> Result<TyAndLayout<'tcx>, &'tcx LayoutError<'tcx>> {
70+
let (param_env, unnormalized_ty) = query.into_parts();
71+
let param_env = param_env.with_reveal_all_normalized(tcx);
72+
// `naive_layout_of` takes care of normalizing the type.
73+
let naive = tcx.naive_layout_of(query)?;
74+
let ty = naive.ty;
75+
76+
if ty != unnormalized_ty {
77+
// Ensure this layout is also cached for the normalized type.
78+
return tcx.layout_of(param_env.and(ty));
79+
}
80+
81+
let cx = LayoutCx { tcx, param_env };
6182
let layout = layout_of_uncached(&cx, ty)?;
62-
let layout = TyAndLayout { ty, layout };
6383

64-
record_layout_for_printing(&cx, layout);
84+
if !naive.is_underestimate_of(layout) {
85+
bug!(
86+
"the estimated naive layout is bigger than the actual layout:\n{:#?}\n{:#?}",
87+
naive,
88+
layout,
89+
);
90+
}
6591

92+
let layout = TyAndLayout { ty, layout };
93+
record_layout_for_printing(&cx, layout);
6694
sanity_check_layout(&cx, &layout);
6795

6896
Ok(layout)
@@ -75,6 +103,132 @@ fn error<'tcx>(
75103
cx.tcx.arena.alloc(err)
76104
}
77105

106+
fn naive_layout_of_uncached<'tcx>(
107+
cx: &LayoutCx<'tcx, TyCtxt<'tcx>>,
108+
ty: Ty<'tcx>,
109+
) -> Result<NaiveLayout, &'tcx LayoutError<'tcx>> {
110+
let tcx = cx.tcx;
111+
let dl = cx.data_layout();
112+
113+
let scalar =
114+
|value: Primitive| NaiveLayout { min_size: value.size(dl), min_align: value.align(dl).abi };
115+
116+
let univariant = |fields: &mut dyn Iterator<Item = Ty<'tcx>>,
117+
repr: &ReprOptions|
118+
-> Result<NaiveLayout, &'tcx LayoutError<'tcx>> {
119+
// For simplicity, ignore inter-field padding; this may underestimate the size.
120+
// FIXME(reference_niches): Be smarter and implement something closer to the real layout logic.
121+
let mut layout = NaiveLayout::EMPTY;
122+
for field in fields {
123+
let field = cx.naive_layout_of(field)?;
124+
layout = layout
125+
.concat(&field, cx)
126+
.ok_or_else(|| error(cx, LayoutError::SizeOverflow(ty)))?;
127+
}
128+
129+
if let Some(align) = repr.align {
130+
layout.min_align = std::cmp::max(layout.min_align, align);
131+
}
132+
if let Some(pack) = repr.pack {
133+
layout.min_align = std::cmp::min(layout.min_align, pack);
134+
}
135+
136+
Ok(layout.pad_to_align())
137+
};
138+
139+
debug_assert!(!ty.has_non_region_infer());
140+
141+
Ok(match *ty.kind() {
142+
// Basic scalars
143+
ty::Bool => scalar(Int(I8, false)),
144+
ty::Char => scalar(Int(I32, false)),
145+
ty::Int(ity) => scalar(Int(Integer::from_int_ty(dl, ity), true)),
146+
ty::Uint(ity) => scalar(Int(Integer::from_uint_ty(dl, ity), false)),
147+
ty::Float(fty) => scalar(match fty {
148+
ty::FloatTy::F32 => F32,
149+
ty::FloatTy::F64 => F64,
150+
}),
151+
ty::FnPtr(_) => scalar(Pointer(dl.instruction_address_space)),
152+
153+
// The never type.
154+
ty::Never => NaiveLayout::EMPTY,
155+
156+
// Potentially-wide pointers.
157+
ty::Ref(_, _, _) | ty::RawPtr(_) => {
158+
// TODO(reference_niches): handle wide pointers
159+
scalar(Pointer(AddressSpace::DATA))
160+
}
161+
162+
ty::Dynamic(_, _, ty::DynStar) => {
163+
let ptr = scalar(Pointer(AddressSpace::DATA));
164+
ptr.concat(&ptr, cx).ok_or_else(|| error(cx, LayoutError::SizeOverflow(ty)))?
165+
}
166+
167+
// Arrays and slices.
168+
ty::Array(element, _count) => {
169+
let element = cx.naive_layout_of(element)?;
170+
NaiveLayout {
171+
min_size: Size::ZERO, // TODO(reference_niches): proper array size
172+
min_align: element.min_align,
173+
}
174+
}
175+
ty::Slice(element) => {
176+
NaiveLayout { min_size: Size::ZERO, min_align: cx.naive_layout_of(element)?.min_align }
177+
}
178+
ty::Str => NaiveLayout::EMPTY,
179+
180+
// Odd unit types.
181+
ty::FnDef(..) | ty::Dynamic(_, _, ty::Dyn) | ty::Foreign(..) => NaiveLayout::EMPTY,
182+
183+
// FIXME(reference_niches): try to actually compute a reasonable layout estimate,
184+
// without duplicating too much code from `generator_layout`.
185+
ty::Generator(..) => NaiveLayout::EMPTY,
186+
187+
ty::Closure(_, ref substs) => {
188+
univariant(&mut substs.as_closure().upvar_tys(), &ReprOptions::default())?
189+
}
190+
191+
ty::Tuple(tys) => univariant(&mut tys.iter(), &ReprOptions::default())?,
192+
193+
ty::Adt(def, substs) if def.is_union() => {
194+
let repr = def.repr();
195+
let only_variant = &def.variants()[FIRST_VARIANT];
196+
only_variant.fields.iter().try_fold(NaiveLayout::EMPTY, |layout, f| {
197+
let mut fields = std::iter::once(f.ty(tcx, substs));
198+
univariant(&mut fields, &repr).map(|l| layout.union(&l))
199+
})?
200+
}
201+
202+
ty::Adt(def, substs) => {
203+
// For simplicity, assume that any discriminant field (if it exists)
204+
// gets niched inside one of the variants; this will underestimate the size
205+
// (and sometimes alignment) of enums.
206+
// FIXME(reference_niches): Be smarter and actually take into accoount the discriminant.
207+
let repr = def.repr();
208+
def.variants().iter().try_fold(NaiveLayout::EMPTY, |layout, v| {
209+
let mut fields = v.fields.iter().map(|f| f.ty(tcx, substs));
210+
let vlayout = univariant(&mut fields, &repr)?;
211+
Ok(layout.union(&vlayout))
212+
})?
213+
}
214+
215+
// Types with no meaningful known layout.
216+
ty::Alias(..) => {
217+
// NOTE(eddyb) `layout_of` query should've normalized these away,
218+
// if that was possible, so there's no reason to try again here.
219+
return Err(error(cx, LayoutError::Unknown(ty)));
220+
}
221+
222+
ty::Bound(..) | ty::GeneratorWitness(..) | ty::GeneratorWitnessMIR(..) | ty::Infer(_) => {
223+
bug!("Layout::compute: unexpected type `{}`", ty)
224+
}
225+
226+
ty::Placeholder(..) | ty::Param(_) | ty::Error(_) => {
227+
return Err(error(cx, LayoutError::Unknown(ty)));
228+
}
229+
})
230+
}
231+
78232
fn univariant_uninterned<'tcx>(
79233
cx: &LayoutCx<'tcx, TyCtxt<'tcx>>,
80234
ty: Ty<'tcx>,
@@ -146,6 +300,14 @@ fn layout_of_uncached<'tcx>(
146300
ty::Ref(_, pointee, _) | ty::RawPtr(ty::TypeAndMut { ty: pointee, .. }) => {
147301
let mut data_ptr = scalar_unit(Pointer(AddressSpace::DATA));
148302
if !ty.is_unsafe_ptr() {
303+
match cx.naive_layout_of(pointee) {
304+
// TODO(reference_niches): actually use the naive layout to set
305+
// reference niches; the query is still kept to for testing purposes.
306+
Ok(_) => (),
307+
// This can happen when computing the `SizeSkeleton` of a generic type.
308+
Err(LayoutError::Unknown(_)) => (),
309+
Err(err) => return Err(err),
310+
}
149311
data_ptr.valid_range_mut().start = 1;
150312
}
151313

@@ -558,18 +720,15 @@ fn layout_of_uncached<'tcx>(
558720
}
559721

560722
// Types with no meaningful known layout.
561-
ty::Alias(..) => {
562-
// NOTE(eddyb) `layout_of` query should've normalized these away,
563-
// if that was possible, so there's no reason to try again here.
564-
return Err(error(cx, LayoutError::Unknown(ty)));
565-
}
566-
567-
ty::Bound(..) | ty::GeneratorWitness(..) | ty::GeneratorWitnessMIR(..) | ty::Infer(_) => {
568-
bug!("Layout::compute: unexpected type `{}`", ty)
569-
}
570-
571-
ty::Placeholder(..) | ty::Param(_) | ty::Error(_) => {
572-
return Err(error(cx, LayoutError::Unknown(ty)));
723+
ty::Alias(..)
724+
| ty::Bound(..)
725+
| ty::GeneratorWitness(..)
726+
| ty::GeneratorWitnessMIR(..)
727+
| ty::Infer(_)
728+
| ty::Placeholder(..)
729+
| ty::Param(_)
730+
| ty::Error(_) => {
731+
unreachable!("already rejected by `naive_layout_of`");
573732
}
574733
})
575734
}

src/tools/miri/tests/fail/layout_cycle.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//@error-in-other-file: a cycle occurred during layout computation
2-
//~^ ERROR: cycle detected when computing layout of
2+
//~^ ERROR: cycle detected when computing layout (naive) of
33

44
use std::mem;
55

src/tools/miri/tests/fail/layout_cycle.stderr

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
error[E0391]: cycle detected when computing layout of `S<S<()>>`
1+
error[E0391]: cycle detected when computing layout (naive) of `S<S<()>>`
22
|
3-
= note: ...which requires computing layout of `<S<()> as Tr>::I`...
4-
= note: ...which again requires computing layout of `S<S<()>>`, completing the cycle
3+
= note: ...which requires computing layout (naive) of `<S<()> as Tr>::I`...
4+
= note: ...which again requires computing layout (naive) of `S<S<()>>`, completing the cycle
5+
= note: cycle used when computing layout of `S<S<()>>`
56
= note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information
67

78
error: post-monomorphization error: a cycle occurred during layout computation

0 commit comments

Comments
 (0)