Skip to main content

rustc_ty_utils/layout/
invariant.rs

1use std::assert_matches;
2
3use rustc_abi::{BackendRepr, FieldsShape, Scalar, Size, TagEncoding, Variants};
4use rustc_middle::ty;
5use rustc_middle::ty::TypeVisitableExt;
6use rustc_middle::ty::layout::{HasTyCtxt, LayoutCx, TyAndLayout};
7use rustc_span::bug;
8
9/// Enforce some basic invariants on layouts.
10pub(super) fn layout_sanity_check<'tcx>(cx: &LayoutCx<'tcx>, layout: &TyAndLayout<'tcx>) {
11    let tcx = cx.tcx();
12
13    if !layout.size.bytes().is_multiple_of(layout.align.bytes()) {
14        bug_impl(None,
    format_args!("size is not a multiple of align, in the following layout:\n{0:#?}",
        layout), Location::caller());bug!("size is not a multiple of align, in the following layout:\n{layout:#?}");
15    }
16    if layout.size.bytes() >= tcx.data_layout.obj_size_bound() {
17        bug_impl(None,
    format_args!("size is too large, in the following layout:\n{0:#?}",
        layout), Location::caller());bug!("size is too large, in the following layout:\n{layout:#?}");
18    }
19    // FIXME(#124403): Once `repr_c_enums_larger_than_int` is a hard error, we could assert
20    // here that a repr(c) enum discriminant is never larger than a c_int.
21
22    if !truecfg!(debug_assertions) {
23        // Stop here, the rest is kind of expensive.
24        return;
25    }
26
27    // Type-level uninhabitedness should always imply ABI uninhabitedness. This can be expensive on
28    // big non-exhaustive types, and is [hard to
29    // fix](https://github.com/rust-lang/rust/issues/141006#issuecomment-2883415000) in general.
30    // Only doing this sanity check when debug assertions are turned on avoids the issue for the
31    // very specific case of #140944.
32    if layout.ty.is_privately_uninhabited(tcx, cx.typing_env) {
33        if !layout.is_uninhabited() {
    {
        ::core::panicking::panic_fmt(format_args!("{0:?} is type-level uninhabited but not ABI-uninhabited?",
                layout.ty));
    }
};assert!(
34            layout.is_uninhabited(),
35            "{:?} is type-level uninhabited but not ABI-uninhabited?",
36            layout.ty
37        );
38    }
39    // ABI uninhabitedness should imply opsem uninhabitedness. However, we can only check that if
40    // the type is really monomorphic (while we can compute a layout for some generic types).
41    if layout.is_uninhabited() && !layout.ty.has_param() {
42        if !!layout.ty.is_opsem_inhabited(tcx, cx.typing_env) {
    {
        ::core::panicking::panic_fmt(format_args!("{0:?} is ABI-uninhabited but not opsem-uninhabited?",
                layout.ty));
    }
};assert!(
43            !layout.ty.is_opsem_inhabited(tcx, cx.typing_env),
44            "{:?} is ABI-uninhabited but not opsem-uninhabited?",
45            layout.ty
46        );
47    }
48
49    /// Yields non-ZST fields of the type
50    fn non_zst_fields<'tcx, 'a>(
51        cx: &'a LayoutCx<'tcx>,
52        layout: &'a TyAndLayout<'tcx>,
53    ) -> impl Iterator<Item = (Size, TyAndLayout<'tcx>)> {
54        (0..layout.layout.fields().count()).filter_map(|i| {
55            let field = layout.field(cx, i);
56            // Also checking `align == 1` here leads to test failures in
57            // `layout/zero-sized-array-union.rs`, where a type has a zero-size field with
58            // alignment 4 that still gets ignored during layout computation (which is okay
59            // since other fields already force alignment 4).
60            let zst = field.is_zst();
61            (!zst).then(|| (layout.fields.offset(i), field))
62        })
63    }
64
65    fn skip_newtypes<'tcx>(cx: &LayoutCx<'tcx>, layout: &TyAndLayout<'tcx>) -> TyAndLayout<'tcx> {
66        match *layout.ty.kind() {
67            ty::UnsafeBinder(bound_ty) => {
68                let ty = cx.tcx().instantiate_bound_regions_with_erased(bound_ty.into());
69                return skip_newtypes(cx, &TyAndLayout { ty, ..*layout });
70            }
71            _ => {}
72        }
73
74        if #[allow(non_exhaustive_omitted_patterns)] match layout.layout.variants() {
    Variants::Multiple { .. } => true,
    _ => false,
}matches!(layout.layout.variants(), Variants::Multiple { .. }) {
75            // Definitely not a newtype of anything.
76            return *layout;
77        }
78        let mut fields = non_zst_fields(cx, layout);
79        let Some(first) = fields.next() else {
80            // No fields here, so this could be a primitive or enum -- either way it's not a newtype around a thing
81            return *layout;
82        };
83        if fields.next().is_none() {
84            let (offset, first) = first;
85            if offset == Size::ZERO && first.layout.size() == layout.size {
86                // This is a newtype, so keep recursing.
87                // FIXME(RalfJung): I don't think it would be correct to do any checks for
88                // alignment here, so we don't. Is that correct?
89                return skip_newtypes(cx, &first);
90            }
91        }
92        // No more newtypes here.
93        *layout
94    }
95
96    fn check_layout_abi<'tcx>(cx: &LayoutCx<'tcx>, layout: &TyAndLayout<'tcx>) {
97        // Verify the ABI-mandated alignment and size for scalars.
98        let align = layout.backend_repr.scalar_platform_align(cx);
99        let size = layout.backend_repr.scalar_size(cx);
100        if let Some(align) = align {
101            {
    match (&layout.layout.align().abi, &align) {
        (left_val, right_val) => {
            if !(*left_val == *right_val) {
                let kind = ::core::panicking::AssertKind::Eq;
                ::core::panicking::assert_failed(kind, &*left_val,
                    &*right_val,
                    ::core::option::Option::Some(format_args!("alignment mismatch between ABI and layout in {0:#?}",
                            layout)));
            }
        }
    }
};assert_eq!(
102                layout.layout.align().abi,
103                align,
104                "alignment mismatch between ABI and layout in {layout:#?}"
105            );
106        }
107        if let Some(size) = size {
108            {
    match (&layout.layout.size(), &size) {
        (left_val, right_val) => {
            if !(*left_val == *right_val) {
                let kind = ::core::panicking::AssertKind::Eq;
                ::core::panicking::assert_failed(kind, &*left_val,
                    &*right_val,
                    ::core::option::Option::Some(format_args!("size mismatch between ABI and layout in {0:#?}",
                            layout)));
            }
        }
    }
};assert_eq!(
109                layout.layout.size(),
110                size,
111                "size mismatch between ABI and layout in {layout:#?}"
112            );
113        }
114
115        // Verify per-ABI invariants
116        match layout.layout.backend_repr() {
117            BackendRepr::Scalar(_) => {
118                // These must always be present for `Scalar` types.
119                let align = align.unwrap();
120                let size = size.unwrap();
121                // Check that this matches the underlying field.
122                let inner = skip_newtypes(cx, layout);
123                if !#[allow(non_exhaustive_omitted_patterns)] match inner.layout.backend_repr()
            {
            BackendRepr::Scalar(_) => true,
            _ => false,
        } {
    {
        ::core::panicking::panic_fmt(format_args!("`Scalar` type {0} is newtype around non-`Scalar` type {1}",
                layout.ty, inner.ty));
    }
};assert!(
124                    matches!(inner.layout.backend_repr(), BackendRepr::Scalar(_)),
125                    "`Scalar` type {} is newtype around non-`Scalar` type {}",
126                    layout.ty,
127                    inner.ty
128                );
129                match inner.layout.fields() {
130                    FieldsShape::Primitive => {
131                        // Fine.
132                    }
133                    FieldsShape::Union(..) => {
134                        // FIXME: I guess we could also check something here? Like, look at all fields?
135                        return;
136                    }
137                    FieldsShape::Arbitrary { .. } => {
138                        // Should be an enum, the only field is the discriminant.
139                        if !inner.ty.is_enum() {
    {
        ::core::panicking::panic_fmt(format_args!("`Scalar` layout for non-primitive non-enum type {0}",
                inner.ty));
    }
};assert!(
140                            inner.ty.is_enum(),
141                            "`Scalar` layout for non-primitive non-enum type {}",
142                            inner.ty
143                        );
144                        {
    match (&inner.layout.fields().count(), &1) {
        (left_val, right_val) => {
            if !(*left_val == *right_val) {
                let kind = ::core::panicking::AssertKind::Eq;
                ::core::panicking::assert_failed(kind, &*left_val,
                    &*right_val,
                    ::core::option::Option::Some(format_args!("`Scalar` layout for multiple-field type in {0:#?}",
                            inner)));
            }
        }
    }
};assert_eq!(
145                            inner.layout.fields().count(),
146                            1,
147                            "`Scalar` layout for multiple-field type in {inner:#?}",
148                        );
149                        let offset = inner.layout.fields().offset(0);
150                        let field = inner.field(cx, 0);
151                        // The field should be at the right offset, and match the `scalar` layout.
152                        {
    match (&offset, &Size::ZERO) {
        (left_val, right_val) => {
            if !(*left_val == *right_val) {
                let kind = ::core::panicking::AssertKind::Eq;
                ::core::panicking::assert_failed(kind, &*left_val,
                    &*right_val,
                    ::core::option::Option::Some(format_args!("`Scalar` field at non-0 offset in {0:#?}",
                            inner)));
            }
        }
    }
};assert_eq!(
153                            offset,
154                            Size::ZERO,
155                            "`Scalar` field at non-0 offset in {inner:#?}",
156                        );
157                        {
    match (&field.size, &size) {
        (left_val, right_val) => {
            if !(*left_val == *right_val) {
                let kind = ::core::panicking::AssertKind::Eq;
                ::core::panicking::assert_failed(kind, &*left_val,
                    &*right_val,
                    ::core::option::Option::Some(format_args!("`Scalar` field with bad size in {0:#?}",
                            inner)));
            }
        }
    }
};assert_eq!(field.size, size, "`Scalar` field with bad size in {inner:#?}",);
158                        {
    match (&field.align.abi, &align) {
        (left_val, right_val) => {
            if !(*left_val == *right_val) {
                let kind = ::core::panicking::AssertKind::Eq;
                ::core::panicking::assert_failed(kind, &*left_val,
                    &*right_val,
                    ::core::option::Option::Some(format_args!("`Scalar` field with bad align in {0:#?}",
                            inner)));
            }
        }
    }
};assert_eq!(
159                            field.align.abi, align,
160                            "`Scalar` field with bad align in {inner:#?}",
161                        );
162                        if !#[allow(non_exhaustive_omitted_patterns)] match field.backend_repr {
            BackendRepr::Scalar(_) => true,
            _ => false,
        } {
    {
        ::core::panicking::panic_fmt(format_args!("`Scalar` field with bad ABI in {0:#?}",
                inner));
    }
};assert!(
163                            matches!(field.backend_repr, BackendRepr::Scalar(_)),
164                            "`Scalar` field with bad ABI in {inner:#?}",
165                        );
166                    }
167                    _ => {
168                        {
    ::core::panicking::panic_fmt(format_args!("`Scalar` layout for non-primitive non-enum type {0}",
            inner.ty));
};panic!("`Scalar` layout for non-primitive non-enum type {}", inner.ty);
169                    }
170                }
171            }
172            BackendRepr::ScalarPair { a: scalar1, b: scalar2, b_offset } => {
173                // Check that the underlying pair of fields matches.
174                let inner = skip_newtypes(cx, layout);
175                if !#[allow(non_exhaustive_omitted_patterns)] match inner.layout.backend_repr()
            {
            BackendRepr::ScalarPair { .. } => true,
            _ => false,
        } {
    {
        ::core::panicking::panic_fmt(format_args!("`ScalarPair` type {0} is newtype around non-`ScalarPair` type {1}",
                layout.ty, inner.ty));
    }
};assert!(
176                    matches!(inner.layout.backend_repr(), BackendRepr::ScalarPair { .. }),
177                    "`ScalarPair` type {} is newtype around non-`ScalarPair` type {}",
178                    layout.ty,
179                    inner.ty
180                );
181                // `a` is at memory offset zero, so to keep them from overlapping the offset
182                // to `b` must be at least as much as the size of `a`.
183                if !(b_offset >= scalar1.size(cx)) {
    {
        ::core::panicking::panic_fmt(format_args!("`ScalarPair` scalars are overlapping in {0:?}",
                layout));
    }
};assert!(
184                    b_offset >= scalar1.size(cx),
185                    "`ScalarPair` scalars are overlapping in {layout:?}",
186                );
187                if #[allow(non_exhaustive_omitted_patterns)] match inner.layout.variants() {
    Variants::Multiple { .. } => true,
    _ => false,
}matches!(inner.layout.variants(), Variants::Multiple { .. }) {
188                    // FIXME: ScalarPair for enums is enormously complicated and it is very hard
189                    // to check anything about them.
190                    return;
191                }
192                match inner.layout.fields() {
193                    FieldsShape::Arbitrary { .. } => {
194                        // Checked below.
195                    }
196                    FieldsShape::Union(..) => {
197                        // FIXME: I guess we could also check something here? Like, look at all fields?
198                        return;
199                    }
200                    _ => {
201                        {
    ::core::panicking::panic_fmt(format_args!("`ScalarPair` layout with unexpected field shape in {0:#?}",
            inner));
};panic!("`ScalarPair` layout with unexpected field shape in {inner:#?}");
202                    }
203                }
204                let mut fields = non_zst_fields(cx, &inner);
205                let (offset1, field1) = fields.next().unwrap_or_else(|| {
206                    {
    ::core::panicking::panic_fmt(format_args!("`ScalarPair` layout for type with not even one non-ZST field: {0:#?}",
            inner));
}panic!(
207                        "`ScalarPair` layout for type with not even one non-ZST field: {inner:#?}"
208                    )
209                });
210                let (offset2, field2) = fields.next().unwrap_or_else(|| {
211                    {
    ::core::panicking::panic_fmt(format_args!("`ScalarPair` layout for type with less than two non-ZST fields: {0:#?}",
            inner));
}panic!(
212                        "`ScalarPair` layout for type with less than two non-ZST fields: {inner:#?}"
213                    )
214                });
215                {
    match fields.next() {
        None => {}
        ref left_val => {
            ::core::panicking::assert_matches_failed(left_val, "None",
                ::core::option::Option::Some(format_args!("`ScalarPair` layout for type with at least three non-ZST fields: {0:#?}",
                        inner)));
        }
    }
};assert_matches!(
216                    fields.next(),
217                    None,
218                    "`ScalarPair` layout for type with at least three non-ZST fields: {inner:#?}"
219                );
220                // The fields might be in opposite order.
221                let (offset1, field1, offset2, field2) = if offset1 <= offset2 {
222                    (offset1, field1, offset2, field2)
223                } else {
224                    (offset2, field2, offset1, field1)
225                };
226                // The fields should be at the right offset, and match the `scalar` layout.
227                let size1 = scalar1.size(cx);
228                let align1 = scalar1.default_align(cx).abi;
229                let size2 = scalar2.size(cx);
230                let align2 = scalar2.default_align(cx).abi;
231                {
    match (&offset1, &Size::ZERO) {
        (left_val, right_val) => {
            if !(*left_val == *right_val) {
                let kind = ::core::panicking::AssertKind::Eq;
                ::core::panicking::assert_failed(kind, &*left_val,
                    &*right_val,
                    ::core::option::Option::Some(format_args!("`ScalarPair` first field at non-0 offset in {0:#?}",
                            inner)));
            }
        }
    }
};assert_eq!(
232                    offset1,
233                    Size::ZERO,
234                    "`ScalarPair` first field at non-0 offset in {inner:#?}",
235                );
236                {
    match (&field1.size, &size1) {
        (left_val, right_val) => {
            if !(*left_val == *right_val) {
                let kind = ::core::panicking::AssertKind::Eq;
                ::core::panicking::assert_failed(kind, &*left_val,
                    &*right_val,
                    ::core::option::Option::Some(format_args!("`ScalarPair` first field with bad size in {0:#?}",
                            inner)));
            }
        }
    }
};assert_eq!(
237                    field1.size, size1,
238                    "`ScalarPair` first field with bad size in {inner:#?}",
239                );
240                {
    match (&field1.align.abi, &align1) {
        (left_val, right_val) => {
            if !(*left_val == *right_val) {
                let kind = ::core::panicking::AssertKind::Eq;
                ::core::panicking::assert_failed(kind, &*left_val,
                    &*right_val,
                    ::core::option::Option::Some(format_args!("`ScalarPair` first field with bad align in {0:#?}",
                            inner)));
            }
        }
    }
};assert_eq!(
241                    field1.align.abi, align1,
242                    "`ScalarPair` first field with bad align in {inner:#?}",
243                );
244                {
    match field1.backend_repr {
        BackendRepr::Scalar(_) => {}
        ref left_val => {
            ::core::panicking::assert_matches_failed(left_val,
                "BackendRepr::Scalar(_)",
                ::core::option::Option::Some(format_args!("`ScalarPair` first field with bad ABI in {0:#?}",
                        inner)));
        }
    }
};assert_matches!(
245                    field1.backend_repr,
246                    BackendRepr::Scalar(_),
247                    "`ScalarPair` first field with bad ABI in {inner:#?}",
248                );
249                let field2_offset = size1.align_to(align2);
250                {
    match (&offset2, &field2_offset) {
        (left_val, right_val) => {
            if !(*left_val == *right_val) {
                let kind = ::core::panicking::AssertKind::Eq;
                ::core::panicking::assert_failed(kind, &*left_val,
                    &*right_val,
                    ::core::option::Option::Some(format_args!("`ScalarPair` second field at bad offset in {0:#?}",
                            inner)));
            }
        }
    }
};assert_eq!(
251                    offset2, field2_offset,
252                    "`ScalarPair` second field at bad offset in {inner:#?}",
253                );
254                {
    match (&b_offset, &field2_offset) {
        (left_val, right_val) => {
            if !(*left_val == *right_val) {
                let kind = ::core::panicking::AssertKind::Eq;
                ::core::panicking::assert_failed(kind, &*left_val,
                    &*right_val,
                    ::core::option::Option::Some(format_args!("`ScalarPair` with inconsistent b_offset in {0:#?}",
                            inner)));
            }
        }
    }
};assert_eq!(
255                    b_offset, field2_offset,
256                    "`ScalarPair` with inconsistent b_offset in {inner:#?}",
257                );
258                {
    match (&field2.size, &size2) {
        (left_val, right_val) => {
            if !(*left_val == *right_val) {
                let kind = ::core::panicking::AssertKind::Eq;
                ::core::panicking::assert_failed(kind, &*left_val,
                    &*right_val,
                    ::core::option::Option::Some(format_args!("`ScalarPair` second field with bad size in {0:#?}",
                            inner)));
            }
        }
    }
};assert_eq!(
259                    field2.size, size2,
260                    "`ScalarPair` second field with bad size in {inner:#?}",
261                );
262                {
    match (&field2.align.abi, &align2) {
        (left_val, right_val) => {
            if !(*left_val == *right_val) {
                let kind = ::core::panicking::AssertKind::Eq;
                ::core::panicking::assert_failed(kind, &*left_val,
                    &*right_val,
                    ::core::option::Option::Some(format_args!("`ScalarPair` second field with bad align in {0:#?}",
                            inner)));
            }
        }
    }
};assert_eq!(
263                    field2.align.abi, align2,
264                    "`ScalarPair` second field with bad align in {inner:#?}",
265                );
266                {
    match field2.backend_repr {
        BackendRepr::Scalar(_) => {}
        ref left_val => {
            ::core::panicking::assert_matches_failed(left_val,
                "BackendRepr::Scalar(_)",
                ::core::option::Option::Some(format_args!("`ScalarPair` second field with bad ABI in {0:#?}",
                        inner)));
        }
    }
};assert_matches!(
267                    field2.backend_repr,
268                    BackendRepr::Scalar(_),
269                    "`ScalarPair` second field with bad ABI in {inner:#?}",
270                );
271            }
272            BackendRepr::SimdVector { element, count } => {
273                let align = layout.align.abi;
274                let size = layout.size;
275                let element_align = element.default_align(cx).abi;
276                let element_size = element.size(cx);
277                // Currently, vectors must always be aligned to at least their elements:
278                if !(align >= element_align) {
    ::core::panicking::panic("assertion failed: align >= element_align")
};assert!(align >= element_align);
279                // And the size has to be element * count plus alignment padding, of course
280                if !(size == (element_size * count.as_u64()).align_to(align)) {
    ::core::panicking::panic("assertion failed: size == (element_size * count.as_u64()).align_to(align)")
};assert!(size == (element_size * count.as_u64()).align_to(align));
281            }
282            BackendRepr::Memory { .. } | BackendRepr::SimdScalableVector { .. } => {} // Nothing to check.
283        }
284    }
285
286    check_layout_abi(cx, layout);
287
288    match &layout.variants {
289        Variants::Empty => {
290            if !layout.is_uninhabited() {
    ::core::panicking::panic("assertion failed: layout.is_uninhabited()")
};assert!(layout.is_uninhabited());
291        }
292        Variants::Single { index } => {
293            if let Some(variants) = layout.ty.variant_range(tcx) {
294                if !variants.contains(index) {
    ::core::panicking::panic("assertion failed: variants.contains(index)")
};assert!(variants.contains(index));
295            } else {
296                // Types without variants use `0` as dummy variant index.
297                if !(index.as_u32() == 0) {
    ::core::panicking::panic("assertion failed: index.as_u32() == 0")
};assert!(index.as_u32() == 0);
298            }
299        }
300        Variants::Multiple { variants, tag, tag_encoding, .. } => {
301            if let TagEncoding::Niche { niche_start, untagged_variant, niche_variants } =
302                tag_encoding
303            {
304                let niche_size = tag.size(cx);
305                if !(*niche_start <= niche_size.unsigned_int_max()) {
    ::core::panicking::panic("assertion failed: *niche_start <= niche_size.unsigned_int_max()")
};assert!(*niche_start <= niche_size.unsigned_int_max());
306                for (idx, variant) in variants.iter_enumerated() {
307                    // Ensure all inhabited variants are accounted for.
308                    if !variant.is_uninhabited() {
309                        if !(idx == *untagged_variant || niche_variants.contains(&idx)) {
    ::core::panicking::panic("assertion failed: idx == *untagged_variant || niche_variants.contains(&idx)")
};assert!(idx == *untagged_variant || niche_variants.contains(&idx));
310                    }
311
312                    // Ensure that for niche encoded tags the discriminant coincides with the variant index.
313                    let val = layout.ty.discriminant_for_variant(tcx, idx).unwrap().val;
314                    if val != u128::from(idx.as_u32()) {
315                        let adt_def = layout.ty.ty_adt_def().unwrap();
316                        cx.tcx().dcx().span_delayed_bug(
317                            cx.tcx().def_span(adt_def.did()),
318                            ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("variant {0:?} has discriminant {1:?} in niche-encoded type",
                idx, val))
    })format!(
319                                "variant {idx:?} has discriminant {val:?} in niche-encoded type"
320                            ),
321                        );
322                    }
323                }
324            }
325            for variant in variants.iter() {
326                // Variants should have the same or a smaller size as the full thing.
327                if variant.size > layout.size {
328                    bug_impl(None,
    format_args!("Type with size {0} bytes has variant with size {1} bytes: {2:#?}",
        layout.size.bytes(), variant.size.bytes(), layout),
    Location::caller())bug!(
329                        "Type with size {} bytes has variant with size {} bytes: {layout:#?}",
330                        layout.size.bytes(),
331                        variant.size.bytes(),
332                    )
333                }
334                // Skip empty variants.
335                if variant.size == Size::ZERO || !variant.has_fields() || variant.is_uninhabited() {
336                    // These are never actually accessed anyway, so we can skip the coherence check
337                    // for them. They also fail that check, since they may have
338                    // a different ABI even when the main type is
339                    // `Scalar`/`ScalarPair`. (Note that sometimes, variants with fields have size
340                    // 0, and sometimes, variants without fields have non-0 size.)
341                    continue;
342                }
343                // The top-level ABI and the ABI of the variants should be coherent.
344                let scalar_coherent = |s1: Scalar, s2: Scalar| {
345                    s1.size(cx) == s2.size(cx) && s1.default_align(cx) == s2.default_align(cx)
346                };
347                let abi_coherent = match (layout.backend_repr, variant.backend_repr) {
348                    (BackendRepr::Scalar(s1), BackendRepr::Scalar(s2)) => scalar_coherent(s1, s2),
349                    (
350                        BackendRepr::ScalarPair { a: a1, b: b1, b_offset: b1_offset },
351                        BackendRepr::ScalarPair { a: a2, b: b2, b_offset: b2_offset },
352                    ) => {
353                        scalar_coherent(a1, a2) && scalar_coherent(b1, b2) && b1_offset == b2_offset
354                    }
355                    (BackendRepr::Memory { .. }, _) => true,
356                    _ => false,
357                };
358                if !abi_coherent {
359                    bug_impl(None,
    format_args!("Variant ABI is incompatible with top-level ABI:\nvariant={0:#?}\nTop-level: {1:#?}",
        variant, layout), Location::caller());bug!(
360                        "Variant ABI is incompatible with top-level ABI:\nvariant={:#?}\nTop-level: {layout:#?}",
361                        variant
362                    );
363                }
364            }
365        }
366    }
367}