Skip to main content

nifty_codegen/compile/
types.rs

1//! The type descriptors compiled code hands the runtime. The built-in types are the
2//! runtime's own; each list and struct type is emitted once, after the code that uses it,
3//! since struct types can lead back to themselves.
4
5use std::collections::HashMap;
6use std::mem::size_of;
7
8use cranelift_module::{DataId, Module};
9use nifty_check::hir::{Program, Type};
10use nifty_rt::{TypeDesc, TypeKind};
11
12use super::runtime::Runtime;
13use crate::CodegenError;
14use crate::emit::{Blob, Emitter, Target};
15
16#[derive(Debug, Default)]
17pub(super) struct Types {
18    declared: HashMap<Type, DataId>,
19    /// Declared but not yet emitted.
20    pending: Vec<(Type, DataId)>,
21}
22
23impl Types {
24    /// The descriptor of `ty`, declaring it if it's new.
25    pub fn desc<M: Module>(
26        &mut self,
27        module: &mut M,
28        runtime: &Runtime,
29        ty: &Type,
30    ) -> Result<DataId, CodegenError> {
31        match ty {
32            Type::Int => return Ok(runtime.int),
33            Type::Float => return Ok(runtime.float),
34            Type::Bool => return Ok(runtime.bool),
35            Type::String => return Ok(runtime.string),
36            Type::List(_) | Type::Struct(_) => {}
37            Type::Void | Type::Error => {
38                return Err(CodegenError::new(format!("no value has the type {ty:?}")));
39            }
40        }
41        if let Some(id) = self.declared.get(ty) {
42            return Ok(*id);
43        }
44        let id = module.declare_anonymous_data(false, false)?;
45        self.declared.insert(ty.clone(), id);
46        self.pending.push((ty.clone(), id));
47        Ok(id)
48    }
49
50    /// Emits every descriptor declared so far, and the ones they lead to.
51    pub fn emit<M: Module>(
52        &mut self,
53        emitter: &mut Emitter<M>,
54        runtime: &Runtime,
55        program: &Program,
56    ) -> Result<(), CodegenError> {
57        while let Some((ty, id)) = self.pending.pop() {
58            let (kind, children) = match &ty {
59                Type::List(element) => (TypeKind::List, vec![Type::clone(element)]),
60                Type::Struct(id) => {
61                    let fields = &program.structs[id.0].fields;
62                    let fields = fields.iter().map(|field| field.ty.clone()).collect();
63                    (TypeKind::Struct, fields)
64                }
65                other => {
66                    return Err(CodegenError::new(format!(
67                        "{other:?} has no descriptor of its own"
68                    )));
69                }
70            };
71            let mut list = Blob::zeroed(children.len() * size_of::<&TypeDesc>());
72            for (index, child) in children.iter().enumerate() {
73                let child = self.desc(emitter.module_mut(), runtime, child)?;
74                list.put_pointer(index * size_of::<&TypeDesc>(), Target::Data(child));
75            }
76            let list = emitter.data(list)?;
77            let mut desc = Blob::zeroed(size_of::<TypeDesc>());
78            desc.put_u8(TypeDesc::KIND_OFFSET, kind.code());
79            desc.put_slice(
80                TypeDesc::CHILDREN_OFFSET,
81                Some(Target::Data(list)),
82                children.len(),
83            );
84            emitter.define_data(id, desc)?;
85        }
86        Ok(())
87    }
88}
89
90/// Whether values of `ty` are reference-counted objects rather than plain bits.
91pub(super) fn is_object(ty: &Type) -> bool {
92    matches!(ty, Type::String | Type::List(_) | Type::Struct(_))
93}