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) => mb::int_ty(mini::Signedness::Unsigned, mini_size(4)),
9 TyKind::Scalar(ScalarTy::Float(_)) => {
10 raise!(span, "MiniRust has no floating-point types")
11 }
12 TyKind::Array(elem_ty, len, _) => mini::Type::Array {
13 elem: mini::GcCow::new(self.ty(span, elem_ty)?),
14 count: mini::Int::from(
15 len.as_usize_literal()
16 .ok_or("non-concrete array length")
17 .context(span)?,
18 ),
19 },
20 TyKind::Slice(elem_ty, _) => mini::Type::Slice {
21 elem: mini::GcCow::new(self.ty(span, elem_ty)?),
22 },
23 TyKind::Adt(tref) if tref.is_box() => {
24 let pointee = ty
25 .builtin_deref(self.krate)
26 .ok_or("Box type is missing its pointee")
27 .context(span)?;
28 mini::Type::Ptr(mini::PtrType::Box {
29 pointee: self.pointee_info(span, pointee)?,
30 })
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 mb::int_ty(mini::Signedness::Unsigned, T::PTR_SIZE)
57 }
58 mini::PointerMetaKind::VTablePointer(trait_name) => {
59 mini::Type::Ptr(mini::PtrType::VTablePtr(trait_name))
60 }
61 },
62 TyKind::DynTrait(pred) => mini::Type::TraitObject(
63 self.trait_name(
64 pred.vtable_ref(self.krate)
65 .ok_or("missing vtable for dyn trait")
66 .context(span)?
67 .id,
68 ),
69 ),
70 TyKind::TypeVar(_) | TyKind::TraitType(..) => {
71 raise!(
72 span,
73 "MiniRust output requires a monomorphized crate: {}",
74 ty.with_ctx(&self.fmt)
75 )
76 }
77 TyKind::Error(error) => raise!(span, "type error: {error}"),
78 })
79 }
80
81 fn adt_type(&self, span: Span, tref: &TypeDeclRef) -> Result<mini::Type> {
82 let tdecl = self
83 .krate
84 .type_decls
85 .get(tref.id)
86 .ok_or_else(|| format!("missing type declaration {}", tref.id.with_ctx(&self.fmt)))
87 .context(span)?;
88 let layout = tdecl
89 .layout
90 .get(self.target_name)
91 .ok_or_else(|| {
92 format!(
93 "missing layout for {}",
94 tdecl.item_meta.name.with_ctx(&self.fmt)
95 )
96 })
97 .context(span)?;
98 Ok(match &tdecl.kind {
99 TypeDeclKind::Struct(fields) => {
100 let variant_layout = layout.variant_layouts[VariantId::ZERO].as_ref();
101 self.tuple_type(span, fields, variant_layout, layout, &tref.generics)?
102 }
103 TypeDeclKind::Union(fields) => {
104 let variant_layout = layout.variant_layouts[VariantId::ZERO]
105 .as_ref()
106 .ok_or("union has no fields layout")
107 .context(span)?;
108 let fields = fields
109 .iter_enumerated()
110 .map(|(id, field)| -> Result<_> {
111 let offset = variant_layout.field_offsets[id]
112 .chosen
113 .ok_or("missing field offset in union layout")
114 .context(span)?;
115 let ty = self.ty(span, &field.ty.clone().substitute(&tref.generics))?;
116 Ok((mini_size(offset), ty))
117 })
118 .try_collect()?;
119 let size = mini_size(self.size(span, &layout.size)?);
120 let align = mini_align(span, self.size(span, &layout.align)?)?;
121 mini::Type::Union {
122 fields,
123 chunks: [(mini::Size::ZERO, size)].into_iter().collect(),
125 size,
126 align,
127 }
128 }
129 TypeDeclKind::Enum(variants) => {
130 let size = mini_size(self.size(span, &layout.size)?);
131 let align = mini_align(span, self.size(span, &layout.align)?)?;
132 let discriminant_ty = self.int_type(
133 variants
134 .first()
135 .map(|variant| variant.discriminant.ty())
136 .unwrap_or(IntegerTy::Unsigned(UIntTy::U8)),
137 );
138 let mini_variants = variants
139 .iter_enumerated()
140 .map(|(id, variant)| -> Result<_> {
141 let variant_layout = layout.variant_layouts[id].as_ref();
142 let ty = self.tuple_type(
143 span,
144 &variant.fields,
145 variant_layout,
146 layout,
147 &tref.generics,
148 )?;
149 let tagger = variant_layout
150 .map(|layout| {
151 layout
152 .tagger
153 .iter()
154 .map(|(offset, value)| {
155 (
156 mini_size(*offset),
157 (self.int_type(value.ty()), mini_int(*value)),
158 )
159 })
160 .collect()
161 })
162 .unwrap_or_default();
163 Ok((mini_int(variant.discriminant), mini::Variant { ty, tagger }))
164 })
165 .try_collect()?;
166 let discriminator = if let Some(discriminator) = &layout.discriminator {
167 self.discriminator(span, variants, discriminator)?
168 } else {
169 mini::Discriminator::Invalid
170 };
171 mini::Type::Enum {
172 variants: mini_variants,
173 discriminant_ty,
174 discriminator,
175 size,
176 align,
177 }
178 }
179 TypeDeclKind::Alias(ty) => self.ty(span, &ty.clone().substitute(&tref.generics))?,
180 TypeDeclKind::Opaque => {
181 raise!(span, "opaque type is not representable in MiniRust")
182 }
183 TypeDeclKind::Error(error) => raise!(span, "type error: {error}"),
184 })
185 }
186
187 fn tuple_type(
188 &self,
189 span: Span,
190 fields: &IndexVec<FieldId, Field>,
191 variant_layout: Option<&VariantLayout>,
192 layout: &crate::ast::Layout,
193 generics: &GenericArgs,
194 ) -> Result<mini::Type> {
195 let (repr_align, packed_align) = match layout.repr.align_modif {
196 Some(AlignmentModifier::Pack(bytes)) => {
197 (mini::Align::ONE, Some(mini_align(span, bytes)?))
198 }
199 Some(AlignmentModifier::Align(bytes)) => (mini_align(span, bytes)?, None),
200 None => (mini::Align::ONE, None),
201 };
202 let mut sized_fields = mini::Fields::new();
203 let mut head_align = repr_align;
204 let mut unsized_field = None;
205 let mut tail_offset = None;
206 for (id, field) in fields.iter_enumerated() {
207 let ty = self.ty(span, &field.ty.clone().substitute(generics))?;
208 let offset = match variant_layout {
209 Some(layout) => layout.field_offsets[id]
210 .chosen
211 .ok_or("missing field offset in ADT layout")
212 .context(span)?,
213 None => 0,
215 };
216 match ty.layout::<T>() {
217 mini::LayoutStrategy::Sized(_, field_align) => {
218 head_align = head_align
219 .max(packed_align.map_or(field_align, |packed| field_align.min(packed)));
220 sized_fields.push((mini_size(offset), ty));
221 }
222 _ => {
223 check!(
224 span,
225 id.index() + 1 == fields.len(),
226 "non-tail field is unsized"
227 );
228 unsized_field = Some(ty);
229 tail_offset = Some(offset);
230 }
231 }
232 }
233 let (end, head_align) = match tail_offset {
234 Some(offset) => (mini_size(offset), head_align),
236 None => (
237 mini_size(self.size(span, &layout.size)?),
238 mini_align(span, self.size(span, &layout.align)?)?,
239 ),
240 };
241 Ok(mini::Type::Tuple {
242 sized_fields,
243 sized_head_layout: mini::TupleHeadLayout {
244 end,
245 align: head_align,
246 packed_align: if tail_offset.is_some() {
248 packed_align
249 } else {
250 None
251 },
252 },
253 unsized_field: mini::GcCow::new(unsized_field),
254 })
255 }
256
257 fn discriminator(
258 &self,
259 span: Span,
260 variants: &IndexVec<VariantId, Variant>,
261 value: &Discriminator,
262 ) -> Result<mini::Discriminator> {
263 Ok(match value {
264 Discriminator::Known(variant) => {
265 mini::Discriminator::Known(mini_int(variants[*variant].discriminant))
266 }
267 Discriminator::Invalid => mini::Discriminator::Invalid,
268 Discriminator::Branch {
269 offset,
270 int_ty,
271 children,
272 fallback,
273 } => mini::Discriminator::Branch {
274 offset: mini_size(
275 offset
276 .chosen
277 .ok_or("non-concrete discriminator offset")
278 .context(span)?,
279 ),
280 value_type: self.int_type(*int_ty),
281 fallback: mini::GcCow::new(self.discriminator(span, variants, fallback)?),
282 children: children
283 .iter()
284 .map(|(range, child)| -> Result<_> {
285 let start = mini_int(*range.start());
286 let end = mini_int(*range.end()) + 1;
287 Ok(((start, end), self.discriminator(span, variants, child)?))
288 })
289 .try_collect()?,
290 },
291 })
292 }
293
294 pub(super) fn variant_discriminant(
295 &self,
296 span: Span,
297 ty: &Ty,
298 variant: VariantId,
299 ) -> Result<mini::Int> {
300 let tref = ty.as_adt().ok_or("variant on non-ADT").context(span)?;
301 self.variant_discriminant_for_tref(span, tref, variant)
302 }
303
304 pub(super) fn variant_discriminant_for_tref(
305 &self,
306 span: Span,
307 tref: &TypeDeclRef,
308 variant: VariantId,
309 ) -> Result<mini::Int> {
310 let tdecl = self
311 .krate
312 .type_decls
313 .get(tref.id)
314 .ok_or("missing enum declaration")
315 .context(span)?;
316 let variants = tdecl
317 .kind
318 .as_enum()
319 .ok_or("variant on non-enum")
320 .context(span)?;
321 Ok(mini_int(variants[variant].discriminant))
322 }
323
324 pub(super) fn pointee_info(&self, span: Span, ty: &Ty) -> Result<mini::PointeeInfo> {
325 let mini_ty = self.ty(span, ty)?;
326 let layout = mini_ty.layout::<T>();
327 let inhabited = !ty
328 .inhabited_predicate(self.krate, Some(self.target_name))
329 .always_false();
330 let unsafe_cells = self.unsafe_cell_strategy(span, ty)?;
331 let (freeze, unpin) = self.freeze_and_unpin(span, ty)?;
332 Ok(mini::PointeeInfo {
333 layout,
334 inhabited,
335 unsafe_cells,
336 freeze,
337 unpin,
338 })
339 }
340
341 fn freeze_and_unpin(&self, span: Span, ty: &Ty) -> Result<(bool, bool)> {
342 Ok(match ty.kind() {
343 TyKind::Array(element, ..) | TyKind::Slice(element, ..) => {
344 self.freeze_and_unpin(span, element)?
345 }
346 TyKind::Adt(tref) => {
347 let marker_traits = self
348 .krate
349 .type_decls
350 .get(tref.id)
351 .and_then(|decl| decl.marker_traits.as_deref())
352 .ok_or("missing marker-trait information for this type")
353 .context(span)?;
354 (marker_traits.is_freeze, marker_traits.is_unpin)
355 }
356 TyKind::Pattern(ty, _) => self.freeze_and_unpin(span, ty)?,
357 TyKind::TypeVar(_) | TyKind::TraitType(..) => raise!(
358 span,
359 "MiniRust output requires a monomorphized crate: {}",
360 ty.with_ctx(&self.fmt)
361 ),
362 TyKind::DynTrait(pred) => {
363 let has_bound = |lang_item| {
364 pred.binder.params.trait_clauses.iter().any(|clause| {
365 self.krate
366 .trait_decls
367 .get(clause.trait_.skip_binder.id)
368 .is_some_and(|decl| {
369 decl.item_meta.lang_item.as_ref() == Some(&lang_item)
370 })
371 })
372 };
373 (has_bound(LangItem::Freeze), has_bound(LangItem::Unpin))
374 }
375 TyKind::Error(error) => raise!(span, "type error: {error}"),
376 TyKind::Scalar(_)
377 | TyKind::Ref(..)
378 | TyKind::RawPtr(..)
379 | TyKind::FnDef(_)
380 | TyKind::FnPtr(_)
381 | TyKind::Never
382 | TyKind::PtrMetadata(_) => (true, true),
383 })
384 }
385
386 pub(super) fn unsafe_cell_strategy(
387 &self,
388 span: Span,
389 ty: &Ty,
390 ) -> Result<mini::UnsafeCellStrategy> {
391 fn all_cells_strategy(layout: mini::LayoutStrategy) -> mini::UnsafeCellStrategy {
393 let whole_range = |size| {
394 if size == mini::Size::ZERO {
395 mini::List::new()
396 } else {
397 [(mini::Size::ZERO, size)].into_iter().collect()
398 }
399 };
400 match layout {
401 mini::LayoutStrategy::Sized(size, _) => mini::UnsafeCellStrategy::Sized {
402 cells: whole_range(size),
403 },
404 mini::LayoutStrategy::Slice(element_size, _) => mini::UnsafeCellStrategy::Slice {
405 element_cells: whole_range(element_size),
406 },
407 mini::LayoutStrategy::Tuple { head, tail } => mini::UnsafeCellStrategy::Tuple {
408 head_cells: whole_range(head.end),
409 tail_cells: mini::GcCow::new(all_cells_strategy(tail.extract())),
410 },
411 mini::LayoutStrategy::TraitObject(_) => mini::UnsafeCellStrategy::TraitObject,
412 }
413 }
414
415 Ok(match ty.kind() {
416 TyKind::Adt(tref) => {
417 let tdecl = self
418 .krate
419 .type_decls
420 .get(tref.id)
421 .ok_or("untranslated type decl")
422 .context(span)?;
423 if matches!(
424 tdecl.item_meta.lang_item.as_ref(),
425 Some(LangItem::UnsafeCell)
426 ) {
427 let mini_ty = self.ty(span, ty)?;
428 all_cells_strategy(mini_ty.layout::<T>())
429 } else {
430 let layout = tdecl
431 .layout
432 .get(self.target_name)
433 .ok_or("missing layout for ADT")
434 .context(span)?;
435
436 let mut sized_cells: Vec<(mini::Size, mini::Size)> = Vec::new();
437 let mut tail_cells = None;
438 let mut add_fields = |fields: &IndexVec<FieldId, Field>,
440 variant_layout: Option<&VariantLayout>|
441 -> Result<()> {
442 for (field_id, field) in fields.iter_enumerated() {
443 let offset = match variant_layout {
444 Some(layout) => layout
445 .field_offsets
446 .get(field_id)
447 .and_then(|offset| offset.chosen)
448 .ok_or("missing field offset in ADT layout")
449 .context(span)?,
450 None => 0,
451 };
452 let field_ty = field.ty.clone().substitute(&tref.generics);
453 match self.unsafe_cell_strategy(span, &field_ty)? {
454 mini::UnsafeCellStrategy::Sized { cells: field_cells } => {
455 for (field_cell_offset, cell_size) in field_cells {
456 let field_cell_offset =
457 size_bytes(span, field_cell_offset)?;
458 let offset = offset
459 .checked_add(field_cell_offset)
460 .ok_or("UnsafeCell offset overflows u64")
461 .context(span)?;
462 sized_cells.push((mini_size(offset), cell_size));
463 }
464 }
465 strategy => {
466 check!(
467 span,
468 field_id.index() + 1 == fields.len()
469 && tail_cells.is_none(),
470 "non-tail field is unsized"
471 );
472 tail_cells = Some(strategy);
473 }
474 }
475 }
476 Ok(())
477 };
478
479 match &tdecl.kind {
480 TypeDeclKind::Struct(fields) => {
481 let variant_layout = layout.variant_layouts[VariantId::ZERO].as_ref();
482 add_fields(fields, variant_layout)?;
483 }
484 TypeDeclKind::Enum(variants) => {
485 for (variant_id, variant) in variants.iter_enumerated() {
486 if let Some(variant_layout) =
487 layout.variant_layouts[variant_id].as_ref()
488 {
489 add_fields(&variant.fields, Some(variant_layout))?;
490 }
491 }
492 check!(
493 span,
494 tail_cells.is_none(),
495 "enum variant has an unsized field"
496 );
497 }
498 TypeDeclKind::Union(fields) => {
499 let variant_layout = layout.variant_layouts[VariantId::ZERO].as_ref();
500 add_fields(fields, variant_layout)?;
501 check!(span, tail_cells.is_none(), "union has an unsized field");
502 }
503 TypeDeclKind::Opaque => {
504 raise!(span, "opaque type is not representable in MiniRust")
505 }
506 TypeDeclKind::Alias(alias) => {
507 return self.unsafe_cell_strategy(
508 span,
509 &alias.clone().substitute(&tref.generics),
510 );
511 }
512 TypeDeclKind::Error(error) => raise!(span, "type error: {error}"),
513 };
514
515 sized_cells.sort_unstable();
517 let mut merged_ranges: Vec<(u64, u64)> = Vec::new();
518 for (offset, size) in sized_cells {
519 let start = size_bytes(span, offset)?;
520 let size = size_bytes(span, size)?;
521 let end = start
522 .checked_add(size)
523 .ok_or("UnsafeCell range overflows u64")
524 .context(span)?;
525 if let Some((_, previous_end)) = merged_ranges.last_mut()
526 && start <= *previous_end
527 {
528 *previous_end = (*previous_end).max(end);
529 } else {
530 merged_ranges.push((start, end));
531 }
532 }
533 let sized_cells = merged_ranges
534 .into_iter()
535 .map(|(start, end)| (mini_size(start), mini_size(end - start)))
536 .collect();
537
538 match tail_cells {
539 None => mini::UnsafeCellStrategy::Sized { cells: sized_cells },
540 Some(tail_cells) => mini::UnsafeCellStrategy::Tuple {
541 head_cells: sized_cells,
542 tail_cells: mini::GcCow::new(tail_cells),
543 },
544 }
545 }
546 }
547 TyKind::Array(elem_ty, len, ..) => {
548 let len = len
549 .as_usize_literal()
550 .ok_or("non-concrete array length")
551 .context(span)?;
552 let (elem_size, _) = self.size_and_align(span, elem_ty)?;
553 let mini::UnsafeCellStrategy::Sized { cells } =
554 self.unsafe_cell_strategy(span, elem_ty)?
555 else {
556 unreachable!()
557 };
558 let cells = cells.into_iter().collect_vec(); mini::UnsafeCellStrategy::Sized {
560 cells: (0u128..len)
561 .cartesian_product(cells.iter().copied())
562 .map(|(index, (offset, cell_size))| -> Result<_> {
563 let index = u64::try_from(index)
564 .map_err(|_| "array index does not fit u64")
565 .context(span)?;
566 let elem_start = index
567 .checked_mul(elem_size)
568 .ok_or("array cell offset overflows u64")
569 .context(span)?;
570 Ok((mini_size(elem_start) + offset, cell_size))
571 })
572 .try_collect()?,
573 }
574 }
575 TyKind::Slice(elem_ty, ..) => {
576 let mini::UnsafeCellStrategy::Sized { cells } =
577 self.unsafe_cell_strategy(span, elem_ty)?
578 else {
579 unreachable!()
580 };
581 mini::UnsafeCellStrategy::Slice {
582 element_cells: cells,
583 }
584 }
585 TyKind::Pattern(ty, _) => self.unsafe_cell_strategy(span, ty)?,
586 TyKind::TypeVar(_) | TyKind::TraitType(..) => raise!(
587 span,
588 "MiniRust output requires a monomorphized crate: {}",
589 ty.with_ctx(&self.fmt)
590 ),
591 TyKind::DynTrait(_) => mini::UnsafeCellStrategy::TraitObject,
592 TyKind::Error(error) => raise!(span, "type error: {error}"),
593 TyKind::Scalar(_)
594 | TyKind::Ref(..)
595 | TyKind::RawPtr(..)
596 | TyKind::FnDef(_)
597 | TyKind::FnPtr(_)
598 | TyKind::Never
599 | TyKind::PtrMetadata(_) => mini::UnsafeCellStrategy::Sized {
600 cells: Default::default(),
601 },
602 })
603 }
604
605 pub(super) fn size_and_align(&self, span: Span, ty: &Ty) -> Result<(u64, u64)> {
606 let ty = self.ty(span, ty)?;
607 Ok(match ty.layout::<T>() {
608 mini::LayoutStrategy::Sized(size, align) => {
609 (size_bytes(span, size)?, align_bytes(span, align)?)
610 }
611 _ => raise!(span, "expected a sized type"),
612 })
613 }
614
615 pub(super) fn metadata_kind(&self, span: Span, ty: &Ty) -> Result<mini::PointerMetaKind> {
616 Ok(match ty.get_ptr_metadata(self.krate) {
617 PtrMetadata::None => mini::PointerMetaKind::None,
618 PtrMetadata::Length => mini::PointerMetaKind::ElementCount,
619 PtrMetadata::VTable(tref) => {
620 mini::PointerMetaKind::VTablePointer(self.trait_name(tref.id))
621 }
622 PtrMetadata::InheritFrom(_) => raise!(span, "unresolved pointer metadata"),
623 })
624 }
625
626 pub(super) fn int_type(&self, ty: IntegerTy) -> mini::IntType {
627 let signed = match ty {
628 IntegerTy::Signed(_) => mini::Signedness::Signed,
629 IntegerTy::Unsigned(_) => mini::Signedness::Unsigned,
630 };
631 mini::IntType {
632 signed,
633 size: mini::Size::from_bytes(ty.target_size(self.target.target_pointer_size)).unwrap(),
634 }
635 }
636
637 pub(super) fn size(&self, span: Span, size: &crate::ast::Size) -> Result<u64> {
638 let expr = size
639 .chosen
640 .as_ref()
641 .ok_or("layout has no concrete size")
642 .context(span)?;
643 match expr.kind() {
644 SizeExprKind::Constant(value) => {
645 if let Some(value) = value.as_usize_literal() {
646 u64::try_from(value)
647 .map_err(|_| "layout size does not fit u64")
648 .context(span)
649 } else {
650 Ok(match value.kind() {
651 ConstantExprKind::SizeOf(ty) => self.size_and_align(span, ty)?.0,
652 ConstantExprKind::AlignOf(ty) => self.size_and_align(span, ty)?.1,
653 _ => raise!(span, "layout has no concrete size"),
654 })
655 }
656 }
657 _ => raise!(span, "layout has no concrete size"),
658 }
659 }
660}