Skip to main content

hir_ty/
layout.rs

1//! Compute the binary representation of a type
2
3use std::fmt;
4
5use hir_def::{
6    AdtId, LocalFieldId, StructId,
7    attrs::AttrFlags,
8    layout::{LayoutCalculatorError, LayoutData},
9};
10use la_arena::{Idx, RawIdx};
11
12use rustc_abi::{
13    AddressSpace, BackendRepr, FieldsShape, Float, Integer, LayoutCalculator, Niche, Primitive,
14    ReprOptions, Scalar, Size, StructKind, TargetDataLayout, WrappingRange,
15};
16use rustc_index::IndexVec;
17use rustc_type_ir::{
18    FloatTy, IntTy, TypeVisitableExt as _, UintTy,
19    inherent::{GenericArgs as _, IntoKind},
20};
21use triomphe::Arc;
22
23use crate::{
24    ParamEnvAndCrate,
25    consteval::try_const_usize,
26    db::HirDatabase,
27    next_solver::{
28        Const, ConstKind, DbInterner, GenericArgs, PatternKind, StoredTy, Ty, TyKind, TypingMode,
29        ValueConst,
30        infer::{DbInternerInferExt, traits::ObligationCause},
31    },
32    traits::StoredParamEnvAndCrate,
33};
34
35pub use self::{adt::layout_of_adt_query, target::target_data_layout_query};
36
37pub(crate) mod adt;
38pub(crate) mod target;
39
40#[cfg(test)]
41mod tests;
42
43#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
44pub struct RustcEnumVariantIdx(pub usize);
45
46impl rustc_index::Idx for RustcEnumVariantIdx {
47    fn new(idx: usize) -> Self {
48        RustcEnumVariantIdx(idx)
49    }
50
51    fn index(self) -> usize {
52        self.0
53    }
54}
55
56#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
57pub struct RustcFieldIdx(pub LocalFieldId);
58
59impl RustcFieldIdx {
60    pub fn new(idx: usize) -> Self {
61        RustcFieldIdx(Idx::from_raw(RawIdx::from(idx as u32)))
62    }
63}
64
65impl rustc_index::Idx for RustcFieldIdx {
66    fn new(idx: usize) -> Self {
67        RustcFieldIdx(Idx::from_raw(RawIdx::from(idx as u32)))
68    }
69
70    fn index(self) -> usize {
71        u32::from(self.0.into_raw()) as usize
72    }
73}
74
75pub type Layout = LayoutData<RustcFieldIdx, RustcEnumVariantIdx>;
76pub type TagEncoding = hir_def::layout::TagEncoding<RustcEnumVariantIdx>;
77pub type Variants = hir_def::layout::Variants<RustcFieldIdx, RustcEnumVariantIdx>;
78
79#[derive(Debug, PartialEq, Eq, Clone)]
80pub enum LayoutError {
81    // FIXME: Remove more variants once they get added to LayoutCalculatorError
82    BadCalc(LayoutCalculatorError<()>),
83    HasErrorConst,
84    HasErrorType,
85    HasPlaceholder,
86    InvalidSimdType,
87    NotImplemented,
88    RecursiveTypeWithoutIndirection,
89    TargetLayoutNotAvailable,
90    Unknown,
91    UserReprTooSmall,
92}
93
94impl std::error::Error for LayoutError {}
95impl fmt::Display for LayoutError {
96    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
97        match self {
98            LayoutError::BadCalc(err) => err.fallback_fmt(f),
99            LayoutError::HasErrorConst => write!(f, "type contains an unevaluatable const"),
100            LayoutError::HasErrorType => write!(f, "type contains an error"),
101            LayoutError::HasPlaceholder => write!(f, "type contains placeholders"),
102            LayoutError::InvalidSimdType => write!(f, "invalid simd type definition"),
103            LayoutError::NotImplemented => write!(f, "not implemented"),
104            LayoutError::RecursiveTypeWithoutIndirection => {
105                write!(f, "recursive type without indirection")
106            }
107            LayoutError::TargetLayoutNotAvailable => write!(f, "target layout not available"),
108            LayoutError::Unknown => write!(f, "unknown"),
109            LayoutError::UserReprTooSmall => {
110                write!(f, "the `#[repr]` hint is too small to hold the discriminants of the enum")
111            }
112        }
113    }
114}
115
116impl<F> From<LayoutCalculatorError<F>> for LayoutError {
117    fn from(err: LayoutCalculatorError<F>) -> Self {
118        LayoutError::BadCalc(err.without_payload())
119    }
120}
121
122struct LayoutCx<'a> {
123    calc: LayoutCalculator<&'a TargetDataLayout>,
124}
125
126impl<'a> LayoutCx<'a> {
127    fn new(target: &'a TargetDataLayout) -> Self {
128        Self { calc: LayoutCalculator::new(target) }
129    }
130}
131
132// FIXME: move this to the `rustc_abi`.
133fn layout_of_simd_ty<'db>(
134    db: &'db dyn HirDatabase,
135    id: StructId,
136    repr_packed: bool,
137    args: &GenericArgs<'db>,
138    env: ParamEnvAndCrate<'db>,
139    dl: &TargetDataLayout,
140) -> Result<Arc<Layout>, LayoutError> {
141    // Supported SIMD vectors are homogeneous ADTs with exactly one array field:
142    //
143    // * #[repr(simd)] struct S([T; 4])
144    //
145    // where T is a primitive scalar (integer/float/pointer).
146    let fields = db.field_types(id.into());
147    let mut fields = fields.iter();
148    let Some(TyKind::Array(e_ty, e_len)) =
149        fields.next().filter(|_| fields.next().is_none()).map(|f| {
150            (*f.1).ty().instantiate(DbInterner::new_no_crate(db), args).skip_norm_wip().kind()
151        })
152    else {
153        return Err(LayoutError::InvalidSimdType);
154    };
155
156    let e_len = try_const_usize(db, e_len).ok_or(LayoutError::HasErrorConst)? as u64;
157    let e_ly = db.layout_of_ty(e_ty.store(), env.store())?;
158
159    let cx = LayoutCx::new(dl);
160    Ok(Arc::new(cx.calc.simd_type(e_ly, e_len, repr_packed)?))
161}
162
163#[salsa::tracked(cycle_result = layout_of_ty_cycle_result)]
164pub fn layout_of_ty_query(
165    db: &dyn HirDatabase,
166    ty: StoredTy,
167    trait_env: StoredParamEnvAndCrate,
168) -> Result<Arc<Layout>, LayoutError> {
169    let krate = trait_env.krate;
170    let interner = DbInterner::new_with(db, krate);
171    let Ok(target) = db.target_data_layout(krate) else {
172        return Err(LayoutError::TargetLayoutNotAvailable);
173    };
174    let dl = target;
175    let cx = LayoutCx::new(dl);
176    let infer_ctxt = interner.infer_ctxt().build(TypingMode::PostAnalysis);
177    let cause = ObligationCause::dummy();
178    let ty = infer_ctxt
179        .at(&cause, trait_env.param_env(db))
180        .deeply_normalize(ty.as_ref())
181        .unwrap_or(ty.as_ref());
182    let result = match ty.kind() {
183        TyKind::Adt(def, args) => {
184            match def.def_id() {
185                hir_def::AdtId::StructId(s) => {
186                    let repr = AttrFlags::repr(db, s.into()).unwrap_or_default();
187                    if repr.simd() {
188                        return layout_of_simd_ty(
189                            db,
190                            s,
191                            repr.packed(),
192                            &args,
193                            trait_env.as_ref(db),
194                            target,
195                        );
196                    }
197                }
198                _ => {}
199            }
200            return db.layout_of_adt(def.def_id(), args.store(), trait_env);
201        }
202        TyKind::Bool => Layout::scalar(
203            dl,
204            Scalar::Initialized {
205                value: Primitive::Int(Integer::I8, false),
206                valid_range: WrappingRange { start: 0, end: 1 },
207            },
208        ),
209        TyKind::Char => Layout::scalar(
210            dl,
211            Scalar::Initialized {
212                value: Primitive::Int(Integer::I32, false),
213                valid_range: WrappingRange { start: 0, end: 0x10FFFF },
214            },
215        ),
216        TyKind::Int(i) => Layout::scalar(
217            dl,
218            scalar_unit(
219                dl,
220                Primitive::Int(
221                    match i {
222                        IntTy::Isize => dl.ptr_sized_integer(),
223                        IntTy::I8 => Integer::I8,
224                        IntTy::I16 => Integer::I16,
225                        IntTy::I32 => Integer::I32,
226                        IntTy::I64 => Integer::I64,
227                        IntTy::I128 => Integer::I128,
228                    },
229                    true,
230                ),
231            ),
232        ),
233        TyKind::Uint(i) => Layout::scalar(
234            dl,
235            scalar_unit(
236                dl,
237                Primitive::Int(
238                    match i {
239                        UintTy::Usize => dl.ptr_sized_integer(),
240                        UintTy::U8 => Integer::I8,
241                        UintTy::U16 => Integer::I16,
242                        UintTy::U32 => Integer::I32,
243                        UintTy::U64 => Integer::I64,
244                        UintTy::U128 => Integer::I128,
245                    },
246                    false,
247                ),
248            ),
249        ),
250        TyKind::Float(f) => Layout::scalar(
251            dl,
252            scalar_unit(
253                dl,
254                Primitive::Float(match f {
255                    FloatTy::F16 => Float::F16,
256                    FloatTy::F32 => Float::F32,
257                    FloatTy::F64 => Float::F64,
258                    FloatTy::F128 => Float::F128,
259                }),
260            ),
261        ),
262        TyKind::Tuple(tys) => {
263            let kind =
264                if tys.is_empty() { StructKind::AlwaysSized } else { StructKind::MaybeUnsized };
265
266            let fields = tys
267                .iter()
268                .map(|k| db.layout_of_ty(k.store(), trait_env.clone()))
269                .collect::<Result<Vec<_>, _>>()?;
270            let fields = fields.iter().map(|it| &**it).collect::<Vec<_>>();
271            let fields = fields.iter().collect::<IndexVec<_, _>>();
272            cx.calc.univariant(&fields, &ReprOptions::default(), kind)?
273        }
274        TyKind::Array(element, count) => {
275            let count = try_const_usize(db, count).ok_or(LayoutError::HasErrorConst)? as u64;
276            let element = db.layout_of_ty(element.store(), trait_env)?;
277            cx.calc.array_like::<_, _, ()>(&element, Some(count))?
278        }
279        TyKind::Slice(element) => {
280            let element = db.layout_of_ty(element.store(), trait_env)?;
281            cx.calc.array_like::<_, _, ()>(&element, None)?
282        }
283        TyKind::Str => {
284            let element = scalar_unit(dl, Primitive::Int(Integer::I8, false));
285            cx.calc.array_like::<_, _, ()>(&Layout::scalar(dl, element), None)?
286        }
287        // Potentially-wide pointers.
288        TyKind::Ref(_, pointee, _) | TyKind::RawPtr(pointee, _) => {
289            let mut data_ptr = scalar_unit(dl, Primitive::Pointer(AddressSpace::ZERO));
290            if matches!(ty.kind(), TyKind::Ref(..)) {
291                data_ptr.valid_range_mut().start = 1;
292            }
293
294            // FIXME(next-solver)
295            // let pointee = tcx.normalize_erasing_regions(param_env, pointee);
296            // if pointee.is_sized(tcx.at(DUMMY_SP), param_env) {
297            //     return Ok(tcx.mk_layout(LayoutS::scalar(cx, data_ptr)));
298            // }
299
300            let unsized_part = struct_tail_erasing_lifetimes(db, pointee);
301            // FIXME(next-solver)
302            /*
303            if let TyKind::AssociatedType(id, subst) = unsized_part.kind(Interner) {
304                unsized_part = TyKind::Alias(chalk_ir::AliasTy::Projection(ProjectionTy {
305                    associated_ty_id: *id,
306                    substitution: subst.clone(),
307                }))
308                .intern(Interner);
309            }
310            unsized_part = normalize(db, trait_env, unsized_part);
311            */
312            let metadata = match unsized_part.kind() {
313                TyKind::Slice(_) | TyKind::Str => {
314                    scalar_unit(dl, Primitive::Int(dl.ptr_sized_integer(), false))
315                }
316                TyKind::Dynamic(..) => {
317                    let mut vtable = scalar_unit(dl, Primitive::Pointer(AddressSpace::ZERO));
318                    vtable.valid_range_mut().start = 1;
319                    vtable
320                }
321                _ => {
322                    // pointee is sized
323                    return Ok(Arc::new(Layout::scalar(dl, data_ptr)));
324                }
325            };
326
327            // Effectively a (ptr, meta) tuple.
328            LayoutData::scalar_pair(dl, data_ptr, metadata)
329        }
330        TyKind::Never => LayoutData::never_type(dl),
331        TyKind::FnDef(..) => LayoutData::unit(dl, true),
332        TyKind::Dynamic(..) | TyKind::Foreign(_) => LayoutData::unit(dl, false),
333        TyKind::FnPtr(..) => {
334            let mut ptr = scalar_unit(dl, Primitive::Pointer(dl.instruction_address_space));
335            ptr.valid_range_mut().start = 1;
336            Layout::scalar(dl, ptr)
337        }
338        TyKind::Closure(_, args) => {
339            return db.layout_of_ty(args.as_closure().tupled_upvars_ty().store(), trait_env);
340        }
341        TyKind::Coroutine(_, args) => {
342            return db.layout_of_ty(args.as_coroutine().tupled_upvars_ty().store(), trait_env);
343        }
344        TyKind::CoroutineClosure(_, args) => {
345            return db
346                .layout_of_ty(args.as_coroutine_closure().tupled_upvars_ty().store(), trait_env);
347        }
348        TyKind::CoroutineWitness(_, _) => {
349            return Err(LayoutError::NotImplemented);
350        }
351
352        TyKind::Pat(ty, pat) => {
353            let mut layout = (*db.layout_of_ty(ty.store(), trait_env.clone())?).clone();
354            match pat.kind() {
355                PatternKind::Range { start, end } => {
356                    if let BackendRepr::Scalar(scalar) = &mut layout.backend_repr {
357                        scalar.valid_range_mut().start = extract_const_value(start)?
358                            .try_to_bits(db, trait_env.as_ref(db))
359                            .ok_or(LayoutError::Unknown)?;
360
361                        scalar.valid_range_mut().end = extract_const_value(end)?
362                            .try_to_bits(db, trait_env.as_ref(db))
363                            .ok_or(LayoutError::Unknown)?;
364
365                        // FIXME(pattern_types): create implied bounds from pattern types in signatures
366                        // that require that the range end is >= the range start so that we can't hit
367                        // this error anymore without first having hit a trait solver error.
368                        // Very fuzzy on the details here, but pattern types are an internal impl detail,
369                        // so we can just go with this for now
370                        if scalar.is_signed() {
371                            let range = scalar.valid_range_mut();
372                            let start = layout.size.sign_extend(range.start);
373                            let end = layout.size.sign_extend(range.end);
374                            if end < start {
375                                return Err(LayoutError::HasErrorType);
376                            }
377                        } else {
378                            let range = scalar.valid_range_mut();
379                            if range.end < range.start {
380                                return Err(LayoutError::HasErrorType);
381                            }
382                        };
383
384                        let niche = Niche {
385                            offset: Size::ZERO,
386                            value: scalar.primitive(),
387                            valid_range: scalar.valid_range(target),
388                        };
389
390                        layout.largest_niche = Some(niche);
391                    } else {
392                        panic!("pattern type with range but not scalar layout: {ty:?}, {layout:?}")
393                    }
394                }
395                PatternKind::NotNull => {
396                    if let BackendRepr::Scalar(scalar) | BackendRepr::ScalarPair(scalar, _) =
397                        &mut layout.backend_repr
398                    {
399                        scalar.valid_range_mut().start = 1;
400                        let niche = Niche {
401                            offset: Size::ZERO,
402                            value: scalar.primitive(),
403                            valid_range: scalar.valid_range(target),
404                        };
405
406                        layout.largest_niche = Some(niche);
407                    } else {
408                        panic!(
409                            "pattern type with `!null` pattern but not scalar/pair layout: {ty:?}, {layout:?}"
410                        )
411                    }
412                }
413
414                PatternKind::Or(variants) => match variants[0].kind() {
415                    PatternKind::Range { .. } => {
416                        if let BackendRepr::Scalar(scalar) = &mut layout.backend_repr {
417                            let variants: Result<Vec<_>, _> = variants
418                                .iter()
419                                .map(|pat| match pat.kind() {
420                                    PatternKind::Range { start, end } => Ok::<_, LayoutError>((
421                                        extract_const_value(start)?
422                                            .try_to_bits(db, trait_env.as_ref(db))
423                                            .ok_or(LayoutError::Unknown)?,
424                                        extract_const_value(end)?
425                                            .try_to_bits(db, trait_env.as_ref(db))
426                                            .ok_or(LayoutError::Unknown)?,
427                                    )),
428                                    PatternKind::NotNull | PatternKind::Or(_) => {
429                                        Err(LayoutError::Unknown)
430                                    }
431                                })
432                                .collect();
433                            let mut variants = variants?;
434                            if !scalar.is_signed() {
435                                return Err(LayoutError::HasErrorType);
436                            }
437                            variants.sort();
438                            if variants.len() != 2 {
439                                return Err(LayoutError::HasErrorType);
440                            }
441
442                            // first is the one starting at the signed in range min
443                            let mut first = variants[0];
444                            let mut second = variants[1];
445                            if second.0
446                                == layout.size.truncate(layout.size.signed_int_min() as u128)
447                            {
448                                (second, first) = (first, second);
449                            }
450
451                            if layout.size.sign_extend(first.1) >= layout.size.sign_extend(second.0)
452                            {
453                                return Err(LayoutError::HasErrorType);
454                            }
455                            if layout.size.signed_int_max() as u128 != second.1 {
456                                return Err(LayoutError::HasErrorType);
457                            }
458
459                            // Now generate a wrapping range (which aren't allowed in surface syntax).
460                            scalar.valid_range_mut().start = second.0;
461                            scalar.valid_range_mut().end = first.1;
462
463                            let niche = Niche {
464                                offset: Size::ZERO,
465                                value: scalar.primitive(),
466                                valid_range: scalar.valid_range(target),
467                            };
468
469                            layout.largest_niche = Some(niche);
470                        } else {
471                            panic!(
472                                "pattern type with range but not scalar layout: {ty:?}, {layout:?}"
473                            )
474                        }
475                    }
476                    PatternKind::NotNull => panic!("or patterns can't contain `!null` patterns"),
477                    PatternKind::Or(..) => panic!("patterns cannot have nested or patterns"),
478                },
479            }
480            // Pattern types contain their base as their sole field.
481            // This allows the rest of the compiler to process pattern types just like
482            // single field transparent Adts, and only the parts of the compiler that
483            // specifically care about pattern types will have to handle it.
484            layout.fields = FieldsShape::Arbitrary {
485                offsets: [Size::ZERO].into_iter().collect(),
486                in_memory_order: [RustcFieldIdx::new(0)].into_iter().collect(),
487            };
488            layout
489        }
490        TyKind::UnsafeBinder(_) => {
491            return Err(LayoutError::NotImplemented);
492        }
493
494        TyKind::Error(_) => return Err(LayoutError::HasErrorType),
495        TyKind::Placeholder(_)
496        | TyKind::Bound(..)
497        | TyKind::Infer(..)
498        | TyKind::Param(..)
499        | TyKind::Alias(..) => {
500            return Err(LayoutError::HasPlaceholder);
501        }
502    };
503    Ok(Arc::new(result))
504}
505
506fn layout_of_ty_cycle_result(
507    _: &dyn HirDatabase,
508    _: salsa::Id,
509    _: StoredTy,
510    _: StoredParamEnvAndCrate,
511) -> Result<Arc<Layout>, LayoutError> {
512    Err(LayoutError::RecursiveTypeWithoutIndirection)
513}
514
515fn extract_const_value<'db>(ct: Const<'db>) -> Result<ValueConst<'db>, LayoutError> {
516    match ct.kind() {
517        ConstKind::Value(cv) => Ok(cv),
518        ConstKind::Param(_)
519        | ConstKind::Expr(_)
520        | ConstKind::Unevaluated(_)
521        | ConstKind::Infer(_)
522        | ConstKind::Bound(..)
523        | ConstKind::Placeholder(_) => {
524            if ct.has_param() {
525                Err(LayoutError::HasPlaceholder)
526            } else {
527                Err(LayoutError::Unknown)
528            }
529        }
530        ConstKind::Error(_) => Err(LayoutError::HasErrorConst),
531    }
532}
533
534fn struct_tail_erasing_lifetimes<'a>(db: &'a dyn HirDatabase, pointee: Ty<'a>) -> Ty<'a> {
535    match pointee.kind() {
536        TyKind::Adt(def, args) => {
537            let struct_id = match def.def_id() {
538                AdtId::StructId(id) => id,
539                _ => return pointee,
540            };
541            let data = struct_id.fields(db);
542            let mut it = data.fields().iter().rev();
543            match it.next() {
544                Some((f, _)) => {
545                    let last_field_ty = field_ty(db, struct_id.into(), f, args);
546                    struct_tail_erasing_lifetimes(db, last_field_ty)
547                }
548                None => pointee,
549            }
550        }
551        TyKind::Tuple(tys) => {
552            if let Some(last_field_ty) = tys.iter().next_back() {
553                struct_tail_erasing_lifetimes(db, last_field_ty)
554            } else {
555                pointee
556            }
557        }
558        _ => pointee,
559    }
560}
561
562fn field_ty<'a>(
563    db: &'a dyn HirDatabase,
564    def: hir_def::VariantId,
565    fd: LocalFieldId,
566    args: GenericArgs<'a>,
567) -> Ty<'a> {
568    db.field_types(def)[fd].ty().instantiate(DbInterner::new_no_crate(db), args).skip_norm_wip()
569}
570
571fn scalar_unit(dl: &TargetDataLayout, value: Primitive) -> Scalar {
572    Scalar::Initialized { value, valid_range: WrappingRange::full(value.size(dl)) }
573}