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 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 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 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.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 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}