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.size.bytes().is_multiple_of(layout.align.bytes()) {
12 bug!("size is not a multiple of align, in the following layout:\n{layout:#?}");
13 }
14 if layout.size.bytes() >= tcx.data_layout.obj_size_bound() {
15 bug!("size is too large, in the following layout:\n{layout:#?}");
16 }
17 if !cfg!(debug_assertions) {
21 return;
23 }
24
25 if layout.ty.is_privately_uninhabited(tcx, cx.typing_env) {
31 assert!(
32 layout.is_uninhabited(),
33 "{:?} is type-level uninhabited but not ABI-uninhabited?",
34 layout.ty
35 );
36 }
37
38 fn non_zst_fields<'tcx, 'a>(
40 cx: &'a LayoutCx<'tcx>,
41 layout: &'a TyAndLayout<'tcx>,
42 ) -> impl Iterator<Item = (Size, TyAndLayout<'tcx>)> {
43 (0..layout.layout.fields().count()).filter_map(|i| {
44 let field = layout.field(cx, i);
45 let zst = field.is_zst();
50 (!zst).then(|| (layout.fields.offset(i), field))
51 })
52 }
53
54 fn skip_newtypes<'tcx>(cx: &LayoutCx<'tcx>, layout: &TyAndLayout<'tcx>) -> TyAndLayout<'tcx> {
55 if matches!(layout.layout.variants(), Variants::Multiple { .. }) {
56 return *layout;
58 }
59 let mut fields = non_zst_fields(cx, layout);
60 let Some(first) = fields.next() else {
61 return *layout;
63 };
64 if fields.next().is_none() {
65 let (offset, first) = first;
66 if offset == Size::ZERO && first.layout.size() == layout.size {
67 return skip_newtypes(cx, &first);
71 }
72 }
73 *layout
75 }
76
77 fn check_layout_abi<'tcx>(cx: &LayoutCx<'tcx>, layout: &TyAndLayout<'tcx>) {
78 let align = layout.backend_repr.scalar_align(cx);
80 let size = layout.backend_repr.scalar_size(cx);
81 if let Some(align) = align {
82 assert_eq!(
83 layout.layout.align().abi,
84 align,
85 "alignment mismatch between ABI and layout in {layout:#?}"
86 );
87 }
88 if let Some(size) = size {
89 assert_eq!(
90 layout.layout.size(),
91 size,
92 "size mismatch between ABI and layout in {layout:#?}"
93 );
94 }
95
96 match layout.layout.backend_repr() {
98 BackendRepr::Scalar(_) => {
99 let align = align.unwrap();
101 let size = size.unwrap();
102 let inner = skip_newtypes(cx, layout);
104 assert!(
105 matches!(inner.layout.backend_repr(), BackendRepr::Scalar(_)),
106 "`Scalar` type {} is newtype around non-`Scalar` type {}",
107 layout.ty,
108 inner.ty
109 );
110 match inner.layout.fields() {
111 FieldsShape::Primitive => {
112 }
114 FieldsShape::Union(..) => {
115 return;
117 }
118 FieldsShape::Arbitrary { .. } => {
119 assert!(
121 inner.ty.is_enum(),
122 "`Scalar` layout for non-primitive non-enum type {}",
123 inner.ty
124 );
125 assert_eq!(
126 inner.layout.fields().count(),
127 1,
128 "`Scalar` layout for multiple-field type in {inner:#?}",
129 );
130 let offset = inner.layout.fields().offset(0);
131 let field = inner.field(cx, 0);
132 assert_eq!(
134 offset,
135 Size::ZERO,
136 "`Scalar` field at non-0 offset in {inner:#?}",
137 );
138 assert_eq!(field.size, size, "`Scalar` field with bad size in {inner:#?}",);
139 assert_eq!(
140 field.align.abi, align,
141 "`Scalar` field with bad align in {inner:#?}",
142 );
143 assert!(
144 matches!(field.backend_repr, BackendRepr::Scalar(_)),
145 "`Scalar` field with bad ABI in {inner:#?}",
146 );
147 }
148 _ => {
149 panic!("`Scalar` layout for non-primitive non-enum type {}", inner.ty);
150 }
151 }
152 }
153 BackendRepr::ScalarPair(scalar1, scalar2) => {
154 let inner = skip_newtypes(cx, layout);
156 assert!(
157 matches!(inner.layout.backend_repr(), BackendRepr::ScalarPair(..)),
158 "`ScalarPair` type {} is newtype around non-`ScalarPair` type {}",
159 layout.ty,
160 inner.ty
161 );
162 if matches!(inner.layout.variants(), Variants::Multiple { .. }) {
163 return;
166 }
167 match inner.layout.fields() {
168 FieldsShape::Arbitrary { .. } => {
169 }
171 FieldsShape::Union(..) => {
172 return;
174 }
175 _ => {
176 panic!("`ScalarPair` layout with unexpected field shape in {inner:#?}");
177 }
178 }
179 let mut fields = non_zst_fields(cx, &inner);
180 let (offset1, field1) = fields.next().unwrap_or_else(|| {
181 panic!(
182 "`ScalarPair` layout for type with not even one non-ZST field: {inner:#?}"
183 )
184 });
185 let (offset2, field2) = fields.next().unwrap_or_else(|| {
186 panic!(
187 "`ScalarPair` layout for type with less than two non-ZST fields: {inner:#?}"
188 )
189 });
190 assert_matches!(
191 fields.next(),
192 None,
193 "`ScalarPair` layout for type with at least three non-ZST fields: {inner:#?}"
194 );
195 let (offset1, field1, offset2, field2) = if offset1 <= offset2 {
197 (offset1, field1, offset2, field2)
198 } else {
199 (offset2, field2, offset1, field1)
200 };
201 let size1 = scalar1.size(cx);
203 let align1 = scalar1.align(cx).abi;
204 let size2 = scalar2.size(cx);
205 let align2 = scalar2.align(cx).abi;
206 assert_eq!(
207 offset1,
208 Size::ZERO,
209 "`ScalarPair` first field at non-0 offset in {inner:#?}",
210 );
211 assert_eq!(
212 field1.size, size1,
213 "`ScalarPair` first field with bad size in {inner:#?}",
214 );
215 assert_eq!(
216 field1.align.abi, align1,
217 "`ScalarPair` first field with bad align in {inner:#?}",
218 );
219 assert_matches!(
220 field1.backend_repr,
221 BackendRepr::Scalar(_),
222 "`ScalarPair` first field with bad ABI in {inner:#?}",
223 );
224 let field2_offset = size1.align_to(align2);
225 assert_eq!(
226 offset2, field2_offset,
227 "`ScalarPair` second field at bad offset in {inner:#?}",
228 );
229 assert_eq!(
230 field2.size, size2,
231 "`ScalarPair` second field with bad size in {inner:#?}",
232 );
233 assert_eq!(
234 field2.align.abi, align2,
235 "`ScalarPair` second field with bad align in {inner:#?}",
236 );
237 assert_matches!(
238 field2.backend_repr,
239 BackendRepr::Scalar(_),
240 "`ScalarPair` second field with bad ABI in {inner:#?}",
241 );
242 }
243 BackendRepr::SimdVector { element, count } => {
244 let align = layout.align.abi;
245 let size = layout.size;
246 let element_align = element.align(cx).abi;
247 let element_size = element.size(cx);
248 assert!(align >= element_align);
250 assert!(size == (element_size * count).align_to(align));
252 }
253 BackendRepr::Memory { .. } => {} }
255 }
256
257 check_layout_abi(cx, layout);
258
259 match &layout.variants {
260 Variants::Empty => {
261 assert!(layout.is_uninhabited());
262 }
263 Variants::Single { index } => {
264 if let Some(variants) = layout.ty.variant_range(tcx) {
265 assert!(variants.contains(index));
266 } else {
267 assert!(index.as_u32() == 0);
269 }
270 }
271 Variants::Multiple { variants, tag, tag_encoding, .. } => {
272 if let TagEncoding::Niche { niche_start, untagged_variant, niche_variants } =
273 tag_encoding
274 {
275 let niche_size = tag.size(cx);
276 assert!(*niche_start <= niche_size.unsigned_int_max());
277 for (idx, variant) in variants.iter_enumerated() {
278 if !variant.is_uninhabited() {
280 assert!(idx == *untagged_variant || niche_variants.contains(&idx));
281 }
282
283 assert_eq!(
285 layout.ty.discriminant_for_variant(tcx, idx).unwrap().val,
286 u128::from(idx.as_u32()),
287 );
288 }
289 }
290 for variant in variants.iter() {
291 assert_matches!(variant.variants, Variants::Single { .. });
293 if variant.size > layout.size {
296 bug!(
297 "Type with size {} bytes has variant with size {} bytes: {layout:#?}",
298 layout.size.bytes(),
299 variant.size.bytes(),
300 )
301 }
302 if variant.align.abi > layout.align.abi {
303 bug!(
304 "Type with alignment {} bytes has variant with alignment {} bytes: {layout:#?}",
305 layout.align.bytes(),
306 variant.align.bytes(),
307 )
308 }
309 if variant.size == Size::ZERO
311 || variant.fields.count() == 0
312 || variant.is_uninhabited()
313 {
314 continue;
320 }
321 let scalar_coherent = |s1: Scalar, s2: Scalar| {
323 s1.size(cx) == s2.size(cx) && s1.align(cx) == s2.align(cx)
324 };
325 let abi_coherent = match (layout.backend_repr, variant.backend_repr) {
326 (BackendRepr::Scalar(s1), BackendRepr::Scalar(s2)) => scalar_coherent(s1, s2),
327 (BackendRepr::ScalarPair(a1, b1), BackendRepr::ScalarPair(a2, b2)) => {
328 scalar_coherent(a1, a2) && scalar_coherent(b1, b2)
329 }
330 (BackendRepr::Memory { .. }, _) => true,
331 _ => false,
332 };
333 if !abi_coherent {
334 bug!(
335 "Variant ABI is incompatible with top-level ABI:\nvariant={:#?}\nTop-level: {layout:#?}",
336 variant
337 );
338 }
339 }
340 }
341 }
342}