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