1use std::assert_matches::assert_matches;
2
3use rustc_abi::{BackendRepr, FieldsShape, Scalar, Size, TagEncoding, Variants};
4use rustc_middle::bug;
5use rustc_middle::ty::layout::{HasTyCtxt, LayoutCx, TyAndLayout};
6
7pub(super) fn layout_sanity_check<'tcx>(cx: &LayoutCx<'tcx>, layout: &TyAndLayout<'tcx>) {
9 let tcx = cx.tcx();
10
11 if layout.ty.is_privately_uninhabited(tcx, cx.typing_env) {
13 assert!(layout.is_uninhabited());
14 }
15
16 if layout.size.bytes() % layout.align.abi.bytes() != 0 {
17 bug!("size is not a multiple of align, in the following layout:\n{layout:#?}");
18 }
19 if layout.size.bytes() >= tcx.data_layout.obj_size_bound() {
20 bug!("size is too large, in the following layout:\n{layout:#?}");
21 }
22
23 if !cfg!(debug_assertions) {
24 return;
26 }
27
28 fn non_zst_fields<'tcx, 'a>(
30 cx: &'a LayoutCx<'tcx>,
31 layout: &'a TyAndLayout<'tcx>,
32 ) -> impl Iterator<Item = (Size, TyAndLayout<'tcx>)> + 'a {
33 (0..layout.layout.fields().count()).filter_map(|i| {
34 let field = layout.field(cx, i);
35 let zst = field.is_zst();
40 (!zst).then(|| (layout.fields.offset(i), field))
41 })
42 }
43
44 fn skip_newtypes<'tcx>(cx: &LayoutCx<'tcx>, layout: &TyAndLayout<'tcx>) -> TyAndLayout<'tcx> {
45 if matches!(layout.layout.variants(), Variants::Multiple { .. }) {
46 return *layout;
48 }
49 let mut fields = non_zst_fields(cx, layout);
50 let Some(first) = fields.next() else {
51 return *layout;
53 };
54 if fields.next().is_none() {
55 let (offset, first) = first;
56 if offset == Size::ZERO && first.layout.size() == layout.size {
57 return skip_newtypes(cx, &first);
61 }
62 }
63 *layout
65 }
66
67 fn check_layout_abi<'tcx>(cx: &LayoutCx<'tcx>, layout: &TyAndLayout<'tcx>) {
68 let align = layout.backend_repr.inherent_align(cx).map(|align| align.abi);
70 let size = layout.backend_repr.inherent_size(cx);
71 let Some((align, size)) = align.zip(size) else {
72 assert_matches!(
73 layout.layout.backend_repr(),
74 BackendRepr::Uninhabited | BackendRepr::Memory { .. },
75 "ABI unexpectedly missing alignment and/or size in {layout:#?}"
76 );
77 return;
78 };
79 assert_eq!(
80 layout.layout.align().abi,
81 align,
82 "alignment mismatch between ABI and layout in {layout:#?}"
83 );
84 assert_eq!(
85 layout.layout.size(),
86 size,
87 "size mismatch between ABI and layout in {layout:#?}"
88 );
89
90 match layout.layout.backend_repr() {
92 BackendRepr::Scalar(_) => {
93 let inner = skip_newtypes(cx, layout);
95 assert!(
96 matches!(inner.layout.backend_repr(), BackendRepr::Scalar(_)),
97 "`Scalar` type {} is newtype around non-`Scalar` type {}",
98 layout.ty,
99 inner.ty
100 );
101 match inner.layout.fields() {
102 FieldsShape::Primitive => {
103 }
105 FieldsShape::Union(..) => {
106 return;
108 }
109 FieldsShape::Arbitrary { .. } => {
110 assert!(
112 inner.ty.is_enum(),
113 "`Scalar` layout for non-primitive non-enum type {}",
114 inner.ty
115 );
116 assert_eq!(
117 inner.layout.fields().count(),
118 1,
119 "`Scalar` layout for multiple-field type in {inner:#?}",
120 );
121 let offset = inner.layout.fields().offset(0);
122 let field = inner.field(cx, 0);
123 assert_eq!(
125 offset,
126 Size::ZERO,
127 "`Scalar` field at non-0 offset in {inner:#?}",
128 );
129 assert_eq!(field.size, size, "`Scalar` field with bad size in {inner:#?}",);
130 assert_eq!(
131 field.align.abi, align,
132 "`Scalar` field with bad align in {inner:#?}",
133 );
134 assert!(
135 matches!(field.backend_repr, BackendRepr::Scalar(_)),
136 "`Scalar` field with bad ABI in {inner:#?}",
137 );
138 }
139 _ => {
140 panic!("`Scalar` layout for non-primitive non-enum type {}", inner.ty);
141 }
142 }
143 }
144 BackendRepr::ScalarPair(scalar1, scalar2) => {
145 let inner = skip_newtypes(cx, layout);
147 assert!(
148 matches!(inner.layout.backend_repr(), BackendRepr::ScalarPair(..)),
149 "`ScalarPair` type {} is newtype around non-`ScalarPair` type {}",
150 layout.ty,
151 inner.ty
152 );
153 if matches!(inner.layout.variants(), Variants::Multiple { .. }) {
154 return;
157 }
158 match inner.layout.fields() {
159 FieldsShape::Arbitrary { .. } => {
160 }
162 FieldsShape::Union(..) => {
163 return;
165 }
166 _ => {
167 panic!("`ScalarPair` layout with unexpected field shape in {inner:#?}");
168 }
169 }
170 let mut fields = non_zst_fields(cx, &inner);
171 let (offset1, field1) = fields.next().unwrap_or_else(|| {
172 panic!(
173 "`ScalarPair` layout for type with not even one non-ZST field: {inner:#?}"
174 )
175 });
176 let (offset2, field2) = fields.next().unwrap_or_else(|| {
177 panic!(
178 "`ScalarPair` layout for type with less than two non-ZST fields: {inner:#?}"
179 )
180 });
181 assert_matches!(
182 fields.next(),
183 None,
184 "`ScalarPair` layout for type with at least three non-ZST fields: {inner:#?}"
185 );
186 let (offset1, field1, offset2, field2) = if offset1 <= offset2 {
188 (offset1, field1, offset2, field2)
189 } else {
190 (offset2, field2, offset1, field1)
191 };
192 let size1 = scalar1.size(cx);
194 let align1 = scalar1.align(cx).abi;
195 let size2 = scalar2.size(cx);
196 let align2 = scalar2.align(cx).abi;
197 assert_eq!(
198 offset1,
199 Size::ZERO,
200 "`ScalarPair` first field at non-0 offset in {inner:#?}",
201 );
202 assert_eq!(
203 field1.size, size1,
204 "`ScalarPair` first field with bad size in {inner:#?}",
205 );
206 assert_eq!(
207 field1.align.abi, align1,
208 "`ScalarPair` first field with bad align in {inner:#?}",
209 );
210 assert_matches!(
211 field1.backend_repr,
212 BackendRepr::Scalar(_),
213 "`ScalarPair` first field with bad ABI in {inner:#?}",
214 );
215 let field2_offset = size1.align_to(align2);
216 assert_eq!(
217 offset2, field2_offset,
218 "`ScalarPair` second field at bad offset in {inner:#?}",
219 );
220 assert_eq!(
221 field2.size, size2,
222 "`ScalarPair` second field with bad size in {inner:#?}",
223 );
224 assert_eq!(
225 field2.align.abi, align2,
226 "`ScalarPair` second field with bad align in {inner:#?}",
227 );
228 assert_matches!(
229 field2.backend_repr,
230 BackendRepr::Scalar(_),
231 "`ScalarPair` second field with bad ABI in {inner:#?}",
232 );
233 }
234 BackendRepr::Vector { element, .. } => {
235 assert!(align >= element.align(cx).abi); }
238 BackendRepr::Uninhabited | BackendRepr::Memory { .. } => {} }
240 }
241
242 check_layout_abi(cx, layout);
243
244 match &layout.variants {
245 Variants::Empty => {
246 assert!(layout.is_uninhabited());
247 }
248 Variants::Single { index } => {
249 if let Some(variants) = layout.ty.variant_range(tcx) {
250 assert!(variants.contains(index));
251 } else {
252 assert!(index.as_u32() == 0);
254 }
255 }
256 Variants::Multiple { variants, tag, tag_encoding, .. } => {
257 if let TagEncoding::Niche { niche_start, untagged_variant, niche_variants } =
258 tag_encoding
259 {
260 let niche_size = tag.size(cx);
261 assert!(*niche_start <= niche_size.unsigned_int_max());
262 for (idx, variant) in variants.iter_enumerated() {
263 if !variant.is_uninhabited() {
265 assert!(idx == *untagged_variant || niche_variants.contains(&idx));
266 }
267 }
268 }
269 for variant in variants.iter() {
270 assert_matches!(variant.variants, Variants::Single { .. });
272 if variant.size > layout.size {
275 bug!(
276 "Type with size {} bytes has variant with size {} bytes: {layout:#?}",
277 layout.size.bytes(),
278 variant.size.bytes(),
279 )
280 }
281 if variant.align.abi > layout.align.abi {
282 bug!(
283 "Type with alignment {} bytes has variant with alignment {} bytes: {layout:#?}",
284 layout.align.abi.bytes(),
285 variant.align.abi.bytes(),
286 )
287 }
288 if variant.size == Size::ZERO
290 || variant.fields.count() == 0
291 || variant.is_uninhabited()
292 {
293 continue;
299 }
300 let scalar_coherent = |s1: Scalar, s2: Scalar| {
302 s1.size(cx) == s2.size(cx) && s1.align(cx) == s2.align(cx)
303 };
304 let abi_coherent = match (layout.backend_repr, variant.backend_repr) {
305 (BackendRepr::Scalar(s1), BackendRepr::Scalar(s2)) => scalar_coherent(s1, s2),
306 (BackendRepr::ScalarPair(a1, b1), BackendRepr::ScalarPair(a2, b2)) => {
307 scalar_coherent(a1, a2) && scalar_coherent(b1, b2)
308 }
309 (BackendRepr::Uninhabited, _) => true,
310 (BackendRepr::Memory { .. }, _) => true,
311 _ => false,
312 };
313 if !abi_coherent {
314 bug!(
315 "Variant ABI is incompatible with top-level ABI:\nvariant={:#?}\nTop-level: {layout:#?}",
316 variant
317 );
318 }
319 }
320 }
321 }
322}