Skip to main content

charon_lib/minirust/
types.rs

1use super::*;
2
3impl<T: mini::Target> TranslateCtx<'_, T> {
4    pub(super) fn ty(&self, span: Span, ty: &Ty) -> Result<mini::Type> {
5        Ok(match ty.kind() {
6            TyKind::Scalar(ScalarTy::Integer(integer)) => mini::Type::Int(self.int_type(*integer)),
7            TyKind::Scalar(ScalarTy::Bool) => mini::Type::Bool,
8            TyKind::Scalar(ScalarTy::Char) => mini::Type::Int(mini::IntType {
9                signed: mini::Signedness::Unsigned,
10                size: mini_size(4),
11            }),
12            TyKind::Scalar(ScalarTy::Float(_)) => {
13                raise!(span, "MiniRust has no floating-point types")
14            }
15            TyKind::Array(elem_ty, len, _) => mini::Type::Array {
16                elem: mini::GcCow::new(self.ty(span, elem_ty)?),
17                count: mini::Int::from(
18                    len.as_usize_literal()
19                        .ok_or("non-concrete array length")
20                        .context(span)?,
21                ),
22            },
23            TyKind::Slice(elem_ty, _) => mini::Type::Slice {
24                elem: mini::GcCow::new(self.ty(span, elem_ty)?),
25            },
26            TyKind::Adt(tref) if tref.is_box() => mini::Type::Ptr(mini::PtrType::Box {
27                pointee: self.pointee_info(span, &tref.generics.types[0])?,
28            }),
29            TyKind::Adt(tref) if tref.is_str() => mini::Type::Slice {
30                elem: mini::GcCow::new(self.ty(span, &Ty::mk_u8())?),
31            },
32            TyKind::Adt(tref) => self.adt_type(span, tref)?,
33            TyKind::Ref(_, pointee, kind) => mini::Type::Ptr(mini::PtrType::Ref {
34                mutbl: match kind {
35                    RefKind::Mut => mini::Mutability::Mutable,
36                    RefKind::Shared => mini::Mutability::Immutable,
37                },
38                pointee: self.pointee_info(span, pointee)?,
39            }),
40            TyKind::RawPtr(pointee, _) => mini::Type::Ptr(mini::PtrType::Raw {
41                meta_kind: self.metadata_kind(span, pointee)?,
42            }),
43            TyKind::FnDef(_) => mini::unit_ty(),
44            TyKind::FnPtr(_) => mini::Type::Ptr(mini::PtrType::FnPtr),
45            TyKind::Never => mini::Type::Enum {
46                variants: Default::default(),
47                discriminant_ty: self.int_type(IntegerTy::Unsigned(UIntTy::U8)),
48                discriminator: mini::Discriminator::Invalid,
49                size: mini_size(0),
50                align: mini_align(span, 1)?,
51            },
52            TyKind::Pattern(base, _) => self.ty(span, base)?,
53            TyKind::PtrMetadata(pointee) => match self.metadata_kind(span, pointee)? {
54                mini::PointerMetaKind::None => mini::unit_ty(),
55                mini::PointerMetaKind::ElementCount => {
56                    mini::Type::Int(self.int_type(IntegerTy::Unsigned(UIntTy::Usize)))
57                }
58                mini::PointerMetaKind::VTablePointer(trait_name) => {
59                    mini::Type::Ptr(mini::PtrType::VTablePtr(trait_name))
60                }
61            },
62            TyKind::DynTrait(_) => {
63                // FIXME(minirust): support dyn Trait
64                raise!(span, "MiniRust output does not support `dyn Trait`")
65            }
66            TyKind::TypeVar(_) | TyKind::TraitType(..) => {
67                raise!(
68                    span,
69                    "MiniRust output requires a monomorphized crate: {}",
70                    ty.with_ctx(&self.fmt)
71                )
72            }
73            TyKind::Error(error) => raise!(span, "type error: {error}"),
74        })
75    }
76
77    fn adt_type(&self, span: Span, tref: &TypeDeclRef) -> Result<mini::Type> {
78        let tdecl = self
79            .krate
80            .type_decls
81            .get(tref.id)
82            .ok_or_else(|| format!("missing type declaration {}", tref.id.with_ctx(&self.fmt)))
83            .context(span)?;
84        let layout = tdecl
85            .layout
86            .get(self.target_name)
87            .ok_or_else(|| {
88                format!(
89                    "missing layout for {}",
90                    tdecl.item_meta.name.with_ctx(&self.fmt)
91                )
92            })
93            .context(span)?;
94        let decl_span = tdecl.item_meta.span;
95        check!(
96            decl_span,
97            layout.size.chosen.as_ref().is_some_and(|size| matches!(
98                size.kind(),
99                SizeExprKind::Constant(value) if value.as_usize_literal().is_some()
100            )),
101            "MiniRust output does not support unsized ADTs"
102        );
103        match layout.repr.align_modif {
104            Some(AlignmentModifier::Pack(_)) => {
105                raise!(decl_span, "MiniRust output does not support packed layouts")
106            }
107            Some(AlignmentModifier::Align(_)) => {
108                raise!(
109                    decl_span,
110                    "MiniRust output does not support overaligned layouts"
111                )
112            }
113            None => {}
114        }
115        let size = mini_size(self.size(span, &layout.size)?);
116        let align = mini_align(span, self.size(span, &layout.align)?)?;
117
118        Ok(match &tdecl.kind {
119            TypeDeclKind::Struct(fields) => {
120                let variant_layout = layout.variant_layouts[VariantId::ZERO].as_ref();
121                self.tuple_type(span, fields, variant_layout, size, align)?
122            }
123            TypeDeclKind::Union(_) => {
124                // let variant_layout = layout.variant_layouts[VariantId::ZERO].as_ref();
125                // let fields = self.fields(span, fields, variant_layout)?;
126                // // FIXME(minirust): compute the precise union chunks. Treating the complete
127                // // allocation as one chunk preserves too much padding for some repr(C) unions.
128                // mini::Type::Union {
129                //     fields,
130                //     chunks: ????
131                //     size,
132                //     align,
133                // }
134                raise!(
135                    decl_span,
136                    "MiniRust output does not support unions because we lack padding information"
137                )
138            }
139            TypeDeclKind::Enum(variants) => {
140                let discriminant_ty = self.int_type(
141                    variants
142                        .first()
143                        .map(|variant| variant.discriminant.ty())
144                        .unwrap_or(IntegerTy::Unsigned(UIntTy::U8)),
145                );
146                let mini_variants = variants
147                    .iter_enumerated()
148                    .map(|(id, variant)| -> Result<_> {
149                        let variant_layout = layout.variant_layouts[id].as_ref();
150                        let ty =
151                            self.tuple_type(span, &variant.fields, variant_layout, size, align)?;
152                        let tagger = variant_layout
153                            .map(|layout| {
154                                layout
155                                    .tagger
156                                    .iter()
157                                    .map(|(offset, value)| {
158                                        (
159                                            mini_size(*offset),
160                                            (self.int_type(value.ty()), mini_int(*value)),
161                                        )
162                                    })
163                                    .collect()
164                            })
165                            .unwrap_or_default();
166                        Ok((mini_int(variant.discriminant), mini::Variant { ty, tagger }))
167                    })
168                    .try_collect()?;
169                let discriminator = if let Some(discriminator) = &layout.discriminator {
170                    self.discriminator(span, variants, discriminator)?
171                } else {
172                    mini::Discriminator::Invalid
173                };
174                mini::Type::Enum {
175                    variants: mini_variants,
176                    discriminant_ty,
177                    discriminator,
178                    size,
179                    align,
180                }
181            }
182            TypeDeclKind::Alias(ty) => self.ty(span, ty)?,
183            TypeDeclKind::Opaque => {
184                raise!(span, "opaque type is not representable in MiniRust")
185            }
186            TypeDeclKind::Error(error) => raise!(span, "type error: {error}"),
187        })
188    }
189
190    fn tuple_type(
191        &self,
192        span: Span,
193        fields: &IndexVec<FieldId, Field>,
194        layout: Option<&VariantLayout>,
195        size: mini::Size,
196        align: mini::Align,
197    ) -> Result<mini::Type> {
198        Ok(mini::Type::Tuple {
199            sized_fields: self.fields(span, fields, layout)?,
200            sized_head_layout: mini::TupleHeadLayout {
201                end: size,
202                align,
203                packed_align: None,
204            },
205            unsized_field: mini::GcCow::new(None),
206        })
207    }
208
209    fn fields(
210        &self,
211        span: Span,
212        fields: &IndexVec<FieldId, Field>,
213        layout: Option<&VariantLayout>,
214    ) -> Result<mini::Fields> {
215        fields
216            .iter_enumerated()
217            .map(|(id, field)| {
218                let offset = if let Some(layout) = layout
219                    && let Some(offset) = layout.field_offsets.get(id)
220                {
221                    offset.chosen.unwrap_or(0)
222                } else {
223                    0
224                };
225                Ok((mini_size(offset), self.ty(span, &field.ty)?))
226            })
227            .try_collect()
228    }
229
230    fn discriminator(
231        &self,
232        span: Span,
233        variants: &IndexVec<VariantId, Variant>,
234        value: &Discriminator,
235    ) -> Result<mini::Discriminator> {
236        Ok(match value {
237            Discriminator::Known(variant) => {
238                mini::Discriminator::Known(mini_int(variants[*variant].discriminant))
239            }
240            Discriminator::Invalid => mini::Discriminator::Invalid,
241            Discriminator::Branch {
242                offset,
243                int_ty,
244                children,
245                fallback,
246            } => mini::Discriminator::Branch {
247                offset: mini_size(
248                    offset
249                        .chosen
250                        .ok_or("non-concrete discriminator offset")
251                        .context(span)?,
252                ),
253                value_type: self.int_type(*int_ty),
254                fallback: mini::GcCow::new(self.discriminator(span, variants, fallback)?),
255                children: children
256                    .iter()
257                    .map(|(range, child)| -> Result<_> {
258                        let start = mini_int(*range.start());
259                        let end = mini_int(*range.end()) + 1;
260                        Ok(((start, end), self.discriminator(span, variants, child)?))
261                    })
262                    .try_collect()?,
263            },
264        })
265    }
266
267    pub(super) fn variant_discriminant(
268        &self,
269        span: Span,
270        ty: &Ty,
271        variant: VariantId,
272    ) -> Result<mini::Int> {
273        let tref = ty.as_adt().ok_or("variant on non-ADT").context(span)?;
274        self.variant_discriminant_for_tref(span, tref, variant)
275    }
276
277    pub(super) fn variant_discriminant_for_tref(
278        &self,
279        span: Span,
280        tref: &TypeDeclRef,
281        variant: VariantId,
282    ) -> Result<mini::Int> {
283        let tdecl = self
284            .krate
285            .type_decls
286            .get(tref.id)
287            .ok_or("missing enum declaration")
288            .context(span)?;
289        let variants = tdecl
290            .kind
291            .as_enum()
292            .ok_or("variant on non-enum")
293            .context(span)?;
294        Ok(mini_int(variants[variant].discriminant))
295    }
296
297    pub(super) fn pointee_info(&self, span: Span, ty: &Ty) -> Result<mini::PointeeInfo> {
298        let mini_ty = self.ty(span, ty)?;
299        let layout = mini_ty.layout::<T>();
300        let inhabited = !ty
301            .inhabited_predicate(self.krate, Some(self.target_name))
302            .always_false();
303        let unsafe_cells = self.unsafe_cell_strategy(span, ty)?;
304        let (freeze, unpin) = self.freeze_and_unpin(span, ty)?;
305        Ok(mini::PointeeInfo {
306            layout,
307            inhabited,
308            unsafe_cells,
309            freeze,
310            unpin,
311        })
312    }
313
314    fn freeze_and_unpin(&self, span: Span, ty: &Ty) -> Result<(bool, bool)> {
315        Ok(match ty.kind() {
316            TyKind::Array(element, ..) | TyKind::Slice(element, ..) => {
317                self.freeze_and_unpin(span, element)?
318            }
319            TyKind::Adt(tref) => {
320                let marker_traits = self
321                    .krate
322                    .type_decls
323                    .get(tref.id)
324                    .and_then(|decl| decl.marker_traits.as_deref())
325                    .ok_or("missing marker-trait information for this type")
326                    .context(span)?;
327                (marker_traits.is_freeze, marker_traits.is_unpin)
328            }
329            TyKind::Pattern(ty, _) => self.freeze_and_unpin(span, ty)?,
330            TyKind::TypeVar(_) | TyKind::TraitType(..) => raise!(
331                span,
332                "MiniRust output requires a monomorphized crate: {}",
333                ty.with_ctx(&self.fmt)
334            ),
335            TyKind::DynTrait(_) => raise!(span, "MiniRust output does not support `dyn Trait`"),
336            TyKind::Error(error) => raise!(span, "type error: {error}"),
337            TyKind::Scalar(_)
338            | TyKind::Ref(..)
339            | TyKind::RawPtr(..)
340            | TyKind::FnDef(_)
341            | TyKind::FnPtr(_)
342            | TyKind::Never
343            | TyKind::PtrMetadata(_) => (true, true),
344        })
345    }
346
347    fn unsafe_cell_strategy(&self, span: Span, ty: &Ty) -> Result<mini::UnsafeCellStrategy> {
348        Ok(match ty.kind() {
349            TyKind::Adt(tref) => {
350                let tdecl = self
351                    .krate
352                    .type_decls
353                    .get(tref.id)
354                    .ok_or("untranslated type decl")
355                    .context(span)?;
356                if matches!(
357                    tdecl.item_meta.lang_item.as_ref(),
358                    Some(LangItem::UnsafeCell)
359                ) {
360                    let mini_ty = self.ty(span, ty)?;
361                    // FIXME(minirust): support unsized types
362                    let mini::LayoutStrategy::Sized(size, _) = mini_ty.layout::<T>() else {
363                        raise!(span, "expected a sized type")
364                    };
365                    mini::UnsafeCellStrategy::Sized {
366                        cells: [(mini_size(0), size)].into_iter().collect(),
367                    }
368                } else {
369                    let layout = tdecl
370                        .layout
371                        .get(self.target_name)
372                        .ok_or("missing layout for ADT")
373                        .context(span)?;
374
375                    let mut cells: Vec<(mini::Size, mini::Size)> = Vec::new();
376                    let mut add_fields = |fields: &IndexVec<FieldId, Field>,
377                                          variant_layout: Option<&VariantLayout>|
378                     -> Result<()> {
379                        for (field_id, field) in fields.iter_enumerated() {
380                            let offset = match variant_layout {
381                                Some(layout) => layout
382                                    .field_offsets
383                                    .get(field_id)
384                                    .and_then(|offset| offset.chosen)
385                                    .ok_or("missing field offset in ADT layout")
386                                    .context(span)?,
387                                None => 0,
388                            };
389                            let field_ty = field.ty.clone().substitute(&tref.generics);
390                            let mini::UnsafeCellStrategy::Sized { cells: field_cells } =
391                                self.unsafe_cell_strategy(span, &field_ty)?
392                            else {
393                                raise!(span, "MiniRust output doesn't support unsized types yet")
394                            };
395                            for (field_cell_offset, cell_size) in field_cells {
396                                let field_cell_offset = size_bytes(span, field_cell_offset)?;
397                                let offset = offset
398                                    .checked_add(field_cell_offset)
399                                    .ok_or("UnsafeCell offset overflows u64")
400                                    .context(span)?;
401                                cells.push((mini_size(offset), cell_size));
402                            }
403                        }
404                        Ok(())
405                    };
406
407                    match &tdecl.kind {
408                        TypeDeclKind::Struct(fields) => {
409                            let variant_layout = layout.variant_layouts[VariantId::ZERO].as_ref();
410                            add_fields(fields, variant_layout)?;
411                        }
412                        TypeDeclKind::Enum(variants) => {
413                            for (variant_id, variant) in variants.iter_enumerated() {
414                                if let Some(variant_layout) =
415                                    layout.variant_layouts[variant_id].as_ref()
416                                {
417                                    add_fields(&variant.fields, Some(variant_layout))?;
418                                }
419                            }
420                        }
421                        TypeDeclKind::Union(_) => {
422                            raise!(span, "MiniRust output does not support unions")
423                        }
424                        TypeDeclKind::Opaque => {
425                            raise!(span, "opaque type is not representable in MiniRust")
426                        }
427                        TypeDeclKind::Alias(alias) => {
428                            return self.unsafe_cell_strategy(
429                                span,
430                                &alias.clone().substitute(&tref.generics),
431                            );
432                        }
433                        TypeDeclKind::Error(error) => raise!(span, "type error: {error}"),
434                    }
435
436                    // Cells from different enum variants may overlap.
437                    cells.sort_unstable();
438                    let mut merged_ranges: Vec<(u64, u64)> = Vec::new();
439                    for (offset, size) in cells {
440                        let start = size_bytes(span, offset)?;
441                        let size = size_bytes(span, size)?;
442                        let end = start
443                            .checked_add(size)
444                            .ok_or("UnsafeCell range overflows u64")
445                            .context(span)?;
446                        if let Some((_, previous_end)) = merged_ranges.last_mut()
447                            && start <= *previous_end
448                        {
449                            *previous_end = (*previous_end).max(end);
450                        } else {
451                            merged_ranges.push((start, end));
452                        }
453                    }
454                    mini::UnsafeCellStrategy::Sized {
455                        cells: merged_ranges
456                            .into_iter()
457                            .map(|(start, end)| (mini_size(start), mini_size(end - start)))
458                            .collect(),
459                    }
460                }
461            }
462            TyKind::Array(elem_ty, len, ..) => {
463                let len = len
464                    .as_usize_literal()
465                    .ok_or("non-concrete array length")
466                    .context(span)?;
467                let (elem_size, _) = self.size_and_align(span, elem_ty)?;
468                let mini::UnsafeCellStrategy::Sized { cells } =
469                    self.unsafe_cell_strategy(span, elem_ty)?
470                else {
471                    unreachable!()
472                };
473                mini::UnsafeCellStrategy::Sized {
474                    cells: cells
475                        .into_iter()
476                        .cartesian_product(0u128..len)
477                        .map(|((offset, cell_size), index)| -> Result<_> {
478                            let index = u64::try_from(index)
479                                .map_err(|_| "array index does not fit u64")
480                                .context(span)?;
481                            let elem_start = index
482                                .checked_mul(elem_size)
483                                .ok_or("array cell offset overflows u64")
484                                .context(span)?;
485                            Ok((mini_size(elem_start) + offset, cell_size))
486                        })
487                        .try_collect()?,
488                }
489            }
490            TyKind::Slice(elem_ty, ..) => {
491                let mini::UnsafeCellStrategy::Sized { cells } =
492                    self.unsafe_cell_strategy(span, elem_ty)?
493                else {
494                    unreachable!()
495                };
496                mini::UnsafeCellStrategy::Slice {
497                    element_cells: cells,
498                }
499            }
500            TyKind::Pattern(ty, _) => self.unsafe_cell_strategy(span, ty)?,
501            TyKind::TypeVar(_) | TyKind::TraitType(..) => raise!(
502                span,
503                "MiniRust output requires a monomorphized crate: {}",
504                ty.with_ctx(&self.fmt)
505            ),
506            TyKind::DynTrait(_) => mini::UnsafeCellStrategy::TraitObject,
507            TyKind::Error(error) => raise!(span, "type error: {error}"),
508            TyKind::Scalar(_)
509            | TyKind::Ref(..)
510            | TyKind::RawPtr(..)
511            | TyKind::FnDef(_)
512            | TyKind::FnPtr(_)
513            | TyKind::Never
514            | TyKind::PtrMetadata(_) => mini::UnsafeCellStrategy::Sized {
515                cells: Default::default(),
516            },
517        })
518    }
519
520    pub(super) fn size_and_align(&self, span: Span, ty: &Ty) -> Result<(u64, u64)> {
521        let ty = self.ty(span, ty)?;
522        Ok(match ty.layout::<T>() {
523            mini::LayoutStrategy::Sized(size, align) => {
524                (size_bytes(span, size)?, align_bytes(span, align)?)
525            }
526            _ => raise!(span, "expected a sized type"),
527        })
528    }
529
530    pub(super) fn metadata_kind(&self, span: Span, ty: &Ty) -> Result<mini::PointerMetaKind> {
531        Ok(match ty.get_ptr_metadata(self.krate) {
532            PtrMetadata::None => mini::PointerMetaKind::None,
533            PtrMetadata::Length => mini::PointerMetaKind::ElementCount,
534            PtrMetadata::VTable(_) | PtrMetadata::InheritFrom(_) => {
535                // FIXME(minirust): dyn Trait
536                raise!(span, "MiniRust output does not support `dyn Trait`")
537            }
538        })
539    }
540
541    pub(super) fn int_type(&self, ty: IntegerTy) -> mini::IntType {
542        let signed = match ty {
543            IntegerTy::Signed(_) => mini::Signedness::Signed,
544            IntegerTy::Unsigned(_) => mini::Signedness::Unsigned,
545        };
546        mini::IntType {
547            signed,
548            size: mini::Size::from_bytes(ty.target_size(self.target.target_pointer_size)).unwrap(),
549        }
550    }
551
552    pub(super) fn size(&self, span: Span, size: &crate::ast::Size) -> Result<u64> {
553        let expr = size
554            .chosen
555            .as_ref()
556            .ok_or("layout has no concrete size")
557            .context(span)?;
558        match expr.kind() {
559            SizeExprKind::Constant(value) => {
560                if let Some(value) = value.as_usize_literal() {
561                    u64::try_from(value)
562                        .map_err(|_| "layout size does not fit u64")
563                        .context(span)
564                } else {
565                    Ok(match value.kind() {
566                        ConstantExprKind::SizeOf(ty) => self.size_and_align(span, ty)?.0,
567                        ConstantExprKind::AlignOf(ty) => self.size_and_align(span, ty)?.1,
568                        _ => raise!(span, "layout has no concrete size"),
569                    })
570                }
571            }
572            _ => raise!(span, "layout has no concrete size"),
573        }
574    }
575}