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