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    representability::{Representability, representability},
33    traits::StoredParamEnvAndCrate,
34};
35
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: ParamEnvAndCrate<'db>,
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(id.into());
145    let mut fields = fields.iter();
146    let Some(TyKind::Array(e_ty, e_len)) =
147        fields.next().filter(|_| fields.next().is_none()).map(|f| {
148            (*f.1).ty().instantiate(DbInterner::new_no_crate(db), args).skip_norm_wip().kind()
149        })
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.store(), env.store())?;
156
157    let cx = LayoutCx::new(dl);
158    Ok(Arc::new(cx.calc.simd_type(e_ly, e_len, repr_packed)?))
159}
160
161#[salsa::tracked(cycle_result = layout_of_ty_cycle_result, returns(clone))]
162pub fn layout_of_ty_query(
163    db: &dyn HirDatabase,
164    ty: StoredTy,
165    trait_env: StoredParamEnvAndCrate,
166) -> Result<Arc<Layout>, LayoutError> {
167    let krate = trait_env.krate;
168    let interner = DbInterner::new_with(db, krate);
169    let Ok(target) = db.target_data_layout(krate) else {
170        return Err(LayoutError::TargetLayoutNotAvailable);
171    };
172    let dl = target;
173    let cx = LayoutCx::new(dl);
174    let infer_ctxt = interner.infer_ctxt().build(TypingMode::PostAnalysis);
175    let cause = ObligationCause::dummy();
176    let ty = infer_ctxt
177        .at(&cause, trait_env.param_env(db))
178        .deeply_normalize(ty.as_ref())
179        .unwrap_or(ty.as_ref());
180    let result = match ty.kind() {
181        TyKind::Adt(def, args) => {
182            match def.def_id() {
183                hir_def::AdtId::StructId(s) => {
184                    let repr = AttrFlags::repr(db, s.into()).unwrap_or_default();
185                    if repr.simd() {
186                        return layout_of_simd_ty(
187                            db,
188                            s,
189                            repr.packed(),
190                            &args,
191                            trait_env.as_ref(db),
192                            target,
193                        );
194                    }
195                }
196                _ => {}
197            }
198            return db.layout_of_adt(def.def_id(), args.store(), trait_env);
199        }
200        TyKind::Bool => Layout::scalar(
201            dl,
202            Scalar::Initialized {
203                value: Primitive::Int(Integer::I8, false),
204                valid_range: WrappingRange { start: 0, end: 1 },
205            },
206        ),
207        TyKind::Char => Layout::scalar(
208            dl,
209            Scalar::Initialized {
210                value: Primitive::Int(Integer::I32, false),
211                valid_range: WrappingRange { start: 0, end: 0x10FFFF },
212            },
213        ),
214        TyKind::Int(i) => Layout::scalar(
215            dl,
216            scalar_unit(
217                dl,
218                Primitive::Int(
219                    match i {
220                        IntTy::Isize => dl.ptr_sized_integer(),
221                        IntTy::I8 => Integer::I8,
222                        IntTy::I16 => Integer::I16,
223                        IntTy::I32 => Integer::I32,
224                        IntTy::I64 => Integer::I64,
225                        IntTy::I128 => Integer::I128,
226                    },
227                    true,
228                ),
229            ),
230        ),
231        TyKind::Uint(i) => Layout::scalar(
232            dl,
233            scalar_unit(
234                dl,
235                Primitive::Int(
236                    match i {
237                        UintTy::Usize => dl.ptr_sized_integer(),
238                        UintTy::U8 => Integer::I8,
239                        UintTy::U16 => Integer::I16,
240                        UintTy::U32 => Integer::I32,
241                        UintTy::U64 => Integer::I64,
242                        UintTy::U128 => Integer::I128,
243                    },
244                    false,
245                ),
246            ),
247        ),
248        TyKind::Float(f) => Layout::scalar(
249            dl,
250            scalar_unit(
251                dl,
252                Primitive::Float(match f {
253                    FloatTy::F16 => Float::F16,
254                    FloatTy::F32 => Float::F32,
255                    FloatTy::F64 => Float::F64,
256                    FloatTy::F128 => Float::F128,
257                }),
258            ),
259        ),
260        TyKind::Tuple(tys) => {
261            let kind =
262                if tys.is_empty() { StructKind::AlwaysSized } else { StructKind::MaybeUnsized };
263
264            let fields = tys
265                .iter()
266                .map(|k| db.layout_of_ty(k.store(), trait_env.clone()))
267                .collect::<Result<Vec<_>, _>>()?;
268            let fields = fields.iter().map(|it| &**it).collect::<Vec<_>>();
269            let fields = fields.iter().collect::<IndexVec<_, _>>();
270            cx.calc.univariant(&fields, &ReprOptions::default(), kind)?
271        }
272        TyKind::Array(element, count) => {
273            let count = try_const_usize(db, count).ok_or(LayoutError::HasErrorConst)? as u64;
274            let element = db.layout_of_ty(element.store(), trait_env)?;
275            cx.calc.array_like::<_, _, ()>(&element, Some(count))?
276        }
277        TyKind::Slice(element) => {
278            let element = db.layout_of_ty(element.store(), trait_env)?;
279            cx.calc.array_like::<_, _, ()>(&element, None)?
280        }
281        TyKind::Str => {
282            let element = scalar_unit(dl, Primitive::Int(Integer::I8, false));
283            cx.calc.array_like::<_, _, ()>(&Layout::scalar(dl, element), None)?
284        }
285        // Potentially-wide pointers.
286        TyKind::Ref(_, pointee, _) | TyKind::RawPtr(pointee, _) => {
287            let mut data_ptr = scalar_unit(dl, Primitive::Pointer(AddressSpace::ZERO));
288            if matches!(ty.kind(), TyKind::Ref(..)) {
289                data_ptr.valid_range_mut().start = 1;
290            }
291
292            // FIXME(next-solver)
293            // let pointee = tcx.normalize_erasing_regions(param_env, pointee);
294            // if pointee.is_sized(tcx.at(DUMMY_SP), param_env) {
295            //     return Ok(tcx.mk_layout(LayoutS::scalar(cx, data_ptr)));
296            // }
297
298            let unsized_part = struct_tail_erasing_lifetimes(db, pointee);
299            // FIXME(next-solver)
300            /*
301            if let TyKind::AssociatedType(id, subst) = unsized_part.kind(Interner) {
302                unsized_part = TyKind::Alias(chalk_ir::AliasTy::Projection(ProjectionTy {
303                    associated_ty_id: *id,
304                    substitution: subst.clone(),
305                }))
306                .intern(Interner);
307            }
308            unsized_part = normalize(db, trait_env, unsized_part);
309            */
310            let metadata = match unsized_part.kind() {
311                TyKind::Slice(_) | TyKind::Str => {
312                    scalar_unit(dl, Primitive::Int(dl.ptr_sized_integer(), false))
313                }
314                TyKind::Dynamic(..) => {
315                    let mut vtable = scalar_unit(dl, Primitive::Pointer(AddressSpace::ZERO));
316                    vtable.valid_range_mut().start = 1;
317                    vtable
318                }
319                _ => {
320                    // pointee is sized
321                    return Ok(Arc::new(Layout::scalar(dl, data_ptr)));
322                }
323            };
324
325            // Effectively a (ptr, meta) tuple.
326            LayoutData::scalar_pair(dl, data_ptr, metadata)
327        }
328        TyKind::Never => LayoutData::never_type(dl),
329        TyKind::FnDef(..) => LayoutData::unit(dl, true),
330        TyKind::Dynamic(..) | TyKind::Foreign(_) => LayoutData::unit(dl, false),
331        TyKind::FnPtr(..) => {
332            let mut ptr = scalar_unit(dl, Primitive::Pointer(dl.instruction_address_space));
333            ptr.valid_range_mut().start = 1;
334            Layout::scalar(dl, ptr)
335        }
336        TyKind::Closure(_, args) => {
337            return db.layout_of_ty(args.as_closure().tupled_upvars_ty().store(), trait_env);
338        }
339        TyKind::Coroutine(_, args) => {
340            return db.layout_of_ty(args.as_coroutine().tupled_upvars_ty().store(), trait_env);
341        }
342        TyKind::CoroutineClosure(_, args) => {
343            return db
344                .layout_of_ty(args.as_coroutine_closure().tupled_upvars_ty().store(), trait_env);
345        }
346        TyKind::CoroutineWitness(_, _) => {
347            return Err(LayoutError::NotImplemented);
348        }
349
350        TyKind::Pat(ty, pat) => {
351            let mut layout = (*db.layout_of_ty(ty.store(), trait_env.clone())?).clone();
352            match pat.kind() {
353                PatternKind::Range { start, end } => {
354                    if let BackendRepr::Scalar(scalar) = &mut layout.backend_repr {
355                        scalar.valid_range_mut().start = extract_const_value(start)?
356                            .try_to_bits(db, trait_env.as_ref(db))
357                            .ok_or(LayoutError::Unknown)?;
358
359                        scalar.valid_range_mut().end = extract_const_value(end)?
360                            .try_to_bits(db, trait_env.as_ref(db))
361                            .ok_or(LayoutError::Unknown)?;
362
363                        // FIXME(pattern_types): create implied bounds from pattern types in signatures
364                        // that require that the range end is >= the range start so that we can't hit
365                        // this error anymore without first having hit a trait solver error.
366                        // Very fuzzy on the details here, but pattern types are an internal impl detail,
367                        // so we can just go with this for now
368                        if scalar.is_signed() {
369                            let range = scalar.valid_range_mut();
370                            let start = layout.size.sign_extend(range.start);
371                            let end = layout.size.sign_extend(range.end);
372                            if end < start {
373                                return Err(LayoutError::HasErrorType);
374                            }
375                        } else {
376                            let range = scalar.valid_range_mut();
377                            if range.end < range.start {
378                                return Err(LayoutError::HasErrorType);
379                            }
380                        };
381
382                        let niche = Niche {
383                            offset: Size::ZERO,
384                            value: scalar.primitive(),
385                            valid_range: scalar.valid_range(target),
386                        };
387
388                        layout.largest_niche = Some(niche);
389                    } else {
390                        panic!("pattern type with range but not scalar layout: {ty:?}, {layout:?}")
391                    }
392                }
393                PatternKind::NotNull => {
394                    if let BackendRepr::Scalar(scalar) | BackendRepr::ScalarPair(scalar, _) =
395                        &mut layout.backend_repr
396                    {
397                        scalar.valid_range_mut().start = 1;
398                        let niche = Niche {
399                            offset: Size::ZERO,
400                            value: scalar.primitive(),
401                            valid_range: scalar.valid_range(target),
402                        };
403
404                        layout.largest_niche = Some(niche);
405                    } else {
406                        panic!(
407                            "pattern type with `!null` pattern but not scalar/pair layout: {ty:?}, {layout:?}"
408                        )
409                    }
410                }
411
412                PatternKind::Or(variants) => match variants[0].kind() {
413                    PatternKind::Range { .. } => {
414                        if let BackendRepr::Scalar(scalar) = &mut layout.backend_repr {
415                            let variants: Result<Vec<_>, _> = variants
416                                .iter()
417                                .map(|pat| match pat.kind() {
418                                    PatternKind::Range { start, end } => Ok::<_, LayoutError>((
419                                        extract_const_value(start)?
420                                            .try_to_bits(db, trait_env.as_ref(db))
421                                            .ok_or(LayoutError::Unknown)?,
422                                        extract_const_value(end)?
423                                            .try_to_bits(db, trait_env.as_ref(db))
424                                            .ok_or(LayoutError::Unknown)?,
425                                    )),
426                                    PatternKind::NotNull | PatternKind::Or(_) => {
427                                        Err(LayoutError::Unknown)
428                                    }
429                                })
430                                .collect();
431                            let mut variants = variants?;
432                            if !scalar.is_signed() {
433                                return Err(LayoutError::HasErrorType);
434                            }
435                            variants.sort();
436                            if variants.len() != 2 {
437                                return Err(LayoutError::HasErrorType);
438                            }
439
440                            // first is the one starting at the signed in range min
441                            let mut first = variants[0];
442                            let mut second = variants[1];
443                            if second.0
444                                == layout.size.truncate(layout.size.signed_int_min() as u128)
445                            {
446                                (second, first) = (first, second);
447                            }
448
449                            if layout.size.sign_extend(first.1) >= layout.size.sign_extend(second.0)
450                            {
451                                return Err(LayoutError::HasErrorType);
452                            }
453                            if layout.size.signed_int_max() as u128 != second.1 {
454                                return Err(LayoutError::HasErrorType);
455                            }
456
457                            // Now generate a wrapping range (which aren't allowed in surface syntax).
458                            scalar.valid_range_mut().start = second.0;
459                            scalar.valid_range_mut().end = first.1;
460
461                            let niche = Niche {
462                                offset: Size::ZERO,
463                                value: scalar.primitive(),
464                                valid_range: scalar.valid_range(target),
465                            };
466
467                            layout.largest_niche = Some(niche);
468                        } else {
469                            panic!(
470                                "pattern type with range but not scalar layout: {ty:?}, {layout:?}"
471                            )
472                        }
473                    }
474                    PatternKind::NotNull => panic!("or patterns can't contain `!null` patterns"),
475                    PatternKind::Or(..) => panic!("patterns cannot have nested or patterns"),
476                },
477            }
478            // Pattern types contain their base as their sole field.
479            // This allows the rest of the compiler to process pattern types just like
480            // single field transparent Adts, and only the parts of the compiler that
481            // specifically care about pattern types will have to handle it.
482            layout.fields = FieldsShape::Arbitrary {
483                offsets: [Size::ZERO].into_iter().collect(),
484                in_memory_order: [RustcFieldIdx::new(0)].into_iter().collect(),
485            };
486            layout
487        }
488        TyKind::UnsafeBinder(_) => {
489            return Err(LayoutError::NotImplemented);
490        }
491
492        TyKind::Error(_) => return Err(LayoutError::HasErrorType),
493        TyKind::Placeholder(_)
494        | TyKind::Bound(..)
495        | TyKind::Infer(..)
496        | TyKind::Param(..)
497        | TyKind::Alias(..) => {
498            return Err(LayoutError::HasPlaceholder);
499        }
500    };
501    Ok(Arc::new(result))
502}
503
504fn layout_of_ty_cycle_result(
505    _: &dyn HirDatabase,
506    _: salsa::Id,
507    _: StoredTy,
508    _: StoredParamEnvAndCrate,
509) -> Result<Arc<Layout>, LayoutError> {
510    Err(LayoutError::RecursiveTypeWithoutIndirection)
511}
512
513fn extract_const_value<'db>(ct: Const<'db>) -> Result<ValueConst<'db>, LayoutError> {
514    match ct.kind() {
515        ConstKind::Value(cv) => Ok(cv),
516        ConstKind::Param(_)
517        | ConstKind::Expr(_)
518        | ConstKind::Unevaluated(_)
519        | ConstKind::Infer(_)
520        | ConstKind::Bound(..)
521        | ConstKind::Placeholder(_) => {
522            if ct.has_param() {
523                Err(LayoutError::HasPlaceholder)
524            } else {
525                Err(LayoutError::Unknown)
526            }
527        }
528        ConstKind::Error(_) => Err(LayoutError::HasErrorConst),
529    }
530}
531
532fn struct_tail_erasing_lifetimes<'a>(db: &'a dyn HirDatabase, pointee: Ty<'a>) -> Ty<'a> {
533    match pointee.kind() {
534        TyKind::Adt(def, args) => {
535            let struct_id = match def.def_id() {
536                AdtId::StructId(id) => id,
537                _ => return pointee,
538            };
539            if representability(db, def.def_id()) == Representability::Infinite {
540                return pointee;
541            }
542            let data = struct_id.fields(db);
543            let mut it = data.fields().iter().rev();
544            match it.next() {
545                Some((f, _)) => {
546                    let last_field_ty = field_ty(db, struct_id.into(), f, args);
547                    struct_tail_erasing_lifetimes(db, last_field_ty)
548                }
549                None => pointee,
550            }
551        }
552        TyKind::Tuple(tys) => {
553            if let Some(last_field_ty) = tys.iter().next_back() {
554                struct_tail_erasing_lifetimes(db, last_field_ty)
555            } else {
556                pointee
557            }
558        }
559        _ => pointee,
560    }
561}
562
563fn field_ty<'a>(
564    db: &'a dyn HirDatabase,
565    def: hir_def::VariantId,
566    fd: LocalFieldId,
567    args: GenericArgs<'a>,
568) -> Ty<'a> {
569    db.field_types(def)[fd].ty().instantiate(DbInterner::new_no_crate(db), args).skip_norm_wip()
570}
571
572fn scalar_unit(dl: &TargetDataLayout, value: Primitive) -> Scalar {
573    Scalar::Initialized { value, valid_range: WrappingRange::full(value.size(dl)) }
574}