Type Registry

The TypeRegistry stores user-defined types (structs, enums, type aliases). It enables looking up type definitions by name.

Location

compiler/oric/src/typeck/type_registry.rs (~432 lines)

Structure

pub struct TypeRegistry {
    /// Type name -> Definition
    types: HashMap<Name, TypeDef>,

    /// Trait name -> Definition
    traits: HashMap<Name, TraitDef>,

    /// Type -> Trait implementations
    impls: HashMap<Type, Vec<ImplDef>>,
}

pub enum TypeDef {
    Struct(StructDef),
    Enum(EnumDef),
    Alias(Type),
}

pub struct StructDef {
    pub name: Name,
    pub generics: Vec<Name>,
    pub fields: Vec<Field>,
}

pub struct EnumDef {
    pub name: Name,
    pub generics: Vec<Name>,
    pub variants: Vec<Variant>,
}

Registration

Types are registered during an initial pass:

impl TypeRegistry {
    pub fn register_types(&mut self, module: &Module) {
        for type_def in &module.types {
            match type_def {
                TypeDecl::Struct { name, generics, fields } => {
                    self.types.insert(*name, TypeDef::Struct(StructDef {
                        name: *name,
                        generics: generics.clone(),
                        fields: fields.clone(),
                    }));
                }

                TypeDecl::Enum { name, generics, variants } => {
                    self.types.insert(*name, TypeDef::Enum(EnumDef {
                        name: *name,
                        generics: generics.clone(),
                        variants: variants.clone(),
                    }));
                }

                TypeDecl::Alias { name, ty } => {
                    self.types.insert(*name, TypeDef::Alias(ty.clone()));
                }
            }
        }
    }
}

Lookup

Type Definition

impl TypeRegistry {
    pub fn get(&self, name: Name) -> Option<&TypeDef> {
        self.types.get(&name)
    }

    pub fn get_struct(&self, name: Name) -> Option<&StructDef> {
        match self.types.get(&name)? {
            TypeDef::Struct(s) => Some(s),
            _ => None,
        }
    }

    pub fn get_enum(&self, name: Name) -> Option<&EnumDef> {
        match self.types.get(&name)? {
            TypeDef::Enum(e) => Some(e),
            _ => None,
        }
    }
}

Field Lookup

impl TypeRegistry {
    pub fn field_type(&self, ty: &Type, field: Name) -> Option<Type> {
        match ty {
            Type::Named(name) => {
                let struct_def = self.get_struct(*name)?;
                struct_def.fields
                    .iter()
                    .find(|f| f.name == field)
                    .map(|f| f.ty.clone())
            }

            Type::Generic { base, args } => {
                // Substitute generic arguments
                let struct_def = match base.as_ref() {
                    Type::Named(name) => self.get_struct(*name)?,
                    _ => return None,
                };

                let field_ty = struct_def.fields
                    .iter()
                    .find(|f| f.name == field)?
                    .ty.clone();

                // Build substitution from generic params to args
                let subst: HashMap<Name, Type> = struct_def.generics
                    .iter()
                    .zip(args.iter())
                    .map(|(p, a)| (*p, a.clone()))
                    .collect();

                Some(field_ty.substitute(&subst))
            }

            _ => None,
        }
    }
}

Variant Lookup

impl TypeRegistry {
    pub fn variant_type(&self, ty: &Type, variant: Name) -> Option<VariantType> {
        match ty {
            Type::Named(name) => {
                let enum_def = self.get_enum(*name)?;
                enum_def.variants
                    .iter()
                    .find(|v| v.name() == variant)
                    .map(|v| v.to_type())
            }

            Type::Generic { base, args } => {
                // Similar substitution logic...
            }

            _ => None,
        }
    }
}

Generic Instantiation

impl TypeRegistry {
    pub fn instantiate(&self, name: Name, args: &[Type]) -> Result<Type, TypeError> {
        let def = self.get(name).ok_or(TypeError::UndefinedType(name))?;

        match def {
            TypeDef::Struct(s) => {
                if args.len() != s.generics.len() {
                    return Err(TypeError::WrongGenericArgCount {
                        expected: s.generics.len(),
                        found: args.len(),
                    });
                }

                Ok(Type::Generic {
                    base: Box::new(Type::Named(name)),
                    args: args.to_vec(),
                })
            }

            TypeDef::Alias(ty) => {
                // Expand alias and substitute
                Ok(ty.clone())
            }

            _ => todo!(),
        }
    }
}

Trait Registry

Trait Definition

pub struct TraitDef {
    pub name: Name,
    pub methods: Vec<TraitMethod>,
    pub associated_types: Vec<AssociatedType>,
}

pub struct TraitMethod {
    pub name: Name,
    pub params: Vec<Type>,
    pub ret: Type,
    pub default: Option<ExprId>,
}

Trait Implementation

pub struct ImplDef {
    pub trait_name: Name,
    pub for_type: Type,
    pub methods: HashMap<Name, ExprId>,
    pub associated_types: HashMap<Name, Type>,
}

Checking Trait Bounds

impl TypeRegistry {
    pub fn implements(&self, ty: &Type, trait_name: Name) -> bool {
        // Check for explicit impl
        if let Some(impls) = self.impls.get(ty) {
            if impls.iter().any(|i| i.trait_name == trait_name) {
                return true;
            }
        }

        // Check built-in implementations
        match trait_name.as_str() {
            "Eq" => self.is_eq(ty),
            "Clone" => self.is_clone(ty),
            "Default" => self.is_default(ty),
            _ => false,
        }
    }

    fn is_eq(&self, ty: &Type) -> bool {
        match ty {
            // Primitives are Eq
            Type::Int | Type::Float | Type::Bool | Type::String => true,

            // Compound types are Eq if elements are Eq
            Type::List(elem) => self.is_eq(elem),
            Type::Option(inner) => self.is_eq(inner),
            Type::Tuple(elems) => elems.iter().all(|e| self.is_eq(e)),

            // Check for derived Eq
            Type::Named(name) => {
                self.has_derived(name, "Eq")
            }

            _ => false,
        }
    }
}

Error Suggestions

impl TypeRegistry {
    pub fn suggest_similar(&self, name: Name) -> Vec<Name> {
        let name_str = self.interner.resolve(name);

        self.types
            .keys()
            .filter(|&n| {
                let s = self.interner.resolve(*n);
                levenshtein_distance(name_str, s) <= 2
            })
            .copied()
            .collect()
    }
}

Built-in Types

Some types are built-in but still registered:

impl TypeRegistry {
    pub fn with_builtins() -> Self {
        let mut registry = Self::new();

        // Option<T>
        registry.types.insert(option_name, TypeDef::Enum(EnumDef {
            name: option_name,
            generics: vec![t_name],
            variants: vec![
                Variant::Tuple(some_name, vec![Type::Generic(t_name)]),
                Variant::Unit(none_name),
            ],
        }));

        // Result<T, E>
        registry.types.insert(result_name, TypeDef::Enum(EnumDef {
            name: result_name,
            generics: vec![t_name, e_name],
            variants: vec![
                Variant::Tuple(ok_name, vec![Type::Generic(t_name)]),
                Variant::Tuple(err_name, vec![Type::Generic(e_name)]),
            ],
        }));

        registry
    }
}