• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use super::{Byte, Def, Ref};
2 use std::ops::ControlFlow;
3 
4 #[cfg(test)]
5 mod tests;
6 
7 /// A tree-based representation of a type layout.
8 ///
9 /// Invariants:
10 /// 1. All paths through the layout have the same length (in bytes).
11 ///
12 /// Nice-to-haves:
13 /// 1. An `Alt` is never directly nested beneath another `Alt`.
14 /// 2. A `Seq` is never directly nested beneath another `Seq`.
15 /// 3. `Seq`s and `Alt`s with a single member do not exist.
16 #[derive(Clone, Debug, Hash, PartialEq, Eq)]
17 pub(crate) enum Tree<D, R>
18 where
19     D: Def,
20     R: Ref,
21 {
22     /// A sequence of successive layouts.
23     Seq(Vec<Self>),
24     /// A choice between alternative layouts.
25     Alt(Vec<Self>),
26     /// A definition node.
27     Def(D),
28     /// A reference node.
29     Ref(R),
30     /// A byte node.
31     Byte(Byte),
32 }
33 
34 impl<D, R> Tree<D, R>
35 where
36     D: Def,
37     R: Ref,
38 {
39     /// A `Tree` consisting only of a definition node.
def(def: D) -> Self40     pub(crate) fn def(def: D) -> Self {
41         Self::Def(def)
42     }
43 
44     /// A `Tree` representing an uninhabited type.
uninhabited() -> Self45     pub(crate) fn uninhabited() -> Self {
46         Self::Alt(vec![])
47     }
48 
49     /// A `Tree` representing a zero-sized type.
unit() -> Self50     pub(crate) fn unit() -> Self {
51         Self::Seq(Vec::new())
52     }
53 
54     /// A `Tree` containing a single, uninitialized byte.
uninit() -> Self55     pub(crate) fn uninit() -> Self {
56         Self::Byte(Byte::Uninit)
57     }
58 
59     /// A `Tree` representing the layout of `bool`.
bool() -> Self60     pub(crate) fn bool() -> Self {
61         Self::from_bits(0x00).or(Self::from_bits(0x01))
62     }
63 
64     /// A `Tree` whose layout matches that of a `u8`.
u8() -> Self65     pub(crate) fn u8() -> Self {
66         Self::Alt((0u8..=255).map(Self::from_bits).collect())
67     }
68 
69     /// A `Tree` whose layout accepts exactly the given bit pattern.
from_bits(bits: u8) -> Self70     pub(crate) fn from_bits(bits: u8) -> Self {
71         Self::Byte(Byte::Init(bits))
72     }
73 
74     /// A `Tree` whose layout is a number of the given width.
number(width_in_bytes: usize) -> Self75     pub(crate) fn number(width_in_bytes: usize) -> Self {
76         Self::Seq(vec![Self::u8(); width_in_bytes])
77     }
78 
79     /// A `Tree` whose layout is entirely padding of the given width.
padding(width_in_bytes: usize) -> Self80     pub(crate) fn padding(width_in_bytes: usize) -> Self {
81         Self::Seq(vec![Self::uninit(); width_in_bytes])
82     }
83 
84     /// Remove all `Def` nodes, and all branches of the layout for which `f` produces false.
prune<F>(self, f: &F) -> Tree<!, R> where F: Fn(D) -> bool,85     pub(crate) fn prune<F>(self, f: &F) -> Tree<!, R>
86     where
87         F: Fn(D) -> bool,
88     {
89         match self {
90             Self::Seq(elts) => match elts.into_iter().map(|elt| elt.prune(f)).try_fold(
91                 Tree::unit(),
92                 |elts, elt| {
93                     if elt == Tree::uninhabited() {
94                         ControlFlow::Break(Tree::uninhabited())
95                     } else {
96                         ControlFlow::Continue(elts.then(elt))
97                     }
98                 },
99             ) {
100                 ControlFlow::Break(node) | ControlFlow::Continue(node) => node,
101             },
102             Self::Alt(alts) => alts
103                 .into_iter()
104                 .map(|alt| alt.prune(f))
105                 .fold(Tree::uninhabited(), |alts, alt| alts.or(alt)),
106             Self::Byte(b) => Tree::Byte(b),
107             Self::Ref(r) => Tree::Ref(r),
108             Self::Def(d) => {
109                 if !f(d) {
110                     Tree::uninhabited()
111                 } else {
112                     Tree::unit()
113                 }
114             }
115         }
116     }
117 
118     /// Produces `true` if `Tree` is an inhabited type; otherwise false.
is_inhabited(&self) -> bool119     pub(crate) fn is_inhabited(&self) -> bool {
120         match self {
121             Self::Seq(elts) => elts.into_iter().all(|elt| elt.is_inhabited()),
122             Self::Alt(alts) => alts.into_iter().any(|alt| alt.is_inhabited()),
123             Self::Byte(..) | Self::Ref(..) | Self::Def(..) => true,
124         }
125     }
126 }
127 
128 impl<D, R> Tree<D, R>
129 where
130     D: Def,
131     R: Ref,
132 {
133     /// Produces a new `Tree` where `other` is sequenced after `self`.
then(self, other: Self) -> Self134     pub(crate) fn then(self, other: Self) -> Self {
135         match (self, other) {
136             (Self::Seq(elts), other) | (other, Self::Seq(elts)) if elts.len() == 0 => other,
137             (Self::Seq(mut lhs), Self::Seq(mut rhs)) => {
138                 lhs.append(&mut rhs);
139                 Self::Seq(lhs)
140             }
141             (Self::Seq(mut lhs), rhs) => {
142                 lhs.push(rhs);
143                 Self::Seq(lhs)
144             }
145             (lhs, Self::Seq(mut rhs)) => {
146                 rhs.insert(0, lhs);
147                 Self::Seq(rhs)
148             }
149             (lhs, rhs) => Self::Seq(vec![lhs, rhs]),
150         }
151     }
152 
153     /// Produces a new `Tree` accepting either `self` or `other` as alternative layouts.
or(self, other: Self) -> Self154     pub(crate) fn or(self, other: Self) -> Self {
155         match (self, other) {
156             (Self::Alt(alts), other) | (other, Self::Alt(alts)) if alts.len() == 0 => other,
157             (Self::Alt(mut lhs), Self::Alt(rhs)) => {
158                 lhs.extend(rhs);
159                 Self::Alt(lhs)
160             }
161             (Self::Alt(mut alts), alt) | (alt, Self::Alt(mut alts)) => {
162                 alts.push(alt);
163                 Self::Alt(alts)
164             }
165             (lhs, rhs) => Self::Alt(vec![lhs, rhs]),
166         }
167     }
168 }
169 
170 #[cfg(feature = "rustc")]
171 pub(crate) mod rustc {
172     use super::Tree;
173     use crate::layout::rustc::{Def, Ref};
174 
175     use rustc_middle::ty::layout::LayoutError;
176     use rustc_middle::ty::util::Discr;
177     use rustc_middle::ty::AdtDef;
178     use rustc_middle::ty::ParamEnv;
179     use rustc_middle::ty::SubstsRef;
180     use rustc_middle::ty::VariantDef;
181     use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
182     use rustc_span::ErrorGuaranteed;
183     use rustc_target::abi::Align;
184     use std::alloc;
185 
186     #[derive(Debug, Copy, Clone)]
187     pub(crate) enum Err {
188         /// The layout of the type is unspecified.
189         Unspecified,
190         /// This error will be surfaced elsewhere by rustc, so don't surface it.
191         UnknownLayout,
192         TypeError(ErrorGuaranteed),
193     }
194 
195     impl<'tcx> From<&LayoutError<'tcx>> for Err {
from(err: &LayoutError<'tcx>) -> Self196         fn from(err: &LayoutError<'tcx>) -> Self {
197             match err {
198                 LayoutError::Unknown(..) => Self::UnknownLayout,
199                 err => unimplemented!("{:?}", err),
200             }
201         }
202     }
203 
204     trait LayoutExt {
clamp_align(&self, min_align: Align, max_align: Align) -> Self205         fn clamp_align(&self, min_align: Align, max_align: Align) -> Self;
206     }
207 
208     impl LayoutExt for alloc::Layout {
clamp_align(&self, min_align: Align, max_align: Align) -> Self209         fn clamp_align(&self, min_align: Align, max_align: Align) -> Self {
210             let min_align = min_align.bytes().try_into().unwrap();
211             let max_align = max_align.bytes().try_into().unwrap();
212             Self::from_size_align(self.size(), self.align().clamp(min_align, max_align)).unwrap()
213         }
214     }
215 
216     struct LayoutSummary {
217         total_align: Align,
218         total_size: usize,
219         discriminant_size: usize,
220         discriminant_align: Align,
221     }
222 
223     impl LayoutSummary {
from_ty<'tcx>(ty: Ty<'tcx>, ctx: TyCtxt<'tcx>) -> Result<Self, &'tcx LayoutError<'tcx>>224         fn from_ty<'tcx>(ty: Ty<'tcx>, ctx: TyCtxt<'tcx>) -> Result<Self, &'tcx LayoutError<'tcx>> {
225             use rustc_middle::ty::ParamEnvAnd;
226             use rustc_target::abi::{TyAndLayout, Variants};
227 
228             let param_env = ParamEnv::reveal_all();
229             let param_env_and_type = ParamEnvAnd { param_env, value: ty };
230             let TyAndLayout { layout, .. } = ctx.layout_of(param_env_and_type)?;
231 
232             let total_size: usize = layout.size().bytes_usize();
233             let total_align: Align = layout.align().abi;
234             let discriminant_align: Align;
235             let discriminant_size: usize;
236 
237             if let Variants::Multiple { tag, .. } = layout.variants() {
238                 discriminant_align = tag.align(&ctx).abi;
239                 discriminant_size = tag.size(&ctx).bytes_usize();
240             } else {
241                 discriminant_align = Align::ONE;
242                 discriminant_size = 0;
243             };
244 
245             Ok(Self { total_align, total_size, discriminant_align, discriminant_size })
246         }
247 
into(&self) -> alloc::Layout248         fn into(&self) -> alloc::Layout {
249             alloc::Layout::from_size_align(
250                 self.total_size,
251                 self.total_align.bytes().try_into().unwrap(),
252             )
253             .unwrap()
254         }
255     }
256 
257     impl<'tcx> Tree<Def<'tcx>, Ref<'tcx>> {
from_ty(ty: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> Result<Self, Err>258         pub fn from_ty(ty: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> Result<Self, Err> {
259             use rustc_middle::ty::FloatTy::*;
260             use rustc_middle::ty::IntTy::*;
261             use rustc_middle::ty::UintTy::*;
262             use rustc_target::abi::HasDataLayout;
263 
264             if let Err(e) = ty.error_reported() {
265                 return Err(Err::TypeError(e));
266             }
267 
268             let target = tcx.data_layout();
269 
270             match ty.kind() {
271                 ty::Bool => Ok(Self::bool()),
272 
273                 ty::Int(I8) | ty::Uint(U8) => Ok(Self::u8()),
274                 ty::Int(I16) | ty::Uint(U16) => Ok(Self::number(2)),
275                 ty::Int(I32) | ty::Uint(U32) | ty::Float(F32) => Ok(Self::number(4)),
276                 ty::Int(I64) | ty::Uint(U64) | ty::Float(F64) => Ok(Self::number(8)),
277                 ty::Int(I128) | ty::Uint(U128) => Ok(Self::number(16)),
278                 ty::Int(Isize) | ty::Uint(Usize) => {
279                     Ok(Self::number(target.pointer_size.bytes_usize()))
280                 }
281 
282                 ty::Tuple(members) => {
283                     if members.len() == 0 {
284                         Ok(Tree::unit())
285                     } else {
286                         Err(Err::Unspecified)
287                     }
288                 }
289 
290                 ty::Array(ty, len) => {
291                     let len = len
292                         .try_eval_target_usize(tcx, ParamEnv::reveal_all())
293                         .ok_or(Err::Unspecified)?;
294                     let elt = Tree::from_ty(*ty, tcx)?;
295                     Ok(std::iter::repeat(elt)
296                         .take(len as usize)
297                         .fold(Tree::unit(), |tree, elt| tree.then(elt)))
298                 }
299 
300                 ty::Adt(adt_def, substs_ref) => {
301                     use rustc_middle::ty::AdtKind;
302 
303                     // If the layout is ill-specified, halt.
304                     if !(adt_def.repr().c() || adt_def.repr().int.is_some()) {
305                         return Err(Err::Unspecified);
306                     }
307 
308                     // Compute a summary of the type's layout.
309                     let layout_summary = LayoutSummary::from_ty(ty, tcx)?;
310 
311                     // The layout begins with this adt's visibility.
312                     let vis = Self::def(Def::Adt(*adt_def));
313 
314                     // And is followed the layout(s) of its variants
315                     Ok(vis.then(match adt_def.adt_kind() {
316                         AdtKind::Struct => Self::from_repr_c_variant(
317                             ty,
318                             *adt_def,
319                             substs_ref,
320                             &layout_summary,
321                             None,
322                             adt_def.non_enum_variant(),
323                             tcx,
324                         )?,
325                         AdtKind::Enum => {
326                             trace!(?adt_def, "treeifying enum");
327                             let mut tree = Tree::uninhabited();
328 
329                             for (idx, discr) in adt_def.discriminants(tcx) {
330                                 tree = tree.or(Self::from_repr_c_variant(
331                                     ty,
332                                     *adt_def,
333                                     substs_ref,
334                                     &layout_summary,
335                                     Some(discr),
336                                     adt_def.variant(idx),
337                                     tcx,
338                                 )?);
339                             }
340 
341                             tree
342                         }
343                         AdtKind::Union => {
344                             // is the layout well-defined?
345                             if !adt_def.repr().c() {
346                                 return Err(Err::Unspecified);
347                             }
348 
349                             let ty_layout = layout_of(tcx, ty)?;
350 
351                             let mut tree = Tree::uninhabited();
352 
353                             for field in adt_def.all_fields() {
354                                 let variant_ty = field.ty(tcx, substs_ref);
355                                 let variant_layout = layout_of(tcx, variant_ty)?;
356                                 let padding_needed = ty_layout.size() - variant_layout.size();
357                                 let variant = Self::def(Def::Field(field))
358                                     .then(Self::from_ty(variant_ty, tcx)?)
359                                     .then(Self::padding(padding_needed));
360 
361                                 tree = tree.or(variant);
362                             }
363 
364                             tree
365                         }
366                     }))
367                 }
368 
369                 ty::Ref(lifetime, ty, mutability) => {
370                     let align = layout_of(tcx, *ty)?.align();
371                     Ok(Tree::Ref(Ref {
372                         lifetime: *lifetime,
373                         ty: *ty,
374                         mutability: *mutability,
375                         align,
376                     }))
377                 }
378 
379                 _ => Err(Err::Unspecified),
380             }
381         }
382 
from_repr_c_variant( ty: Ty<'tcx>, adt_def: AdtDef<'tcx>, substs_ref: SubstsRef<'tcx>, layout_summary: &LayoutSummary, discr: Option<Discr<'tcx>>, variant_def: &'tcx VariantDef, tcx: TyCtxt<'tcx>, ) -> Result<Self, Err>383         fn from_repr_c_variant(
384             ty: Ty<'tcx>,
385             adt_def: AdtDef<'tcx>,
386             substs_ref: SubstsRef<'tcx>,
387             layout_summary: &LayoutSummary,
388             discr: Option<Discr<'tcx>>,
389             variant_def: &'tcx VariantDef,
390             tcx: TyCtxt<'tcx>,
391         ) -> Result<Self, Err> {
392             let mut tree = Tree::unit();
393 
394             let repr = adt_def.repr();
395             let min_align = repr.align.unwrap_or(Align::ONE);
396             let max_align = repr.pack.unwrap_or(Align::MAX);
397 
398             let clamp =
399                 |align: Align| align.clamp(min_align, max_align).bytes().try_into().unwrap();
400 
401             let variant_span = trace_span!(
402                 "treeifying variant",
403                 min_align = ?min_align,
404                 max_align = ?max_align,
405             )
406             .entered();
407 
408             let mut variant_layout = alloc::Layout::from_size_align(
409                 0,
410                 layout_summary.total_align.bytes().try_into().unwrap(),
411             )
412             .unwrap();
413 
414             // The layout of the variant is prefixed by the discriminant, if any.
415             if let Some(discr) = discr {
416                 trace!(?discr, "treeifying discriminant");
417                 let discr_layout = alloc::Layout::from_size_align(
418                     layout_summary.discriminant_size,
419                     clamp(layout_summary.discriminant_align),
420                 )
421                 .unwrap();
422                 trace!(?discr_layout, "computed discriminant layout");
423                 variant_layout = variant_layout.extend(discr_layout).unwrap().0;
424                 tree = tree.then(Self::from_discr(discr, tcx, layout_summary.discriminant_size));
425             }
426 
427             // Next come fields.
428             let fields_span = trace_span!("treeifying fields").entered();
429             for field_def in variant_def.fields.iter() {
430                 let field_ty = field_def.ty(tcx, substs_ref);
431                 let _span = trace_span!("treeifying field", field = ?field_ty).entered();
432 
433                 // begin with the field's visibility
434                 tree = tree.then(Self::def(Def::Field(field_def)));
435 
436                 // compute the field's layout characteristics
437                 let field_layout = layout_of(tcx, field_ty)?.clamp_align(min_align, max_align);
438 
439                 // next comes the field's padding
440                 let padding_needed = variant_layout.padding_needed_for(field_layout.align());
441                 if padding_needed > 0 {
442                     tree = tree.then(Self::padding(padding_needed));
443                 }
444 
445                 // finally, the field's layout
446                 tree = tree.then(Self::from_ty(field_ty, tcx)?);
447 
448                 // extend the variant layout with the field layout
449                 variant_layout = variant_layout.extend(field_layout).unwrap().0;
450             }
451             drop(fields_span);
452 
453             // finally: padding
454             let padding_span = trace_span!("adding trailing padding").entered();
455             if layout_summary.total_size > variant_layout.size() {
456                 let padding_needed = layout_summary.total_size - variant_layout.size();
457                 tree = tree.then(Self::padding(padding_needed));
458             };
459             drop(padding_span);
460             drop(variant_span);
461             Ok(tree)
462         }
463 
from_discr(discr: Discr<'tcx>, tcx: TyCtxt<'tcx>, size: usize) -> Self464         pub fn from_discr(discr: Discr<'tcx>, tcx: TyCtxt<'tcx>, size: usize) -> Self {
465             use rustc_target::abi::Endian;
466 
467             let bytes: [u8; 16];
468             let bytes = match tcx.data_layout.endian {
469                 Endian::Little => {
470                     bytes = discr.val.to_le_bytes();
471                     &bytes[..size]
472                 }
473                 Endian::Big => {
474                     bytes = discr.val.to_be_bytes();
475                     &bytes[bytes.len() - size..]
476                 }
477             };
478             Self::Seq(bytes.iter().map(|&b| Self::from_bits(b)).collect())
479         }
480     }
481 
layout_of<'tcx>( ctx: TyCtxt<'tcx>, ty: Ty<'tcx>, ) -> Result<alloc::Layout, &'tcx LayoutError<'tcx>>482     fn layout_of<'tcx>(
483         ctx: TyCtxt<'tcx>,
484         ty: Ty<'tcx>,
485     ) -> Result<alloc::Layout, &'tcx LayoutError<'tcx>> {
486         use rustc_middle::ty::ParamEnvAnd;
487         use rustc_target::abi::TyAndLayout;
488 
489         let param_env = ParamEnv::reveal_all();
490         let param_env_and_type = ParamEnvAnd { param_env, value: ty };
491         let TyAndLayout { layout, .. } = ctx.layout_of(param_env_and_type)?;
492         let layout = alloc::Layout::from_size_align(
493             layout.size().bytes_usize(),
494             layout.align().abi.bytes().try_into().unwrap(),
495         )
496         .unwrap();
497         trace!(?ty, ?layout, "computed layout for type");
498         Ok(layout)
499     }
500 }
501