about summary refs log tree commit diff
path: root/compiler/rustc_ty_utils/src/layout.rs
diff options
context:
space:
mode:
authorMoulins <arthur.heuillard@orange.fr>2023-06-28 09:02:24 +0200
committerMoulins <arthur.heuillard@orange.fr>2023-07-21 03:31:46 +0200
commitc30fbb95a6ac4af9f08628870fd5b8cad6ccbf75 (patch)
tree614e3c3e9d91c6b87305d1177122a98ac110376b /compiler/rustc_ty_utils/src/layout.rs
parent403f34b599fb3ed7abd71f3388da3c5061a2d84f (diff)
downloadrust-c30fbb95a6ac4af9f08628870fd5b8cad6ccbf75.tar.gz
rust-c30fbb95a6ac4af9f08628870fd5b8cad6ccbf75.zip
Track exactness in `NaiveLayout` and use it for `SizeSkeleton` checks
Diffstat (limited to 'compiler/rustc_ty_utils/src/layout.rs')
-rw-r--r--compiler/rustc_ty_utils/src/layout.rs71
1 files changed, 39 insertions, 32 deletions
diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs
index c81e76e4471..d484448a0fe 100644
--- a/compiler/rustc_ty_utils/src/layout.rs
+++ b/compiler/rustc_ty_utils/src/layout.rs
@@ -90,9 +90,9 @@ fn layout_of<'tcx>(
     let cx = LayoutCx { tcx, param_env };
     let layout = layout_of_uncached(&cx, ty)?;
 
-    if !naive.is_underestimate_of(layout) {
+    if !naive.is_compatible_with(layout) {
         bug!(
-            "the estimated naive layout is bigger than the actual layout:\n{:#?}\n{:#?}",
+            "the naive layout isn't compatible with the actual layout:\n{:#?}\n{:#?}",
             naive,
             layout,
         );
@@ -119,15 +119,23 @@ fn naive_layout_of_uncached<'tcx>(
     let tcx = cx.tcx;
     let dl = cx.data_layout();
 
-    let scalar =
-        |value: Primitive| NaiveLayout { min_size: value.size(dl), min_align: value.align(dl).abi };
+    let scalar = |value: Primitive| NaiveLayout {
+        min_size: value.size(dl),
+        min_align: value.align(dl).abi,
+        is_exact: true,
+    };
 
     let univariant = |fields: &mut dyn Iterator<Item = Ty<'tcx>>,
                       repr: &ReprOptions|
      -> Result<NaiveLayout, &'tcx LayoutError<'tcx>> {
+        if repr.pack.is_some() && repr.align.is_some() {
+            cx.tcx.sess.delay_span_bug(DUMMY_SP, "struct cannot be packed and aligned");
+            return Err(error(cx, LayoutError::Unknown(ty)));
+        }
+
         // For simplicity, ignore inter-field padding; this may underestimate the size.
         // FIXME(reference_niches): Be smarter and implement something closer to the real layout logic.
-        let mut layout = NaiveLayout::EMPTY;
+        let mut layout = NaiveLayout::UNKNOWN;
         for field in fields {
             let field = cx.naive_layout_of(field)?;
             layout = layout
@@ -192,12 +200,14 @@ fn naive_layout_of_uncached<'tcx>(
                     .min_size
                     .checked_mul(count, cx)
                     .ok_or_else(|| error(cx, LayoutError::SizeOverflow(ty)))?,
-                min_align: element.min_align,
+                ..*element
             }
         }
-        ty::Slice(element) => {
-            NaiveLayout { min_size: Size::ZERO, min_align: cx.naive_layout_of(element)?.min_align }
-        }
+        ty::Slice(element) => NaiveLayout {
+            min_size: Size::ZERO,
+            // NOTE: this could be unconditionally exact if `NaiveLayout` guaranteed exact align.
+            ..*cx.naive_layout_of(element)?
+        },
         ty::Str => NaiveLayout::EMPTY,
 
         // Odd unit types.
@@ -205,7 +215,7 @@ fn naive_layout_of_uncached<'tcx>(
 
         // FIXME(reference_niches): try to actually compute a reasonable layout estimate,
         // without duplicating too much code from `generator_layout`.
-        ty::Generator(..) => NaiveLayout::EMPTY,
+        ty::Generator(..) => NaiveLayout::UNKNOWN,
 
         ty::Closure(_, ref substs) => {
             univariant(&mut substs.as_closure().upvar_tys(), &ReprOptions::default())?
@@ -223,12 +233,21 @@ fn naive_layout_of_uncached<'tcx>(
         }
 
         ty::Adt(def, substs) => {
-            // For simplicity, assume that any discriminant field (if it exists)
-            // gets niched inside one of the variants; this will underestimate the size
-            // (and sometimes alignment) of enums.
-            // FIXME(reference_niches): Be smarter and actually take into accoount the discriminant.
             let repr = def.repr();
-            def.variants().iter().try_fold(NaiveLayout::EMPTY, |layout, v| {
+            let base = if def.is_struct() && !repr.simd() {
+                // FIXME(reference_niches): compute proper alignment for SIMD types.
+                NaiveLayout::EMPTY
+            } else {
+                // For simplicity, assume that any discriminant field (if it exists)
+                // gets niched inside one of the variants; this will underestimate the size
+                // (and sometimes alignment) of enums.
+                // FIXME(reference_niches): Be smarter and actually take into accoount the discriminant.
+                // Also consider adding a special case for null-optimized enums, so that we can have
+                // `Option<&T>: PointerLike` in generic contexts.
+                NaiveLayout::UNKNOWN
+            };
+
+            def.variants().iter().try_fold(base, |layout, v| {
                 let mut fields = v.fields.iter().map(|f| f.ty(tcx, substs));
                 let vlayout = univariant(&mut fields, &repr)?;
                 Ok(layout.union(&vlayout))
@@ -260,12 +279,10 @@ fn univariant_uninterned<'tcx>(
     kind: StructKind,
 ) -> Result<LayoutS, &'tcx LayoutError<'tcx>> {
     let dl = cx.data_layout();
-    let pack = repr.pack;
-    if pack.is_some() && repr.align.is_some() {
-        cx.tcx.sess.delay_span_bug(DUMMY_SP, "struct cannot be packed and aligned");
-        return Err(cx.tcx.arena.alloc(LayoutError::Unknown(ty)));
-    }
-
+    assert!(
+        !(repr.pack.is_some() && repr.align.is_some()),
+        "already rejected by `naive_layout_of`"
+    );
     cx.univariant(dl, fields, repr, kind).ok_or_else(|| error(cx, LayoutError::SizeOverflow(ty)))
 }
 
@@ -325,17 +342,7 @@ fn layout_of_uncached<'tcx>(
             if !ty.is_unsafe_ptr() {
                 // Calling `layout_of` here would cause a query cycle for recursive types;
                 // so use a conservative estimate that doesn't look past references.
-                let naive = match cx.naive_layout_of(pointee) {
-                    Ok(n) => n.layout,
-                    // This can happen when computing the `SizeSkeleton` of a generic type.
-                    Err(LayoutError::Unknown(_)) => {
-                        // TODO(reference_niches): this is *very* incorrect, but we can't
-                        // return an error here; this would break transmute checks.
-                        // We need some other solution.
-                        NaiveLayout::EMPTY
-                    }
-                    Err(err) => return Err(err),
-                };
+                let naive = cx.naive_layout_of(pointee)?.layout;
 
                 let niches = match *pointee.kind() {
                     ty::FnDef(def, ..)