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, Float, Integer, LayoutCalculator, Primitive, ReprOptions, Scalar, StructKind,
14    TargetDataLayout, WrappingRange,
15};
16use rustc_index::IndexVec;
17use rustc_type_ir::{
18    FloatTy, IntTy, UintTy,
19    inherent::{IntoKind, SliceLike},
20};
21use triomphe::Arc;
22
23use crate::{
24    InferenceResult, ParamEnvAndCrate,
25    consteval::try_const_usize,
26    db::HirDatabase,
27    next_solver::{
28        DbInterner, GenericArgs, Ty, TyKind, TypingMode,
29        infer::{DbInternerInferExt, traits::ObligationCause},
30    },
31};
32
33pub(crate) use self::adt::layout_of_adt_cycle_result;
34pub use self::{adt::layout_of_adt_query, target::target_data_layout_query};
35
36pub(crate) mod adt;
37pub(crate) mod target;
38
39#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
40pub struct RustcEnumVariantIdx(pub usize);
41
42impl rustc_index::Idx for RustcEnumVariantIdx {
43    fn new(idx: usize) -> Self {
44        RustcEnumVariantIdx(idx)
45    }
46
47    fn index(self) -> usize {
48        self.0
49    }
50}
51
52#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
53pub struct RustcFieldIdx(pub LocalFieldId);
54
55impl RustcFieldIdx {
56    pub fn new(idx: usize) -> Self {
57        RustcFieldIdx(Idx::from_raw(RawIdx::from(idx as u32)))
58    }
59}
60
61impl rustc_index::Idx for RustcFieldIdx {
62    fn new(idx: usize) -> Self {
63        RustcFieldIdx(Idx::from_raw(RawIdx::from(idx as u32)))
64    }
65
66    fn index(self) -> usize {
67        u32::from(self.0.into_raw()) as usize
68    }
69}
70
71pub type Layout = LayoutData<RustcFieldIdx, RustcEnumVariantIdx>;
72pub type TagEncoding = hir_def::layout::TagEncoding<RustcEnumVariantIdx>;
73pub type Variants = hir_def::layout::Variants<RustcFieldIdx, RustcEnumVariantIdx>;
74
75#[derive(Debug, PartialEq, Eq, Clone)]
76pub enum LayoutError {
77    // FIXME: Remove more variants once they get added to LayoutCalculatorError
78    BadCalc(LayoutCalculatorError<()>),
79    HasErrorConst,
80    HasErrorType,
81    HasPlaceholder,
82    InvalidSimdType,
83    NotImplemented,
84    RecursiveTypeWithoutIndirection,
85    TargetLayoutNotAvailable,
86    Unknown,
87    UserReprTooSmall,
88}
89
90impl std::error::Error for LayoutError {}
91impl fmt::Display for LayoutError {
92    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
93        match self {
94            LayoutError::BadCalc(err) => err.fallback_fmt(f),
95            LayoutError::HasErrorConst => write!(f, "type contains an unevaluatable const"),
96            LayoutError::HasErrorType => write!(f, "type contains an error"),
97            LayoutError::HasPlaceholder => write!(f, "type contains placeholders"),
98            LayoutError::InvalidSimdType => write!(f, "invalid simd type definition"),
99            LayoutError::NotImplemented => write!(f, "not implemented"),
100            LayoutError::RecursiveTypeWithoutIndirection => {
101                write!(f, "recursive type without indirection")
102            }
103            LayoutError::TargetLayoutNotAvailable => write!(f, "target layout not available"),
104            LayoutError::Unknown => write!(f, "unknown"),
105            LayoutError::UserReprTooSmall => {
106                write!(f, "the `#[repr]` hint is too small to hold the discriminants of the enum")
107            }
108        }
109    }
110}
111
112impl<F> From<LayoutCalculatorError<F>> for LayoutError {
113    fn from(err: LayoutCalculatorError<F>) -> Self {
114        LayoutError::BadCalc(err.without_payload())
115    }
116}
117
118struct LayoutCx<'a> {
119    calc: LayoutCalculator<&'a TargetDataLayout>,
120}
121
122impl<'a> LayoutCx<'a> {
123    fn new(target: &'a TargetDataLayout) -> Self {
124        Self { calc: LayoutCalculator::new(target) }
125    }
126}
127
128// FIXME: move this to the `rustc_abi`.
129fn layout_of_simd_ty<'db>(
130    db: &'db dyn HirDatabase,
131    id: StructId,
132    repr_packed: bool,
133    args: &GenericArgs<'db>,
134    env: ParamEnvAndCrate<'db>,
135    dl: &TargetDataLayout,
136) -> Result<Arc<Layout>, LayoutError> {
137    // Supported SIMD vectors are homogeneous ADTs with exactly one array field:
138    //
139    // * #[repr(simd)] struct S([T; 4])
140    //
141    // where T is a primitive scalar (integer/float/pointer).
142    let fields = db.field_types(id.into());
143    let mut fields = fields.iter();
144    let Some(TyKind::Array(e_ty, e_len)) = fields
145        .next()
146        .filter(|_| fields.next().is_none())
147        .map(|f| (*f.1).instantiate(DbInterner::new_no_crate(db), args).kind())
148    else {
149        return Err(LayoutError::InvalidSimdType);
150    };
151
152    let e_len = try_const_usize(db, e_len).ok_or(LayoutError::HasErrorConst)? as u64;
153    let e_ly = db.layout_of_ty(e_ty, env)?;
154
155    let cx = LayoutCx::new(dl);
156    Ok(Arc::new(cx.calc.simd_type(e_ly, e_len, repr_packed)?))
157}
158
159pub fn layout_of_ty_query<'db>(
160    db: &'db dyn HirDatabase,
161    ty: Ty<'db>,
162    trait_env: ParamEnvAndCrate<'db>,
163) -> Result<Arc<Layout>, LayoutError> {
164    let krate = trait_env.krate;
165    let interner = DbInterner::new_with(db, krate);
166    let Ok(target) = db.target_data_layout(krate) else {
167        return Err(LayoutError::TargetLayoutNotAvailable);
168    };
169    let dl = &*target;
170    let cx = LayoutCx::new(dl);
171    let infer_ctxt = interner.infer_ctxt().build(TypingMode::PostAnalysis);
172    let cause = ObligationCause::dummy();
173    let ty = infer_ctxt.at(&cause, trait_env.param_env).deeply_normalize(ty).unwrap_or(ty);
174    let result = match ty.kind() {
175        TyKind::Adt(def, args) => {
176            match def.inner().id {
177                hir_def::AdtId::StructId(s) => {
178                    let repr = AttrFlags::repr(db, s.into()).unwrap_or_default();
179                    if repr.simd() {
180                        return layout_of_simd_ty(db, s, repr.packed(), &args, trait_env, &target);
181                    }
182                }
183                _ => {}
184            }
185            return db.layout_of_adt(def.inner().id, args, trait_env);
186        }
187        TyKind::Bool => Layout::scalar(
188            dl,
189            Scalar::Initialized {
190                value: Primitive::Int(Integer::I8, false),
191                valid_range: WrappingRange { start: 0, end: 1 },
192            },
193        ),
194        TyKind::Char => Layout::scalar(
195            dl,
196            Scalar::Initialized {
197                value: Primitive::Int(Integer::I32, false),
198                valid_range: WrappingRange { start: 0, end: 0x10FFFF },
199            },
200        ),
201        TyKind::Int(i) => Layout::scalar(
202            dl,
203            scalar_unit(
204                dl,
205                Primitive::Int(
206                    match i {
207                        IntTy::Isize => dl.ptr_sized_integer(),
208                        IntTy::I8 => Integer::I8,
209                        IntTy::I16 => Integer::I16,
210                        IntTy::I32 => Integer::I32,
211                        IntTy::I64 => Integer::I64,
212                        IntTy::I128 => Integer::I128,
213                    },
214                    true,
215                ),
216            ),
217        ),
218        TyKind::Uint(i) => Layout::scalar(
219            dl,
220            scalar_unit(
221                dl,
222                Primitive::Int(
223                    match i {
224                        UintTy::Usize => dl.ptr_sized_integer(),
225                        UintTy::U8 => Integer::I8,
226                        UintTy::U16 => Integer::I16,
227                        UintTy::U32 => Integer::I32,
228                        UintTy::U64 => Integer::I64,
229                        UintTy::U128 => Integer::I128,
230                    },
231                    false,
232                ),
233            ),
234        ),
235        TyKind::Float(f) => Layout::scalar(
236            dl,
237            scalar_unit(
238                dl,
239                Primitive::Float(match f {
240                    FloatTy::F16 => Float::F16,
241                    FloatTy::F32 => Float::F32,
242                    FloatTy::F64 => Float::F64,
243                    FloatTy::F128 => Float::F128,
244                }),
245            ),
246        ),
247        TyKind::Tuple(tys) => {
248            let kind =
249                if tys.len() == 0 { StructKind::AlwaysSized } else { StructKind::MaybeUnsized };
250
251            let fields =
252                tys.iter().map(|k| db.layout_of_ty(k, trait_env)).collect::<Result<Vec<_>, _>>()?;
253            let fields = fields.iter().map(|it| &**it).collect::<Vec<_>>();
254            let fields = fields.iter().collect::<IndexVec<_, _>>();
255            cx.calc.univariant(&fields, &ReprOptions::default(), kind)?
256        }
257        TyKind::Array(element, count) => {
258            let count = try_const_usize(db, count).ok_or(LayoutError::HasErrorConst)? as u64;
259            let element = db.layout_of_ty(element, trait_env)?;
260            cx.calc.array_like::<_, _, ()>(&element, Some(count))?
261        }
262        TyKind::Slice(element) => {
263            let element = db.layout_of_ty(element, trait_env)?;
264            cx.calc.array_like::<_, _, ()>(&element, None)?
265        }
266        TyKind::Str => {
267            let element = scalar_unit(dl, Primitive::Int(Integer::I8, false));
268            cx.calc.array_like::<_, _, ()>(&Layout::scalar(dl, element), None)?
269        }
270        // Potentially-wide pointers.
271        TyKind::Ref(_, pointee, _) | TyKind::RawPtr(pointee, _) => {
272            let mut data_ptr = scalar_unit(dl, Primitive::Pointer(AddressSpace::ZERO));
273            if matches!(ty.kind(), TyKind::Ref(..)) {
274                data_ptr.valid_range_mut().start = 1;
275            }
276
277            // FIXME(next-solver)
278            // let pointee = tcx.normalize_erasing_regions(param_env, pointee);
279            // if pointee.is_sized(tcx.at(DUMMY_SP), param_env) {
280            //     return Ok(tcx.mk_layout(LayoutS::scalar(cx, data_ptr)));
281            // }
282
283            let unsized_part = struct_tail_erasing_lifetimes(db, pointee);
284            // FIXME(next-solver)
285            /*
286            if let TyKind::AssociatedType(id, subst) = unsized_part.kind(Interner) {
287                unsized_part = TyKind::Alias(chalk_ir::AliasTy::Projection(ProjectionTy {
288                    associated_ty_id: *id,
289                    substitution: subst.clone(),
290                }))
291                .intern(Interner);
292            }
293            unsized_part = normalize(db, trait_env, unsized_part);
294            */
295            let metadata = match unsized_part.kind() {
296                TyKind::Slice(_) | TyKind::Str => {
297                    scalar_unit(dl, Primitive::Int(dl.ptr_sized_integer(), false))
298                }
299                TyKind::Dynamic(..) => {
300                    let mut vtable = scalar_unit(dl, Primitive::Pointer(AddressSpace::ZERO));
301                    vtable.valid_range_mut().start = 1;
302                    vtable
303                }
304                _ => {
305                    // pointee is sized
306                    return Ok(Arc::new(Layout::scalar(dl, data_ptr)));
307                }
308            };
309
310            // Effectively a (ptr, meta) tuple.
311            LayoutData::scalar_pair(dl, data_ptr, metadata)
312        }
313        TyKind::Never => LayoutData::never_type(dl),
314        TyKind::FnDef(..) => LayoutData::unit(dl, true),
315        TyKind::Dynamic(..) | TyKind::Foreign(_) => LayoutData::unit(dl, false),
316        TyKind::FnPtr(..) => {
317            let mut ptr = scalar_unit(dl, Primitive::Pointer(dl.instruction_address_space));
318            ptr.valid_range_mut().start = 1;
319            Layout::scalar(dl, ptr)
320        }
321        TyKind::Closure(id, args) => {
322            let def = db.lookup_intern_closure(id.0);
323            let infer = InferenceResult::for_body(db, def.0);
324            let (captures, _) = infer.closure_info(id.0);
325            let fields = captures
326                .iter()
327                .map(|it| {
328                    let ty =
329                        it.ty.instantiate(interner, args.split_closure_args_untupled().parent_args);
330                    db.layout_of_ty(ty, trait_env)
331                })
332                .collect::<Result<Vec<_>, _>>()?;
333            let fields = fields.iter().map(|it| &**it).collect::<Vec<_>>();
334            let fields = fields.iter().collect::<IndexVec<_, _>>();
335            cx.calc.univariant(&fields, &ReprOptions::default(), StructKind::AlwaysSized)?
336        }
337
338        TyKind::Coroutine(_, _)
339        | TyKind::CoroutineWitness(_, _)
340        | TyKind::CoroutineClosure(_, _) => {
341            return Err(LayoutError::NotImplemented);
342        }
343
344        TyKind::Pat(_, _) | TyKind::UnsafeBinder(_) => {
345            return Err(LayoutError::NotImplemented);
346        }
347
348        TyKind::Error(_) => return Err(LayoutError::HasErrorType),
349        TyKind::Placeholder(_)
350        | TyKind::Bound(..)
351        | TyKind::Infer(..)
352        | TyKind::Param(..)
353        | TyKind::Alias(..) => {
354            return Err(LayoutError::HasPlaceholder);
355        }
356    };
357    Ok(Arc::new(result))
358}
359
360pub(crate) fn layout_of_ty_cycle_result<'db>(
361    _: &dyn HirDatabase,
362    _: Ty<'db>,
363    _: ParamEnvAndCrate<'db>,
364) -> Result<Arc<Layout>, LayoutError> {
365    Err(LayoutError::RecursiveTypeWithoutIndirection)
366}
367
368fn struct_tail_erasing_lifetimes<'a>(db: &'a dyn HirDatabase, pointee: Ty<'a>) -> Ty<'a> {
369    match pointee.kind() {
370        TyKind::Adt(def, args) => {
371            let struct_id = match def.inner().id {
372                AdtId::StructId(id) => id,
373                _ => return pointee,
374            };
375            let data = struct_id.fields(db);
376            let mut it = data.fields().iter().rev();
377            match it.next() {
378                Some((f, _)) => {
379                    let last_field_ty = field_ty(db, struct_id.into(), f, &args);
380                    struct_tail_erasing_lifetimes(db, last_field_ty)
381                }
382                None => pointee,
383            }
384        }
385        TyKind::Tuple(tys) => {
386            if let Some(last_field_ty) = tys.iter().next_back() {
387                struct_tail_erasing_lifetimes(db, last_field_ty)
388            } else {
389                pointee
390            }
391        }
392        _ => pointee,
393    }
394}
395
396fn field_ty<'a>(
397    db: &'a dyn HirDatabase,
398    def: hir_def::VariantId,
399    fd: LocalFieldId,
400    args: &GenericArgs<'a>,
401) -> Ty<'a> {
402    db.field_types(def)[fd].instantiate(DbInterner::new_no_crate(db), args)
403}
404
405fn scalar_unit(dl: &TargetDataLayout, value: Primitive) -> Scalar {
406    Scalar::Initialized { value, valid_range: WrappingRange::full(value.size(dl)) }
407}
408
409#[cfg(test)]
410mod tests;