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() % layout.align.abi.bytes() != 0 {
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
18 if !cfg!(debug_assertions) {
19 return;
21 }
22
23 if layout.ty.is_privately_uninhabited(tcx, cx.typing_env) {
29 assert!(
30 layout.is_uninhabited(),
31 "{:?} is type-level uninhabited but not ABI-uninhabited?",
32 layout.ty
33 );
34 }
35
36 fn non_zst_fields<'tcx, 'a>(
38 cx: &'a LayoutCx<'tcx>,
39 layout: &'a TyAndLayout<'tcx>,
40 ) -> impl Iterator<Item = (Size, TyAndLayout<'tcx>)> {
41 (0..layout.layout.fields().count()).filter_map(|i| {
42 let field = layout.field(cx, i);
43 let zst = field.is_zst();
48 (!zst).then(|| (layout.fields.offset(i), field))
49 })
50 }
51
52 fn skip_newtypes<'tcx>(cx: &LayoutCx<'tcx>, layout: &TyAndLayout<'tcx>) -> TyAndLayout<'tcx> {
53 if matches!(layout.layout.variants(), Variants::Multiple { .. }) {
54 return *layout;
56 }
57 let mut fields = non_zst_fields(cx, layout);
58 let Some(first) = fields.next() else {
59 return *layout;
61 };
62 if fields.next().is_none() {
63 let (offset, first) = first;
64 if offset == Size::ZERO && first.layout.size() == layout.size {
65 return skip_newtypes(cx, &first);
69 }
70 }
71 *layout
73 }
74
75 fn check_layout_abi<'tcx>(cx: &LayoutCx<'tcx>, layout: &TyAndLayout<'tcx>) {
76 let align = layout.backend_repr.scalar_align(cx);
78 let size = layout.backend_repr.scalar_size(cx);
79 if let Some(align) = align {
80 assert_eq!(
81 layout.layout.align().abi,
82 align,
83 "alignment mismatch between ABI and layout in {layout:#?}"
84 );
85 }
86 if let Some(size) = size {
87 assert_eq!(
88 layout.layout.size(),
89 size,
90 "size mismatch between ABI and layout in {layout:#?}"
91 );
92 }
93
94 match layout.layout.backend_repr() {
96 BackendRepr::Scalar(_) => {
97 let align = align.unwrap();
99 let size = size.unwrap();
100 let inner = skip_newtypes(cx, layout);
102 assert!(
103 matches!(inner.layout.backend_repr(), BackendRepr::Scalar(_)),
104 "`Scalar` type {} is newtype around non-`Scalar` type {}",
105 layout.ty,
106 inner.ty
107 );
108 match inner.layout.fields() {
109 FieldsShape::Primitive => {
110 }
112 FieldsShape::Union(..) => {
113 return;
115 }
116 FieldsShape::Arbitrary { .. } => {
117 assert!(
119 inner.ty.is_enum(),
120 "`Scalar` layout for non-primitive non-enum type {}",
121 inner.ty
122 );
123 assert_eq!(
124 inner.layout.fields().count(),
125 1,
126 "`Scalar` layout for multiple-field type in {inner:#?}",
127 );
128 let offset = inner.layout.fields().offset(0);
129 let field = inner.field(cx, 0);
130 assert_eq!(
132 offset,
133 Size::ZERO,
134 "`Scalar` field at non-0 offset in {inner:#?}",
135 );
136 assert_eq!(field.size, size, "`Scalar` field with bad size in {inner:#?}",);
137 assert_eq!(
138 field.align.abi, align,
139 "`Scalar` field with bad align in {inner:#?}",
140 );
141 assert!(
142 matches!(field.backend_repr, BackendRepr::Scalar(_)),
143 "`Scalar` field with bad ABI in {inner:#?}",
144 );
145 }
146 _ => {
147 panic!("`Scalar` layout for non-primitive non-enum type {}", inner.ty);
148 }
149 }
150 }
151 BackendRepr::ScalarPair(scalar1, scalar2) => {
152 let inner = skip_newtypes(cx, layout);
154 assert!(
155 matches!(inner.layout.backend_repr(), BackendRepr::ScalarPair(..)),
156 "`ScalarPair` type {} is newtype around non-`ScalarPair` type {}",
157 layout.ty,
158 inner.ty
159 );
160 if matches!(inner.layout.variants(), Variants::Multiple { .. }) {
161 return;
164 }
165 match inner.layout.fields() {
166 FieldsShape::Arbitrary { .. } => {
167 }
169 FieldsShape::Union(..) => {
170 return;
172 }
173 _ => {
174 panic!("`ScalarPair` layout with unexpected field shape in {inner:#?}");
175 }
176 }
177 let mut fields = non_zst_fields(cx, &inner);
178 let (offset1, field1) = fields.next().unwrap_or_else(|| {
179 panic!(
180 "`ScalarPair` layout for type with not even one non-ZST field: {inner:#?}"
181 )
182 });
183 let (offset2, field2) = fields.next().unwrap_or_else(|| {
184 panic!(
185 "`ScalarPair` layout for type with less than two non-ZST fields: {inner:#?}"
186 )
187 });
188 assert_matches!(
189 fields.next(),
190 None,
191 "`ScalarPair` layout for type with at least three non-ZST fields: {inner:#?}"
192 );
193 let (offset1, field1, offset2, field2) = if offset1 <= offset2 {
195 (offset1, field1, offset2, field2)
196 } else {
197 (offset2, field2, offset1, field1)
198 };
199 let size1 = scalar1.size(cx);
201 let align1 = scalar1.align(cx).abi;
202 let size2 = scalar2.size(cx);
203 let align2 = scalar2.align(cx).abi;
204 assert_eq!(
205 offset1,
206 Size::ZERO,
207 "`ScalarPair` first field at non-0 offset in {inner:#?}",
208 );
209 assert_eq!(
210 field1.size, size1,
211 "`ScalarPair` first field with bad size in {inner:#?}",
212 );
213 assert_eq!(
214 field1.align.abi, align1,
215 "`ScalarPair` first field with bad align in {inner:#?}",
216 );
217 assert_matches!(
218 field1.backend_repr,
219 BackendRepr::Scalar(_),
220 "`ScalarPair` first field with bad ABI in {inner:#?}",
221 );
222 let field2_offset = size1.align_to(align2);
223 assert_eq!(
224 offset2, field2_offset,
225 "`ScalarPair` second field at bad offset in {inner:#?}",
226 );
227 assert_eq!(
228 field2.size, size2,
229 "`ScalarPair` second field with bad size in {inner:#?}",
230 );
231 assert_eq!(
232 field2.align.abi, align2,
233 "`ScalarPair` second field with bad align in {inner:#?}",
234 );
235 assert_matches!(
236 field2.backend_repr,
237 BackendRepr::Scalar(_),
238 "`ScalarPair` second field with bad ABI in {inner:#?}",
239 );
240 }
241 BackendRepr::SimdVector { element, count } => {
242 let align = layout.align.abi;
243 let size = layout.size;
244 let element_align = element.align(cx).abi;
245 let element_size = element.size(cx);
246 assert!(align >= element_align);
248 assert!(size == (element_size * count).align_to(align));
250 }
251 BackendRepr::Memory { .. } => {} }
253 }
254
255 check_layout_abi(cx, layout);
256
257 match &layout.variants {
258 Variants::Empty => {
259 assert!(layout.is_uninhabited());
260 }
261 Variants::Single { index } => {
262 if let Some(variants) = layout.ty.variant_range(tcx) {
263 assert!(variants.contains(index));
264 } else {
265 assert!(index.as_u32() == 0);
267 }
268 }
269 Variants::Multiple { variants, tag, tag_encoding, .. } => {
270 if let TagEncoding::Niche { niche_start, untagged_variant, niche_variants } =
271 tag_encoding
272 {
273 let niche_size = tag.size(cx);
274 assert!(*niche_start <= niche_size.unsigned_int_max());
275 for (idx, variant) in variants.iter_enumerated() {
276 if !variant.is_uninhabited() {
278 assert!(idx == *untagged_variant || niche_variants.contains(&idx));
279 }
280 }
281 }
282 for variant in variants.iter() {
283 assert_matches!(variant.variants, Variants::Single { .. });
285 if variant.size > layout.size {
288 bug!(
289 "Type with size {} bytes has variant with size {} bytes: {layout:#?}",
290 layout.size.bytes(),
291 variant.size.bytes(),
292 )
293 }
294 if variant.align.abi > layout.align.abi {
295 bug!(
296 "Type with alignment {} bytes has variant with alignment {} bytes: {layout:#?}",
297 layout.align.abi.bytes(),
298 variant.align.abi.bytes(),
299 )
300 }
301 if variant.size == Size::ZERO
303 || variant.fields.count() == 0
304 || variant.is_uninhabited()
305 {
306 continue;
312 }
313 let scalar_coherent = |s1: Scalar, s2: Scalar| {
315 s1.size(cx) == s2.size(cx) && s1.align(cx) == s2.align(cx)
316 };
317 let abi_coherent = match (layout.backend_repr, variant.backend_repr) {
318 (BackendRepr::Scalar(s1), BackendRepr::Scalar(s2)) => scalar_coherent(s1, s2),
319 (BackendRepr::ScalarPair(a1, b1), BackendRepr::ScalarPair(a2, b2)) => {
320 scalar_coherent(a1, a2) && scalar_coherent(b1, b2)
321 }
322 (BackendRepr::Memory { .. }, _) => true,
323 _ => false,
324 };
325 if !abi_coherent {
326 bug!(
327 "Variant ABI is incompatible with top-level ABI:\nvariant={:#?}\nTop-level: {layout:#?}",
328 variant
329 );
330 }
331 }
332 }
333 }
334}