diff options
Diffstat (limited to 'compiler/rustc_middle/src/ty/layout.rs')
-rw-r--r-- | compiler/rustc_middle/src/ty/layout.rs | 115 |
1 files changed, 108 insertions, 7 deletions
diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 254ffc33c..195d951f9 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -5,7 +5,7 @@ use crate::ty::{self, ReprOptions, Ty, TyCtxt, TypeVisitableExt}; use rustc_errors::{DiagnosticBuilder, Handler, IntoDiagnostic}; use rustc_hir as hir; use rustc_hir::def_id::DefId; -use rustc_index::vec::Idx; +use rustc_index::vec::IndexVec; use rustc_session::config::OptLevel; use rustc_span::symbol::{sym, Symbol}; use rustc_span::{Span, DUMMY_SP}; @@ -281,6 +281,12 @@ pub enum SizeSkeleton<'tcx> { /// Any statically computable Layout. Known(Size), + /// This is a generic const expression (i.e. N * 2), which may contain some parameters. + /// It must be of type usize, and represents the size of a type in bytes. + /// It is not required to be evaluatable to a concrete value, but can be used to check + /// that another SizeSkeleton is of equal size. + Generic(ty::Const<'tcx>), + /// A potentially-fat pointer. Pointer { /// If true, this pointer is never null. @@ -326,6 +332,37 @@ impl<'tcx> SizeSkeleton<'tcx> { ), } } + ty::Array(inner, len) + if len.ty() == tcx.types.usize && tcx.features().transmute_generic_consts => + { + match SizeSkeleton::compute(inner, tcx, param_env)? { + // This may succeed because the multiplication of two types may overflow + // but a single size of a nested array will not. + SizeSkeleton::Known(s) => { + if let Some(c) = len.try_eval_target_usize(tcx, param_env) { + let size = s + .bytes() + .checked_mul(c) + .ok_or_else(|| LayoutError::SizeOverflow(ty))?; + return Ok(SizeSkeleton::Known(Size::from_bytes(size))); + } + let len = tcx.expand_abstract_consts(len); + let prev = ty::Const::from_target_usize(tcx, s.bytes()); + let Some(gen_size) = mul_sorted_consts(tcx, param_env, len, prev) else { + return Err(LayoutError::SizeOverflow(ty)); + }; + Ok(SizeSkeleton::Generic(gen_size)) + } + SizeSkeleton::Pointer { .. } => Err(err), + SizeSkeleton::Generic(g) => { + let len = tcx.expand_abstract_consts(len); + let Some(gen_size) = mul_sorted_consts(tcx, param_env, len, g) else { + return Err(LayoutError::SizeOverflow(ty)); + }; + Ok(SizeSkeleton::Generic(gen_size)) + } + } + } ty::Adt(def, substs) => { // Only newtypes and enums w/ nullable pointer optimization. @@ -335,7 +372,7 @@ impl<'tcx> SizeSkeleton<'tcx> { // Get a zero-sized variant or a pointer newtype. let zero_or_ptr_variant = |i| { - let i = VariantIdx::new(i); + let i = VariantIdx::from_usize(i); let fields = def.variant(i).fields.iter().map(|field| { SizeSkeleton::compute(field.ty(tcx, substs), tcx, param_env) @@ -355,6 +392,9 @@ impl<'tcx> SizeSkeleton<'tcx> { } ptr = Some(field); } + SizeSkeleton::Generic(_) => { + return Err(err); + } } } Ok(ptr) @@ -410,11 +450,66 @@ impl<'tcx> SizeSkeleton<'tcx> { (SizeSkeleton::Pointer { tail: a, .. }, SizeSkeleton::Pointer { tail: b, .. }) => { a == b } + // constants are always pre-normalized into a canonical form so this + // only needs to check if their pointers are identical. + (SizeSkeleton::Generic(a), SizeSkeleton::Generic(b)) => a == b, _ => false, } } } +/// When creating the layout for types with abstract conts in their size (i.e. [usize; 4 * N]), +/// to ensure that they have a canonical order and can be compared directly we combine all +/// constants, and sort the other terms. This allows comparison of expressions of sizes, +/// allowing for things like transmutating between types that depend on generic consts. +/// This returns `None` if multiplication of constants overflows. +fn mul_sorted_consts<'tcx>( + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + a: ty::Const<'tcx>, + b: ty::Const<'tcx>, +) -> Option<ty::Const<'tcx>> { + use crate::mir::BinOp::Mul; + use ty::ConstKind::Expr; + use ty::Expr::Binop; + + let mut work = vec![a, b]; + let mut done = vec![]; + while let Some(n) = work.pop() { + if let Expr(Binop(Mul, l, r)) = n.kind() { + work.push(l); + work.push(r) + } else { + done.push(n); + } + } + let mut k = 1; + let mut overflow = false; + done.retain(|c| { + let Some(c) = c.try_eval_target_usize(tcx, param_env) else { + return true; + }; + let Some(next) = c.checked_mul(k) else { + overflow = true; + return false; + }; + k = next; + false + }); + if overflow { + return None; + } + if k != 1 { + done.push(ty::Const::from_target_usize(tcx, k)); + } else if k == 0 { + return Some(ty::Const::from_target_usize(tcx, 0)); + } + done.sort_unstable(); + + // create a single tree from the buffer + done.into_iter().reduce(|acc, n| tcx.mk_const(Expr(Binop(Mul, n, acc)), n.ty())) +} + pub trait HasTyCtxt<'tcx>: HasDataLayout { fn tcx(&self) -> TyCtxt<'tcx>; } @@ -636,7 +731,7 @@ where variants: Variants::Single { index: variant_index }, fields: match NonZeroUsize::new(fields) { Some(fields) => FieldsShape::Union(fields), - None => FieldsShape::Arbitrary { offsets: vec![], memory_index: vec![] }, + None => FieldsShape::Arbitrary { offsets: IndexVec::new(), memory_index: IndexVec::new() }, }, abi: Abi::Uninhabited, largest_niche: None, @@ -730,7 +825,11 @@ where */ }; - let metadata = if let Some(metadata_def_id) = tcx.lang_items().metadata_type() { + let metadata = if let Some(metadata_def_id) = tcx.lang_items().metadata_type() + // Projection eagerly bails out when the pointee references errors, + // fall back to structurally deducing metadata. + && !pointee.references_error() + { let metadata = tcx.normalize_erasing_regions( cx.param_env(), tcx.mk_projection(metadata_def_id, [pointee]), @@ -794,7 +893,8 @@ where ty::Adt(def, substs) => { match this.variants { Variants::Single { index } => { - TyMaybeWithLayout::Ty(def.variant(index).fields[i].ty(tcx, substs)) + let field = &def.variant(index).fields[FieldIdx::from_usize(i)]; + TyMaybeWithLayout::Ty(field.ty(tcx, substs)) } // Discriminant field for enums (where applicable). @@ -1126,10 +1226,11 @@ pub fn fn_can_unwind(tcx: TyCtxt<'_>, fn_def_id: Option<DefId>, abi: SpecAbi) -> | AvrNonBlockingInterrupt | CCmseNonSecureCall | Wasm - | RustIntrinsic | PlatformIntrinsic | Unadjusted => false, - Rust | RustCall | RustCold => tcx.sess.panic_strategy() == PanicStrategy::Unwind, + Rust | RustCall | RustCold | RustIntrinsic => { + tcx.sess.panic_strategy() == PanicStrategy::Unwind + } } } |