From b02182301331550b58df54d08af0c8f368ea7ce3 Mon Sep 17 00:00:00 2001 From: caina Date: Tue, 19 May 2026 16:14:30 -0300 Subject: [PATCH 01/61] refactor: changed 'checker' to be 'type checker' --- crates/type_checker/Cargo.toml | 9 + crates/type_checker/src/decl.rs | 163 ++++++++ crates/type_checker/src/defaults.rs | 88 ++++ crates/type_checker/src/error.rs | 127 ++++++ crates/type_checker/src/expr.rs | 578 +++++++++++++++++++++++++++ crates/type_checker/src/lib.rs | 423 ++++++++++++++++++++ crates/type_checker/src/statement.rs | 59 +++ crates/type_checker/src/styles.rs | 66 +++ 8 files changed, 1513 insertions(+) create mode 100644 crates/type_checker/Cargo.toml create mode 100644 crates/type_checker/src/decl.rs create mode 100644 crates/type_checker/src/defaults.rs create mode 100644 crates/type_checker/src/error.rs create mode 100644 crates/type_checker/src/expr.rs create mode 100644 crates/type_checker/src/lib.rs create mode 100644 crates/type_checker/src/statement.rs create mode 100644 crates/type_checker/src/styles.rs diff --git a/crates/type_checker/Cargo.toml b/crates/type_checker/Cargo.toml new file mode 100644 index 00000000..054c6be8 --- /dev/null +++ b/crates/type_checker/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "slynx-typechecker" +version = "0.1.0" +edition = "2024" + +[dependencies] +color-eyre.workspace=true +common = {path="../common"} +slynx-hir.path="../hir" diff --git a/crates/type_checker/src/decl.rs b/crates/type_checker/src/decl.rs new file mode 100644 index 00000000..e88b22c7 --- /dev/null +++ b/crates/type_checker/src/decl.rs @@ -0,0 +1,163 @@ +/*! +Type checking for top-level declarations. + +This module contains the logic to type-check HIR declarations: +- Functions: type-check all statements in the function body. +- Objects/Aliases: no-op at this stage (handled elsewhere). +- Components: check property initializers and unify them with the declared + component prop types. We read the component type, perform unifications + locally and then write the updated component type back into the + `TypesModule` to avoid borrow conflicts. + +The implementation intentionally clones the component type's `props` before +resolving expressions so we don't hold a long-lived borrow on the +`TypesModule` while performing potentially mutating operations that also +access the `TypesModule`. +*/ + +use super::{Result, TypeChecker}; + +use slynx_hir::{ + TypeId, + model::{ + ComponentMemberDeclaration, HirComponentExpression, HirDeclaration, HirDeclarationKind, + HirType, + }, +}; + +impl TypeChecker { + /// Type-check a single top-level declaration. + /// + /// For functions this resolves statements; for components it resolves + /// property initializers and updates the component type accordingly. + pub(super) fn check_decl(&mut self, decl: &mut HirDeclaration) -> Result<()> { + match &mut decl.kind { + HirDeclarationKind::StyleSheet { .. } => self.check_stylesheet(decl)?, + // Objects and aliases have no bodies to type-check at this pass. + HirDeclarationKind::Object | HirDeclarationKind::Alias => {} + + HirDeclarationKind::Function { statements, .. } => { + let HirType::Function { return_type, .. } = self.types_module.get_type(&decl.ty) + else { + unreachable!("Type of function should be function"); + }; + let return_type = *return_type; + self.resolve_statements(statements, &return_type)?; + } + HirDeclarationKind::ComponentDeclaration { props, .. } => { + let ty = self.types_module.get_type(&decl.ty).clone(); + let HirType::Component { + props: mut declared_props, + } = ty + else { + unreachable!("Component declaration should have component HirType"); + }; + + for member in props.iter_mut() { + match member { + ComponentMemberDeclaration::Property { + index, + value: maybe_expr, + span, + .. + } => { + if let Some(expr) = maybe_expr { + let expr_ty = self.get_type_of_expr(expr)?; + + *declared_props[*index].prop_type_mut() = + self.unify(declared_props[*index].prop_type(), &expr_ty, span)?; + } + } + + ComponentMemberDeclaration::Child { .. } => {} + } + } + + *self.types_module.get_type_mut(&decl.ty) = HirType::Component { + props: declared_props, + }; + } + } + + Ok(()) + } + + pub(super) fn resolve_component_expression( + &mut self, + expr: &mut HirComponentExpression, + ) -> Result<()> { + match expr { + HirComponentExpression::Specialized(spec) => self.resolve_specialized(spec), + HirComponentExpression::Normal { + name, + properties, + children, + span, + } => { + let HirType::Component { props } = self.types_module.get_type(name) else { + unreachable!("Should've received a component type"); + }; + let props = props.clone(); + + for prop_expr in properties { + let prop_ty = *props[prop_expr.index()].prop_type(); + prop_expr.expr_mut().ty = self.unify(&prop_ty, &prop_expr.expr().ty, span)?; + } + for child in children { + self.resolve_component_expression(child)?; + } + Ok(()) + } + } + } + + /// Resolve and unify a list of component members against a target component type. + /// + /// This function is used when checking component instances: it walks the provided + /// members (properties, children and specializations), resolves values and + /// unifies them against the `target` component's declared prop types. + /// + /// Returns the `target` TypeId on success. + pub(super) fn resolve_component_members( + &mut self, + values: &mut [ComponentMemberDeclaration], + target: TypeId, + ) -> Result { + let target_ty = self.types_module.get_type(&target).clone(); + let HirType::Component { + props: mut declared_props, + } = target_ty + else { + unreachable!("Expected component type when resolving component members"); + }; + + for value in values.iter_mut() { + match value { + ComponentMemberDeclaration::Property { + index, + value: maybe_expr, + span, + .. + } => { + if let Some(expr) = maybe_expr { + let expr_ty = self.get_type_of_expr(expr)?; + let unified = + self.unify(declared_props[*index].prop_type(), &expr_ty, span)?; + + *declared_props[*index].prop_type_mut() = unified; + } + } + + ComponentMemberDeclaration::Child(child) => { + self.resolve_component_expression(child)? + } + } + } + + *self.types_module.get_type_mut(&target) = HirType::Component { + props: declared_props, + }; + + Ok(target) + } +} diff --git a/crates/type_checker/src/defaults.rs b/crates/type_checker/src/defaults.rs new file mode 100644 index 00000000..22a89d7e --- /dev/null +++ b/crates/type_checker/src/defaults.rs @@ -0,0 +1,88 @@ +//* File specific for defining default types on declarations +use slynx_hir::model::{ + HirDeclaration, HirDeclarationKind, HirStyleBlock, HirStyleStatement, HirType, StylesDefinition, +}; + +use crate::{Result, TypeChecker}; + +impl TypeChecker { + pub fn default_stylesheet(&mut self, decl: &mut HirDeclaration) -> Result<()> { + let HirDeclarationKind::StyleSheet { + ref mut statements, .. + } = decl.kind + else { + unreachable!("default_stylesheet should receive a stylesheet declaration"); + }; + + for statement in &mut *statements { + self.default_style_statement(statement)?; + } + Ok(()) + } + + ///Asserts that the given `decl` is a function and sets default values to all of its statements, and ensure its returns are correctly. + ///If the given `decl` is not a function, a panic is going to be received, since it's obviously a bug + pub fn default_function(&mut self, decl: &mut HirDeclaration) -> Result<()> { + let HirDeclarationKind::Function { + ref mut statements, .. + } = decl.kind + else { + unreachable!("default_function should receive a function declaration"); + }; + let HirType::Function { return_type, .. } = self.types_module.get_type(&decl.ty).clone() + else { + unreachable!("A function should have function type"); + }; + let infer = self.types_module.infer_id(); + for statement in &mut *statements { + self.default_statement(statement, &infer)?; + } + self.ensure_function_returns(statements, return_type, &decl.span)?; + Ok(()) + } + + ///Sets the default types on the given `decl`. This replaces the infer types on everything on the given `decl` with the correct(or default) type + pub fn set_default(&mut self, decl: &mut HirDeclaration) -> Result<()> { + match decl.kind { + HirDeclarationKind::Object | HirDeclarationKind::Alias => {} + HirDeclarationKind::Function { .. } => self.default_function(decl)?, + HirDeclarationKind::ComponentDeclaration { ref mut props, .. } => { + self.resolve_component_members(props, decl.ty)?; + } + HirDeclarationKind::StyleSheet { .. } => self.default_stylesheet(decl)?, + } + Ok(()) + } + + pub fn default_style_statement(&mut self, statement: &mut HirStyleStatement) -> Result<()> { + match statement { + HirStyleStatement::Statement(s) => { + let infer = self.types_module.infer_id(); + self.default_statement(s, &infer)?; + } + HirStyleStatement::Styles(styleblocks) => { + for block in styleblocks { + self.default_style_block(block)?; + } + } + } + Ok(()) + } + + pub fn default_style_block(&mut self, block: &mut HirStyleBlock) -> Result<()> { + for definition in &mut block.definitions { + self.default_style_definition(definition)?; + } + Ok(()) + } + + pub fn default_style_definition(&mut self, definition: &mut StylesDefinition) -> Result<()> { + self.default_expr(&mut definition.expr)?; + definition.expr.ty = self.unify( + &definition.expr.ty, + &definition.expected_type, + &definition.span, + )?; + Ok(()) + } +} diff --git a/crates/type_checker/src/error.rs b/crates/type_checker/src/error.rs new file mode 100644 index 00000000..75b8584c --- /dev/null +++ b/crates/type_checker/src/error.rs @@ -0,0 +1,127 @@ +//! Type checking errors for the Slynx type checker. +//! +//! This module defines the error types used during type checking, including +//! type mismatch errors, cyclic type errors, and component-related errors. + +use common::Span; +use slynx_hir::{DeclarationId, model::HirType}; + +/// Represents the reason for an incompatible component error. +/// +/// This enum is used to specify the specific reason why two components are +/// incompatible, such as having a different number of properties. +#[derive(Debug)] +pub enum IncompatibleComponentReason { + DifferentPropAmount { rhs: usize, lhs: usize }, +} + +/// Represents a type error that occurred during type checking. +/// +/// This struct holds the type error kind and the span where the error occurred. +#[derive(Debug)] +pub struct TypeError { + pub kind: TypeErrorKind, + pub span: Span, +} + +/// Represents the kind of type error that occurred. +/// +/// This enum is used to specify the specific type error that occurred, such as +/// a type mismatch or a cyclic type reference. +#[derive(Debug)] +pub enum TypeErrorKind { + CiclicType { + ty: HirType, + }, + IncompatibleComponent { + reason: IncompatibleComponentReason, + }, + IncompatibleTypes { + expected: HirType, + received: HirType, + }, + InvalidTupleAccessTarget { + received: HirType, + }, + InvalidTupleIndex { + index: usize, + length: usize, + }, + InvalidFuncallArgLength { + expected_length: usize, + received_length: usize, + }, + InvalidFunctionCallTarget { + declaration: DeclarationId, + received: HirType, + }, + MissingReturnValue { + expected: HirType, + }, + NotAStruct(HirType), + Unrecognized, +} + +impl TypeError { + pub fn invalid_funcall_args(expected: usize, received: usize, span: Span) -> Self { + Self { + span, + kind: TypeErrorKind::InvalidFuncallArgLength { + expected_length: expected, + received_length: received, + }, + } + } +} + +impl std::fmt::Display for TypeError { + /// Formats the type error as a string. + /// + /// This implementation uses the error kind to generate a human-readable error + /// message, including the expected and received types for type mismatch errors. + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let out = match &self.kind { + TypeErrorKind::CiclicType { ty } => { + format!("The type '{ty:?}' is cyclic and cannot exist without recursion") + } + TypeErrorKind::IncompatibleComponent { reason } => { + format!("The component is incompatible because of '{reason:?}'") + } + TypeErrorKind::IncompatibleTypes { expected, received } => format!( + "Incompatible types. Was expecting to receive type '{expected:?}' instead got type '{received:?}'" + ), + TypeErrorKind::InvalidTupleAccessTarget { received } => { + format!("Type '{received:?}' does not support tuple-style access") + } + TypeErrorKind::InvalidTupleIndex { index, length } => { + format!( + "Tuple index {index} is out of bounds. The tuple only exposes {length} fields" + ) + } + TypeErrorKind::InvalidFuncallArgLength { + expected_length, + received_length, + } => format!( + "Invalid function call arg length. Expected {expected_length} args but received {received_length}" + ), + TypeErrorKind::InvalidFunctionCallTarget { + declaration, + received, + } => format!( + "Invalid function call target at declaration {declaration:?}. Expected function type but received {received:?}" + ), + TypeErrorKind::MissingReturnValue { expected } => { + format!("Function is missing a return value of type '{expected:?}'") + } + TypeErrorKind::NotAStruct(t) => { + format!("Using type {t:?} as a Struct, even though it isn't") + } + TypeErrorKind::Unrecognized => { + "Type checker could not resolve the requested symbol/type".to_string() + } + }; + write!(f, "{out}") + } +} + +impl std::error::Error for TypeError {} diff --git a/crates/type_checker/src/expr.rs b/crates/type_checker/src/expr.rs new file mode 100644 index 00000000..daeb5bf2 --- /dev/null +++ b/crates/type_checker/src/expr.rs @@ -0,0 +1,578 @@ +//! Type checking logic for expressions and statements. +//! +//! This module implements the core type-checking pass for the Slynx HIR. +//! It handles the resolution of function bodies, component property +//! initialization, and ensures type safety through unification. + +use super::{Result, TypeChecker}; + +use crate::error::{TypeError, TypeErrorKind}; +use common::Span; +use slynx_hir::{ + DeclarationId, TypeId, + model::{ + FieldMethod, HirComponentExpression, HirExpression, HirExpressionKind, + HirSpecializedComponentExpression, HirStatement, HirStatementKind, HirStyleUsage, HirType, + }, +}; +impl TypeChecker { + /// Resolves the struct type from a reference type. + /// + /// This function recursively follows reference types until it finds a struct type. + /// If the type is not a struct, a `TypeError` is returned. + pub fn get_struct_from_ref(&self, ty: &TypeId, span: &Span) -> Result { + match self.types_module.get_type(ty) { + HirType::Reference { rf, .. } => self.get_struct_from_ref(rf, span), + HirType::Struct { .. } => Ok(*ty), + v => Err(TypeError { + kind: TypeErrorKind::NotAStruct(v.clone()), + span: *span, + }), + } + } + + /// Resolves the type of a field access expression. + /// + /// This function resolves the type of a field access expression by following + /// the field method (type or variable) and updating the field index. + fn resolve_field_access_type( + &mut self, + field_ty: &mut TypeId, + field_index: &mut usize, + span: &Span, + ) -> Result { + let HirType::Field(field_method) = self.types_module.get_type(field_ty).clone() else { + return self.resolve(field_ty, span); + }; + match field_method { + FieldMethod::Type(rf, index) => { + *field_index = index; + *field_ty = self + .types_module + .insert_unnamed_type(HirType::Field(FieldMethod::Type(rf, index))); + self.resolve(field_ty, span) + } + FieldMethod::Tuple(rf, index) => { + *field_index = index; + *field_ty = self + .types_module + .insert_unnamed_type(HirType::Field(FieldMethod::Tuple(rf, index))); + self.resolve(field_ty, span) + } + FieldMethod::Variable(variable_id, field_name) => { + // Field accesses first enter the checker attached to the source + // variable name. Resolve that symbolic access once and rewrite it + // into an indexed field lookup for the rest of the pipeline. + let object_ty = *self + .types_module + .get_variable(&variable_id) + .ok_or(TypeError { + kind: TypeErrorKind::Unrecognized, + span: *span, + })?; + let layout_ty = self.get_object_layout_type(&object_ty, span)?; + + let Some(index) = self + .structs + .get(&layout_ty) + .expect("Type should be defined") + .iter() + .position(|field| *field == field_name) + else { + return Err(TypeError { + kind: TypeErrorKind::Unrecognized, + span: *span, + }); + }; + + *field_index = index; + *self.types_module.get_type_mut(field_ty) = + HirType::Field(FieldMethod::Type(object_ty, index)); + self.resolve(field_ty, span) + } + } + } + + /// Gets the signature of the provided `declaration` id expecting its a function type. + /// + /// This function retrieves the function type from the declarations and returns + /// the argument types and return type. If the declaration is not a function, + /// a `TypeError` is returned. + fn get_function_signature( + &self, + declaration: DeclarationId, + span: &Span, + ) -> Result<(Vec, TypeId)> { + let Some(function_ty) = self + .declarations + .get(declaration.as_raw() as usize) + .copied() + else { + return Err(TypeError { + kind: TypeErrorKind::Unrecognized, + span: *span, + }); + }; + + let resolved = self.types_module.get_type(&function_ty).clone(); + let HirType::Function { args, return_type } = resolved else { + return Err(TypeError { + kind: TypeErrorKind::InvalidFunctionCallTarget { + declaration, + received: resolved, + }, + span: *span, + }); + }; + Ok((args, return_type)) + } + ///Asserts that `args` has same length as `expected_args` + fn check_function_call_len( + &self, + args: &[HirExpression], + expected_args: &[TypeId], + span: &Span, + ) -> Result<()> { + if args.len() != expected_args.len() { + return Err(TypeError { + kind: TypeErrorKind::InvalidFuncallArgLength { + expected_length: expected_args.len(), + received_length: args.len(), + }, + span: *span, + }); + } + Ok(()) + } + + /// Checks that `args` have the same types as `expected`. + /// + /// This function checks that each argument in `args` has a type that can be unified with + /// the corresponding type in `expected`. If a type mismatch occurs, a `TypeError` is returned. + fn check_function_call_args( + &mut self, + args: &mut [HirExpression], + expected: &[TypeId], + ) -> Result<()> { + debug_assert_eq!( + args.len(), + expected.len(), + "arity must be checked before arg validation" + ); + for (arg, expected_ty) in args.iter_mut().zip(expected.iter().copied()) { + arg.ty = self.get_type_of_expr(arg)?; + arg.ty = self.unify(&arg.ty, &expected_ty, &arg.span)?; + } + Ok(()) + } + + /// Defaults the provided `args` to their expected types. + /// + /// This function defaults each argument in `args` to its expected type. If a type mismatch + /// occurs, a `TypeError` is returned. + fn default_function_call_args( + &mut self, + args: &mut [HirExpression], + expected: &[TypeId], + ) -> Result<()> { + debug_assert_eq!( + args.len(), + expected.len(), + "arity must be checked before arg defaulting" + ); + + for (arg, expected_ty) in args.iter_mut().zip(expected.iter().copied()) { + self.default_expr(arg)?; + arg.ty = self.unify(&arg.ty, &expected_ty, &arg.span)?; + } + + Ok(()) + } + + pub fn resolve_style_usage(&mut self, style_usage: &mut HirStyleUsage) -> Result<()> { + let HirType::Style { args } = self + .types_module + .get_type(&self.declarations[style_usage.style.as_raw() as usize]) + else { + unreachable!("Type of style should be style"); + }; + let args = args.clone(); + for (idx, param) in style_usage.params.iter_mut().enumerate() { + param.ty = self.unify(&args[idx], ¶m.ty, ¶m.span)?; + } + + Ok(()) + } + + /// Resolves the type of a specialized component expression. + /// + /// This function resolves the type of a specialized component expression by + /// updating the type of the expression and its children. If a type mismatch + /// occurs, a `TypeError` is returned. + pub(super) fn resolve_specialized( + &mut self, + spec: &mut HirSpecializedComponentExpression, + ) -> Result<()> { + match spec { + HirSpecializedComponentExpression::Text { text, style } => { + text.ty = self.get_type_of_expr(text)?; + text.ty = self.unify(&text.ty, &self.types_module.str_id(), &text.span)?; + if let Some(style) = style { + self.resolve_style_usage(style)?; + } + } + HirSpecializedComponentExpression::Div { children, style } => { + for child in children { + self.resolve_component_expression(child)?; + } + if let Some(style) = style { + self.resolve_style_usage(style)?; + } + } + } + + Ok(()) + } + /// Resolves a while statement, checking that the condition is a boolean and the body is a sequence of statements. + fn resolve_statement_while( + &mut self, + condition: &mut HirExpression, + body: &mut [HirStatement], + ty: &TypeId, + ) -> Result<()> { + condition.ty = self.get_type_of_expr(condition)?; + let bool_id = self.types_module.bool_id(); + + condition.ty = self.unify(&condition.ty, &bool_id, &condition.span)?; + + self.resolve_statements(body, ty)?; + Ok(()) + } + /// Resolves an assignment statement, checking that the types match. + fn resolve_statement_assign( + &mut self, + lhs: &mut HirExpression, + value: &mut HirExpression, + span: &Span, + ) -> Result<()> { + let refty = match self.types_module.get_type(&lhs.ty) { + HirType::Field(FieldMethod::Type(_, _)) | HirType::Field(FieldMethod::Tuple(_, _)) => { + lhs.ty + } + HirType::Field(FieldMethod::Variable(..)) => { + let HirExpressionKind::FieldAccess { + ref mut field_index, + .. + } = lhs.kind + else { + unreachable!(); + }; + let _ = self.resolve_field_access_type(&mut lhs.ty, field_index, &lhs.span)?; + lhs.ty + } + HirType::VarReference(_) => lhs.ty, + _ => unreachable!(), + }; + + let ty = self.resolve(&lhs.ty, span)?; + lhs.ty = refty; + value.ty = self.get_type_of_expr(value)?; + value.ty = self.unify(&ty, &value.ty, &value.span)?; + Ok(()) + } + + pub(super) fn resolve_statement( + &mut self, + statement: &mut HirStatement, + return_type: &TypeId, + ) -> Result<()> { + let span = &statement.span; + match &mut statement.kind { + HirStatementKind::While { condition, body } => { + self.resolve_statement_while(condition, body, return_type)? + } + + HirStatementKind::Variable { value, .. } => { + value.ty = self.get_type_of_expr(value)?; + } + + HirStatementKind::Return { expr } => { + expr.ty = self.get_type_of_expr(expr)?; + expr.ty = self.unify(&expr.ty, return_type, span)?; + } + + HirStatementKind::Expression { expr } => { + expr.ty = self.get_type_of_expr(expr)?; + } + + HirStatementKind::Assign { lhs, value } => { + self.resolve_statement_assign(lhs, value, span)? + } + } + Ok(()) + } + + /// Resolves the types of the provided `statements`. + /// + /// This function resolves the types of the provided `statements` by updating + /// the type of each statement and its children. If a type mismatch occurs, + /// a `TypeError` is returned. + pub(super) fn resolve_statements( + &mut self, + statements: &mut [HirStatement], + return_type: &TypeId, + ) -> Result<()> { + for statement in statements { + self.resolve_statement(statement, return_type)?; + } + + Ok(()) + } + + /// Resolves the types of the provided `fields` asserting that `ty` is a type for a struct, + /// and the types of `fields` match the type expected by the fields of the given struct type. + pub(super) fn resolve_object_types( + &mut self, + ty: &HirType, + fields: &mut [HirExpression], + ) -> Result<()> { + let HirType::Struct { fields: fields_tys } = ty else { + unreachable!("When resolving object types, a type 'struct' should be provided"); + }; + + for (idx, f) in fields.iter_mut().enumerate() { + f.ty = self.unify(&fields_tys[idx], &f.ty, &f.span)?; + } + + Ok(()) + } + + /// Resolves the type of a reference expression, returning the type it references. + pub fn get_type_from_ref(&self, ref_ty: TypeId) -> &HirType { + if let HirType::Reference { rf, .. } = self.types_module.get_type(&ref_ty) { + self.types_module.get_type(rf) + } else { + unreachable!("The provided ref_ty should be of type Reference"); + } + } + /// Helper to resolve a branch (then/else) that may be present or absent. + /// Accepts `Option<&mut Vec>` because the else branch is optional + /// in the HIR representation. `expected` is the expected TypeId for statements. + fn resolve_branch( + &mut self, + branch: Option<&mut Vec>, + expected: &TypeId, + ) -> Result { + let Some(stmts) = branch else { + return Ok(self.types_module.void_id()); + }; + let Some((last, rest)) = stmts.split_last_mut() else { + return Ok(self.types_module.void_id()); + }; + for stmt in rest { + self.default_statement(stmt, expected)?; + } + match &mut last.kind { + HirStatementKind::Expression { expr } => self.get_type_of_expr(expr), + _ => Ok(self.types_module.void_id()), + } + } + + /// Retrieves the type of the provided `expr`. Returns infer if it could not be inferred. + pub(super) fn get_type_of_expr(&mut self, expr: &mut HirExpression) -> Result { + let expected = expr.ty; + + let calc = match expr.kind { + HirExpressionKind::Tuple(ref mut fields) => { + let field_types = fields + .iter_mut() + .map(|f| self.get_type_of_expr(f)) + .collect::>>()?; + self.types_module.add_tuple_type(field_types) + } + HirExpressionKind::If { + ref mut condition, + ref mut else_branch, + ref mut then_branch, + } => { + let if_ty = self.resolve_branch(Some(then_branch), &expected)?; + let else_ty = self.resolve_branch(else_branch.as_mut(), &expected)?; + + let result_ty = self.unify(&if_ty, &else_ty, &expr.span)?; + let cond_ty = self.get_type_of_expr(condition)?; + condition.ty = + self.unify(&cond_ty, &self.types_module.bool_id(), &condition.span)?; + + result_ty + } + HirExpressionKind::FunctionCall { + name, + args: ref mut f_args, + } => { + let (args, return_type) = self.get_function_signature(name, &expr.span)?; + + self.check_function_call_len(f_args, &args, &expr.span)?; + self.check_function_call_args(f_args, &args)?; + + return_type + } + HirExpressionKind::Int(_) => self.types_module.int_id(), + HirExpressionKind::Float(_) => self.types_module.float_id(), + HirExpressionKind::StringLiteral(_) => self.types_module.str_id(), + HirExpressionKind::Binary { + ref mut lhs, + ref mut rhs, + ref op, + } => { + let lhs_ty = self.get_type_of_expr(lhs)?; + let rhs_ty = self.get_type_of_expr(rhs)?; + + let out = self.unify(&lhs_ty, &rhs_ty, &expr.span)?; + + if op.is_logical() { + self.types_module.bool_id() + } else { + out + } + } + HirExpressionKind::Identifier(_) => self.resolve(&expr.ty, &expr.span)?, + HirExpressionKind::Component(HirComponentExpression::Specialized(_)) => { + self.types_module.generic_component_id() + } + HirExpressionKind::Component(HirComponentExpression::Normal { name, .. }) => name, + HirExpressionKind::Object { + name, + ref mut fields, + } => { + let obj = self.get_type_from_ref(name).clone(); + self.resolve_object_types(&obj, fields)?; + name + } + + HirExpressionKind::FieldAccess { + ref mut field_index, + expr: ref mut e, + } => { + self.get_type_of_expr(e)?; + self.resolve_field_access_type(&mut expr.ty, field_index, &expr.span)? + } + HirExpressionKind::Bool(_) => self.types_module.bool_id(), + }; + + expr.ty = self.unify(&expected, &calc, &expr.span)?; + Ok(expr.ty) + } + + /// Sets the default type on the provided `expr`. + /// + /// This function sets the default type on the provided `expr` by recursively + /// defaulting each field in the tuple and collecting their types. The resulting + pub(super) fn default_expr(&mut self, expr: &mut HirExpression) -> Result<()> { + match expr.kind { + HirExpressionKind::Tuple(ref mut fields) => { + let types = fields + .iter_mut() + .map(|f| { + self.default_expr(f)?; + Ok(f.ty) + }) + .collect::>>()?; + + let tuple_ty = self.types_module.add_tuple_type(types); + + expr.ty = self.unify(&tuple_ty, &expr.ty, &expr.span)?; + } + HirExpressionKind::If { + ref mut condition, + ref mut then_branch, + ref mut else_branch, + } => { + self.default_expr(condition)?; + condition.ty = + self.unify(&condition.ty, &self.types_module.bool_id(), &condition.span)?; + let if_ty = self.resolve_branch(Some(then_branch), &expr.ty)?; + let else_ty = self.resolve_branch(else_branch.as_mut(), &expr.ty)?; + self.unify(&if_ty, &else_ty, &expr.span)?; + } + HirExpressionKind::FunctionCall { + name, + args: ref mut f_args, + } => { + let (args, return_type) = self.get_function_signature(name, &expr.span)?; + self.check_function_call_len(f_args, &args, &expr.span)?; + self.default_function_call_args(f_args, &args)?; + expr.ty = self.unify(&expr.ty, &return_type, &expr.span)?; + } + HirExpressionKind::Bool(_) => { + expr.ty = self.unify(&expr.ty, &self.types_module.bool_id(), &expr.span)? + } + HirExpressionKind::StringLiteral(_) => { + expr.ty = self.unify(&expr.ty, &self.types_module.str_id(), &expr.span)? + } + HirExpressionKind::Int(_) => { + expr.ty = self.unify(&expr.ty, &self.types_module.int_id(), &expr.span)? + } + HirExpressionKind::Float(_) => { + expr.ty = self.unify(&expr.ty, &self.types_module.float_id(), &expr.span)? + } + HirExpressionKind::Binary { + ref mut lhs, + ref mut rhs, + op, + } => { + self.default_expr(rhs)?; + self.default_expr(lhs)?; + if op.is_logical() { + expr.ty = self.types_module.bool_id(); + } else { + expr.ty = self.unify(&rhs.ty, &lhs.ty, &expr.span)?; + } + } + HirExpressionKind::Identifier(_) => { + expr.ty = self.resolve(&expr.ty, &expr.span)?; + } + + HirExpressionKind::Object { .. } => { + expr.ty = self.resolve(&expr.ty, &expr.span)?; + } + HirExpressionKind::FieldAccess { + expr: ref mut parent, + .. + } => { + parent.ty = self.resolve(&parent.ty, &expr.span)?; + expr.ty = self.resolve(&expr.ty, &expr.span)?; + } + HirExpressionKind::Component(HirComponentExpression::Normal { + name, + ref mut properties, + span, + .. + }) => { + let HirType::Component { props } = self.types_module.get_type(&name) else { + unreachable!("Type of component should be Component"); + }; + let props = props.clone(); + for prop in properties { + prop.expr_mut().ty = + self.unify(props[prop.index()].prop_type(), &prop.expr().ty, &span)?; + } + //todo: default component expression + + expr.ty = name; + } + HirExpressionKind::Component(HirComponentExpression::Specialized( + HirSpecializedComponentExpression::Text { ref mut text, .. }, + )) => { + let str_id = self.types_module.str_id(); + text.ty = self.unify(&text.ty, &str_id, &expr.span)?; + expr.ty = self.types_module.generic_component_id(); //change to SpecializedComponent later + } + HirExpressionKind::Component(HirComponentExpression::Specialized( + HirSpecializedComponentExpression::Div { children: _, .. }, + )) => { + //todo: default component expression + } + } + Ok(()) + } +} diff --git a/crates/type_checker/src/lib.rs b/crates/type_checker/src/lib.rs new file mode 100644 index 00000000..4b99291e --- /dev/null +++ b/crates/type_checker/src/lib.rs @@ -0,0 +1,423 @@ +//! The core type-checking engine for the Slynx compiler. +//! +//! This module implements a two-phase type inference and validation system: +//! +//! 1. **Resolution Phase**: Iterates through the HIR to resolve `Infer` types +//! into concrete types by analyzing expressions, function calls, and +//! component properties. Functions involved in this phase are typically +//! named `resolve_*`. +//! +//! 2. **Default/Fallback Phase**: After the initial pass, any remaining +//! ambiguous types that couldn't be strictly inferred are assigned their +//! language-default types. Functions for this phase are named `set_default` +//! or `default_*`. +//! +//! The `TypeChecker` mutates the `TypesModule` and the `SlynxHir` in-place, +//! ensuring that subsequent compilation stages (like Codegen) have access to +//! fully concrete type information. + +mod decl; +mod defaults; +pub mod error; +mod expr; +mod statement; +mod styles; +use std::collections::HashMap; + +use common::SymbolPointer; + +use crate::error::{IncompatibleComponentReason, TypeError, TypeErrorKind}; + +use common::Span; +use slynx_hir::model::{ComponentProperty, HirStatement, HirStatementKind}; +use slynx_hir::modules::TypesModule; +use slynx_hir::{ + SlynxHir, TypeId, + model::{FieldMethod, HirType}, +}; + +pub type Result = std::result::Result; + +#[derive(Debug)] +///The type checker of slynx. Slynx has 2 phases of type checking by now. The first phase is to get information about the types on all the HIR +///and try to type them properly. Thus, the HIR might come with infers, and the type checker tries to get rid of them with their actual type. This is made with all function +///whose got `resolve` on their names. Since the idea is for them to try to resolve the types. The second part is that, after checking all the IR, if some code could not be infered, +///then it starts setting the default values for every of them. This is made with functions named as `default`. So every infers are get rid because the default type is provided. +pub struct TypeChecker { + ///A an array of declaration types + declarations: Vec, + ///The types module from the HIR to be mutated + types_module: TypesModule, + /// The type of everything that is expected to have some + types: HashMap, + structs: HashMap>, +} + +impl TypeChecker { + fn get_object_layout_type(&self, ty: &TypeId, span: &Span) -> Result { + let mut current = *ty; + + loop { + match self.types_module.get_type(¤t) { + HirType::Reference { rf, .. } => match self.types_module.get_type(rf) { + HirType::Struct { .. } => return Ok(current), + HirType::Reference { .. } => current = *rf, + other => { + return Err(TypeError { + kind: TypeErrorKind::NotAStruct(other.clone()), + span: *span, + }); + } + }, + HirType::VarReference(variable_id) => { + current = + self.types_module + .get_variable(variable_id) + .copied() + .ok_or(TypeError { + kind: TypeErrorKind::Unrecognized, + span: *span, + })?; + } + other => { + return Err(TypeError { + kind: TypeErrorKind::NotAStruct(other.clone()), + span: *span, + }); + } + } + } + } + + fn resolve_tuple_index_type( + &mut self, + ty: &TypeId, + index: usize, + span: &Span, + ) -> Result { + // Tuple access stays explicit all the way to the checker so numeric + // postfixes never get confused with object field lookups. + let resolved_ty = self.resolve(ty, span)?; + let HirType::Tuple { fields } = self.types_module.get_type(&resolved_ty).clone() else { + return Err(TypeError { + kind: TypeErrorKind::InvalidTupleAccessTarget { + received: self.types_module.get_type(&resolved_ty).clone(), + }, + span: *span, + }); + }; + + fields.get(index).copied().ok_or(TypeError { + kind: TypeErrorKind::InvalidTupleIndex { + index, + length: fields.len(), + }, + span: *span, + }) + } + + /// Checks the types of the provided `hir` and mutates them if needed. Any that could not be inferred but, yet is valid, is + /// at the end, returned as it's default type. Returns the type module to be used on the next steps + pub fn check(hir: &mut SlynxHir) -> Result { + let mut inner = Self { + types: HashMap::new(), + structs: std::mem::take(&mut hir.modules.declarations_module.objects), + types_module: std::mem::take(&mut hir.modules.types_module), + declarations: Vec::new(), + }; + // DeclarationId is currently assigned linearly in hoisting order, + // so declaration type lookup can stay append-only here. + for decl in &hir.declarations { + debug_assert_eq!( + decl.id.as_raw() as usize, + inner.declarations.len(), + "declaration id must stay linear with declarations table", + ); + inner.declarations.push(decl.ty); + } + for decl in &mut hir.declarations { + inner.check_decl(decl)?; + } + // for the ones that couldn't be inferred, put their default + for decl in &mut hir.declarations { + inner.set_default(decl)?; + } + + Ok(inner.types_module) + } + + fn substitute(&mut self, id: TypeId, ty: HirType) { + self.types.insert(id, ty); + } + + /// Resolves recursively the names of the types. If A -> B, B -> int; then we assume that A -> int + fn resolve(&mut self, ty: &TypeId, span: &Span) -> Result { + let referedty = self.types_module.get_type(ty).clone(); + match referedty { + HirType::Field(FieldMethod::Type(rf, index)) => { + let ty = self.resolve(&rf, span)?; + let struct_id = self.get_struct_from_ref(&ty, span)?; + if let HirType::Struct { fields } = self.types_module.get_type(&struct_id) { + Ok(fields[index]) + } else { + Err(TypeError { + kind: TypeErrorKind::IncompatibleTypes { + expected: self.types_module.get_type(&ty).clone(), + received: HirType::Struct { fields: Vec::new() }, + }, + span: *span, + }) + } + } + HirType::Field(FieldMethod::Tuple(rf, index)) => { + self.resolve_tuple_index_type(&rf, index, span) + } + HirType::Field(FieldMethod::Variable(var_id, n)) => { + let object_ty = *self.types_module.get_variable(&var_id).ok_or(TypeError { + kind: TypeErrorKind::Unrecognized, + span: *span, + })?; + let layout_ty = self.get_object_layout_type(&object_ty, span)?; + let concrete_type = self + .types_module + .get_type(&self.get_struct_from_ref(&object_ty, span)?); + let s_fields = self.structs.get(&layout_ty).ok_or(TypeError { + kind: TypeErrorKind::Unrecognized, + span: *span, + })?; + let HirType::Struct { fields } = concrete_type else { + unreachable!("Type should be a struct. Fields only happen to structs"); + }; + if let Some(index) = s_fields.iter().position(|name| *name == n) { + Ok(fields[index]) + } else { + Err(TypeError { + kind: TypeErrorKind::Unrecognized, + span: *span, + }) + } + } + HirType::Reference { rf, .. } => Ok(rf), + HirType::VarReference(rf) => { + if let Some(ty) = self.types_module.get_variable(&rf).cloned() { + Ok(self.resolve(&ty, span)?) + } else { + unreachable!("Variable type should be defined here"); + } + } + HirType::Component { props } => { + let mut resolved_props = { + let mut tys = Vec::with_capacity(props.len()); + for prop in props { + let mut prop = prop.clone(); + *prop.prop_type_mut() = self.resolve(prop.prop_type(), span)?; + tys.push(prop) + } + tys + }; + let HirType::Component { props } = self.types_module.get_type_mut(ty) else { + unreachable!(); + }; + props.clear(); + props.append(&mut resolved_props); + Ok(*ty) + } + + _ => Ok(*ty), + } + } + + /// Tries to unify types `a` and `b` if possible + fn unify(&mut self, a: &TypeId, b: &TypeId, span: &Span) -> Result { + let a = self.resolve(a, span)?; + let b = self.resolve(b, span)?; + + match (&a, &b) { + (a, b) if a == b => Ok(*a), + (out, inf) | (inf, out) + if *out != self.types_module.infer_id() && *inf == self.types_module.infer_id() => + { + Ok(*out) + } + (a, b) => { + let concrete_a = self.types_module.get_type(a).clone(); + let concrete_b = self.types_module.get_type(b).clone(); + + match (&concrete_a, &concrete_b) { + (_, HirType::Infer) => Ok(*a), + (HirType::Infer, _) => Ok(*b), + (HirType::Reference { rf, .. }, _) => self.unify_with_ref(*rf, *b, span), + (_, HirType::Reference { rf, .. }) => self.unify_with_ref(*rf, *b, span), + ( + HirType::Component { props: aprops }, + HirType::Component { props: bprops }, + ) => { + if aprops.len() != bprops.len() { + return Err(TypeError { + kind: TypeErrorKind::IncompatibleComponent { + reason: IncompatibleComponentReason::DifferentPropAmount { + rhs: aprops.len(), + lhs: bprops.len(), + }, + }, + span: *span, + }); + } + let mut unified_props = Vec::with_capacity(aprops.len()); + for (prop_a, prop_b) in aprops.iter().zip(bprops.iter()) { + let unified_prop = + self.unify(prop_a.prop_type(), prop_b.prop_type(), span)?; + unified_props.push(ComponentProperty::new( + *prop_a.visibility(), + prop_a.name().to_string(), + unified_prop, + )); + } + let HirType::Component { props } = self.types_module.get_type_mut(a) else { + unreachable!() + }; + props.clear(); + props.extend_from_slice(&unified_props); + let HirType::Component { props } = self.types_module.get_type_mut(b) else { + unreachable!() + }; + props.clear(); + props.extend_from_slice(&unified_props); + Ok(*a) + } + (HirType::Component { .. }, HirType::GenericComponent) => Ok(*a), + (HirType::GenericComponent, HirType::Component { .. }) => Ok(*b), + + (HirType::Struct { fields: f1 }, HirType::Struct { fields: f2 }) => { + if f1.len() != f2.len() { + Err(TypeError { + kind: TypeErrorKind::IncompatibleTypes { + expected: concrete_a, + received: concrete_b, + }, + span: *span, + }) + } else { + for idx in 0..f1.len() { + self.unify(&f1[idx], &f2[idx], span)?; + } + Ok(*a) + } + } + (HirType::Tuple { fields: f1 }, HirType::Tuple { fields: f2 }) => { + if f1.len() != f2.len() { + Err(TypeError { + kind: TypeErrorKind::IncompatibleTypes { + expected: concrete_a, + received: concrete_b, + }, + span: *span, + }) + } else { + let mut new_fields = Vec::with_capacity(f1.len()); + + for (t1, t2) in f1.iter().zip(f2.iter()) { + let unified = self.unify(t1, t2, span)?; + new_fields.push(unified); + } + + Ok(self.types_module.add_tuple_type(new_fields)) + } + } + (HirType::VarReference(rf1), HirType::VarReference(rf2)) => { + let Some(rf1) = self.types_module.get_variable(rf1).cloned() else { + unreachable!("Variable should have already been declared") + }; + let Some(rf2) = self.types_module.get_variable(rf2).cloned() else { + unreachable!("Variable2 should have already been declared") + }; + self.unify(&rf1, &rf2, span) + } + + (_, _) => Err(TypeError { + kind: TypeErrorKind::IncompatibleTypes { + expected: concrete_b, + received: concrete_a, + }, + span: *span, + }), + } + } + } + } + + fn unify_with_ref(&mut self, rf: TypeId, ty: TypeId, span: &Span) -> Result { + let resolved_ref = self.resolve(&rf, span)?; + if !matches!( + self.types_module.get_type(&resolved_ref), + HirType::Reference { .. } + ) { + return self.unify(&resolved_ref, &ty, span); + } + if let HirType::Reference { rf: refe, .. } = self.types_module.get_type(&ty) + && rf == *refe + { + return Ok(ty); + } + let ty = self.types_module.get_type(&ty); + if self.recursive_ty(rf, ty) { + return Err(TypeError { + kind: TypeErrorKind::CiclicType { ty: ty.clone() }, + span: *span, + }); + } + self.substitute(rf, ty.clone()); + let ty = self.types_module.insert_unnamed_type(HirType::Reference { + rf, + generics: Vec::new(), + }); + Ok(ty) + } + + /// Checks if the provided `ty` is recursive + fn recursive_ty(&self, ty_ref: TypeId, ty: &HirType) -> bool { + match ty { + HirType::Reference { rf, .. } => { + if ty_ref == *rf { + true + } else if let Some(resolved) = self.types.get(rf) { + self.recursive_ty(ty_ref, resolved) + } else { + false + } + } + HirType::Component { props } => props.iter().any(|prop| { + self.recursive_ty(ty_ref, self.types_module.get_type(prop.prop_type())) + }), + _ => false, + } + } + + fn ensure_function_returns( + &mut self, + statements: &[HirStatement], + return_type: TypeId, + span: &Span, + ) -> Result<()> { + let return_type = self.resolve(&return_type, span)?; + if return_type == self.types_module.void_id() { + return Ok(()); + } + + // Functions return implicitly through their tail expression today, + // so a non-void function must end with a lowered `Return`. + if matches!( + statements.last().map(|statement| &statement.kind), + Some(HirStatementKind::Return { .. }) + ) { + return Ok(()); + } + + Err(TypeError { + kind: TypeErrorKind::MissingReturnValue { + expected: self.types_module.get_type(&return_type).clone(), + }, + span: *span, + }) + } +} diff --git a/crates/type_checker/src/statement.rs b/crates/type_checker/src/statement.rs new file mode 100644 index 00000000..860fc5db --- /dev/null +++ b/crates/type_checker/src/statement.rs @@ -0,0 +1,59 @@ +//! Type-checking logic for statements in the Slynx compiler. +//! +//! This module provides the implementation for type-checking statements, including +//! variable declarations, assignments, and expressions. It uses the `TypeChecker` +//! context to resolve types and unify them with expected types. + +use crate::{Result, TypeChecker}; +use slynx_hir::{ + TypeId, + model::{HirStatement, HirStatementKind}, +}; + +impl TypeChecker { + ///Sets the default value on the given `statement` + pub(super) fn default_statement( + &mut self, + statement: &mut HirStatement, + expected: &TypeId, + ) -> Result<()> { + match &mut statement.kind { + HirStatementKind::Variable { name, value } => { + // Ensure the initializer expression is fully typed and propagate it to the variable. + self.default_expr(value)?; + + let ty = self.get_type_of_expr(value)?; + + value.ty = ty; + self.types_module.insert_variable(*name, ty); + } + + HirStatementKind::Assign { lhs, value } => { + let ty = self.resolve(&lhs.ty, &lhs.span)?; + value.ty = self.unify(&ty, &value.ty, &value.span)?; + } + + HirStatementKind::Expression { expr } => self.default_expr(expr)?, + + HirStatementKind::Return { expr } => { + self.default_expr(expr)?; + let unify = self.unify(&expr.ty, expected, &statement.span)?; + + expr.ty = unify; + } + + HirStatementKind::While { condition, body } => { + condition.ty = self.get_type_of_expr(condition)?; + let bool_id = self.types_module.bool_id(); + + condition.ty = self.unify(&condition.ty, &bool_id, &condition.span)?; + + for stmt in body { + self.default_statement(stmt, expected)?; + } + } + }; + + Ok(()) + } +} diff --git a/crates/type_checker/src/styles.rs b/crates/type_checker/src/styles.rs new file mode 100644 index 00000000..8230cbdd --- /dev/null +++ b/crates/type_checker/src/styles.rs @@ -0,0 +1,66 @@ +//* File specific to implement styles resolving, hoisting, and statment resolval + +use slynx_hir::model::{HirDeclaration, HirDeclarationKind, HirStyleStatement, HirStyleUsage}; + +use crate::{Result, TypeChecker, error::TypeError}; + +impl TypeChecker { + pub fn check_style_statement(&mut self, statement: &mut HirStyleStatement) -> Result<()> { + let void = self.types_module.void_id(); + match statement { + HirStyleStatement::Statement(statement) => self.resolve_statement(statement, &void), + HirStyleStatement::Styles(blocks) => { + for block in blocks { + for def in &mut block.definitions { + def.expr.ty = self.unify(&def.expected_type, &def.expr.ty, &def.span)?; + } + } + Ok(()) + } + } + } + pub fn check_style_usage(&mut self, usage: &mut HirStyleUsage) -> Result<()> { + let decl_ty = self.declarations[usage.style.as_raw() as usize]; + let params_types = usage + .params + .iter_mut() + .map(|param| self.get_type_of_expr(param)) + .collect::>>()?; + let slynx_hir::model::HirType::Style { args } = self.types_module.get_type(&decl_ty) else { + unreachable!("Type of style should be Style"); + }; + let args = args.clone(); + if usage.params.len() != args.len() { + return Err(TypeError::invalid_funcall_args( + args.len(), + usage.params.len(), + usage.span, + )); + } + for (idx, paramty) in params_types.iter().enumerate() { + let arg = &args[idx]; + let span = usage.params[idx].span; + usage.params[idx].ty = self.unify(paramty, arg, &span)?; + } + + Ok(()) + } + + pub fn check_stylesheet(&mut self, style: &mut HirDeclaration) -> Result<()> { + let HirDeclarationKind::StyleSheet { + ref mut statements, + ref mut usages, + .. + } = style.kind + else { + unimplemented!("check stylesheet should receive a stylesheet"); + }; + for usage in usages { + self.check_style_usage(usage)?; + } + for statement in statements { + self.check_style_statement(statement)?; + } + Ok(()) + } +} From 12e0d6a6799fbbb6f78a1ca15bbb7930bb5012ba Mon Sep 17 00:00:00 2001 From: caina Date: Tue, 19 May 2026 16:20:24 -0300 Subject: [PATCH 02/61] refactor: renamed slynx_ir to be now simply 'ir' refactor: renorganized code folders --- Cargo.toml | 4 +- crates/checker/Cargo.toml | 9 - crates/checker/src/decl.rs | 163 ----- crates/checker/src/defaults.rs | 54 -- crates/checker/src/error.rs | 138 ----- crates/checker/src/expr.rs | 578 ------------------ crates/checker/src/lib.rs | 423 ------------- crates/checker/src/statement.rs | 59 -- crates/checker/src/styles.rs | 103 ---- crates/{slynx_ir => ir}/Cargo.lock | 0 crates/{slynx_ir => ir}/Cargo.toml | 0 crates/{slynx_ir => ir}/README.md | 0 crates/{slynx_ir => ir}/STYLES_TABLE.md | 0 .../docs/component-init-lowering.md | 0 .../docs/linerize-the-graph.md | 0 .../docs/reactive-graph-generation.md | 0 .../docs/stylesheet-lowering.md | 0 crates/{slynx_ir/src/ir => ir/src}/api.rs | 2 +- crates/{slynx_ir => ir}/src/cfg/mod.rs | 0 .../{slynx_ir/src/ir => ir/src}/components.rs | 3 +- .../{slynx_ir/src/ir => ir/src}/contexts.rs | 6 +- crates/{slynx_ir => ir}/src/error.rs | 0 .../src/ir => ir/src}/helper/contexts.rs | 5 +- .../src/ir => ir/src}/helper/ir_value.rs | 2 +- .../src/ir => ir/src}/helper/label.rs | 5 +- .../{slynx_ir/src/ir => ir/src}/helper/mod.rs | 0 .../src/ir => ir/src}/helper/styles.rs | 24 +- .../src/ir => ir/src}/helper/types.rs | 7 +- .../src/ir => ir/src}/instructions.rs | 4 +- .../{slynx_ir/src/ir/mod.rs => ir/src/ir.rs} | 41 +- crates/ir/src/lib.rs | 21 + .../src/ir => ir/src}/model/components.rs | 0 .../src/ir => ir/src}/model/context.rs | 0 .../src/ir => ir/src}/model/instruction.rs | 2 +- .../src/ir => ir/src}/model/label.rs | 0 .../{slynx_ir/src/ir => ir/src}/model/mod.rs | 0 .../{slynx_ir/src/ir => ir/src}/model/ptr.rs | 0 .../src/ir => ir/src}/model/styles.rs | 28 + .../src/ir => ir/src}/model/value.rs | 0 .../ir/temp.rs => ir/src/temporary_data.rs} | 5 +- .../{slynx_ir => ir}/src/types/components.rs | 0 .../{slynx_ir => ir}/src/types/functions.rs | 0 crates/{slynx_ir => ir}/src/types/irtype.rs | 0 crates/{slynx_ir => ir}/src/types/mod.rs | 0 crates/{slynx_ir => ir}/src/types/structs.rs | 0 crates/{slynx_ir => ir}/src/types/tuple.rs | 0 .../src/ir => ir/src}/visualize/formatter.rs | 0 .../src/ir => ir/src}/visualize/mod.rs | 0 crates/slynx_ir/src/lib.rs | 9 - 49 files changed, 82 insertions(+), 1613 deletions(-) delete mode 100644 crates/checker/Cargo.toml delete mode 100644 crates/checker/src/decl.rs delete mode 100644 crates/checker/src/defaults.rs delete mode 100644 crates/checker/src/error.rs delete mode 100644 crates/checker/src/expr.rs delete mode 100644 crates/checker/src/lib.rs delete mode 100644 crates/checker/src/statement.rs delete mode 100644 crates/checker/src/styles.rs rename crates/{slynx_ir => ir}/Cargo.lock (100%) rename crates/{slynx_ir => ir}/Cargo.toml (100%) rename crates/{slynx_ir => ir}/README.md (100%) rename crates/{slynx_ir => ir}/STYLES_TABLE.md (100%) rename crates/{slynx_ir => ir}/docs/component-init-lowering.md (100%) rename crates/{slynx_ir => ir}/docs/linerize-the-graph.md (100%) rename crates/{slynx_ir => ir}/docs/reactive-graph-generation.md (100%) rename crates/{slynx_ir => ir}/docs/stylesheet-lowering.md (100%) rename crates/{slynx_ir/src/ir => ir/src}/api.rs (98%) rename crates/{slynx_ir => ir}/src/cfg/mod.rs (100%) rename crates/{slynx_ir/src/ir => ir/src}/components.rs (99%) rename crates/{slynx_ir/src/ir => ir/src}/contexts.rs (98%) rename crates/{slynx_ir => ir}/src/error.rs (100%) rename crates/{slynx_ir/src/ir => ir/src}/helper/contexts.rs (97%) rename crates/{slynx_ir/src/ir => ir/src}/helper/ir_value.rs (97%) rename crates/{slynx_ir/src/ir => ir/src}/helper/label.rs (85%) rename crates/{slynx_ir/src/ir => ir/src}/helper/mod.rs (100%) rename crates/{slynx_ir/src/ir => ir/src}/helper/styles.rs (95%) rename crates/{slynx_ir/src/ir => ir/src}/helper/types.rs (97%) rename crates/{slynx_ir/src/ir => ir/src}/instructions.rs (99%) rename crates/{slynx_ir/src/ir/mod.rs => ir/src/ir.rs} (93%) create mode 100644 crates/ir/src/lib.rs rename crates/{slynx_ir/src/ir => ir/src}/model/components.rs (100%) rename crates/{slynx_ir/src/ir => ir/src}/model/context.rs (100%) rename crates/{slynx_ir/src/ir => ir/src}/model/instruction.rs (99%) rename crates/{slynx_ir/src/ir => ir/src}/model/label.rs (100%) rename crates/{slynx_ir/src/ir => ir/src}/model/mod.rs (100%) rename crates/{slynx_ir/src/ir => ir/src}/model/ptr.rs (100%) rename crates/{slynx_ir/src/ir => ir/src}/model/styles.rs (62%) rename crates/{slynx_ir/src/ir => ir/src}/model/value.rs (100%) rename crates/{slynx_ir/src/ir/temp.rs => ir/src/temporary_data.rs} (98%) rename crates/{slynx_ir => ir}/src/types/components.rs (100%) rename crates/{slynx_ir => ir}/src/types/functions.rs (100%) rename crates/{slynx_ir => ir}/src/types/irtype.rs (100%) rename crates/{slynx_ir => ir}/src/types/mod.rs (100%) rename crates/{slynx_ir => ir}/src/types/structs.rs (100%) rename crates/{slynx_ir => ir}/src/types/tuple.rs (100%) rename crates/{slynx_ir/src/ir => ir/src}/visualize/formatter.rs (100%) rename crates/{slynx_ir/src/ir => ir/src}/visualize/mod.rs (100%) delete mode 100644 crates/slynx_ir/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index c8596466..f08591e8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,9 +17,9 @@ common.workspace=true slynx-lexer.path="./crates/lexer" slynx-parser.path="./crates/parser" slynx-hir.path="./crates/hir" -slynx-typechecker.path="./crates/checker" +slynx-typechecker.path="./crates/type_checker" slynx-monomorphizer.path="./crates/monomorphizer" -slynx-ir.path="./crates/slynx_ir" +slynx-ir.path="./crates/ir" [workspace] members = ["crates/*"] diff --git a/crates/checker/Cargo.toml b/crates/checker/Cargo.toml deleted file mode 100644 index 054c6be8..00000000 --- a/crates/checker/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "slynx-typechecker" -version = "0.1.0" -edition = "2024" - -[dependencies] -color-eyre.workspace=true -common = {path="../common"} -slynx-hir.path="../hir" diff --git a/crates/checker/src/decl.rs b/crates/checker/src/decl.rs deleted file mode 100644 index e88b22c7..00000000 --- a/crates/checker/src/decl.rs +++ /dev/null @@ -1,163 +0,0 @@ -/*! -Type checking for top-level declarations. - -This module contains the logic to type-check HIR declarations: -- Functions: type-check all statements in the function body. -- Objects/Aliases: no-op at this stage (handled elsewhere). -- Components: check property initializers and unify them with the declared - component prop types. We read the component type, perform unifications - locally and then write the updated component type back into the - `TypesModule` to avoid borrow conflicts. - -The implementation intentionally clones the component type's `props` before -resolving expressions so we don't hold a long-lived borrow on the -`TypesModule` while performing potentially mutating operations that also -access the `TypesModule`. -*/ - -use super::{Result, TypeChecker}; - -use slynx_hir::{ - TypeId, - model::{ - ComponentMemberDeclaration, HirComponentExpression, HirDeclaration, HirDeclarationKind, - HirType, - }, -}; - -impl TypeChecker { - /// Type-check a single top-level declaration. - /// - /// For functions this resolves statements; for components it resolves - /// property initializers and updates the component type accordingly. - pub(super) fn check_decl(&mut self, decl: &mut HirDeclaration) -> Result<()> { - match &mut decl.kind { - HirDeclarationKind::StyleSheet { .. } => self.check_stylesheet(decl)?, - // Objects and aliases have no bodies to type-check at this pass. - HirDeclarationKind::Object | HirDeclarationKind::Alias => {} - - HirDeclarationKind::Function { statements, .. } => { - let HirType::Function { return_type, .. } = self.types_module.get_type(&decl.ty) - else { - unreachable!("Type of function should be function"); - }; - let return_type = *return_type; - self.resolve_statements(statements, &return_type)?; - } - HirDeclarationKind::ComponentDeclaration { props, .. } => { - let ty = self.types_module.get_type(&decl.ty).clone(); - let HirType::Component { - props: mut declared_props, - } = ty - else { - unreachable!("Component declaration should have component HirType"); - }; - - for member in props.iter_mut() { - match member { - ComponentMemberDeclaration::Property { - index, - value: maybe_expr, - span, - .. - } => { - if let Some(expr) = maybe_expr { - let expr_ty = self.get_type_of_expr(expr)?; - - *declared_props[*index].prop_type_mut() = - self.unify(declared_props[*index].prop_type(), &expr_ty, span)?; - } - } - - ComponentMemberDeclaration::Child { .. } => {} - } - } - - *self.types_module.get_type_mut(&decl.ty) = HirType::Component { - props: declared_props, - }; - } - } - - Ok(()) - } - - pub(super) fn resolve_component_expression( - &mut self, - expr: &mut HirComponentExpression, - ) -> Result<()> { - match expr { - HirComponentExpression::Specialized(spec) => self.resolve_specialized(spec), - HirComponentExpression::Normal { - name, - properties, - children, - span, - } => { - let HirType::Component { props } = self.types_module.get_type(name) else { - unreachable!("Should've received a component type"); - }; - let props = props.clone(); - - for prop_expr in properties { - let prop_ty = *props[prop_expr.index()].prop_type(); - prop_expr.expr_mut().ty = self.unify(&prop_ty, &prop_expr.expr().ty, span)?; - } - for child in children { - self.resolve_component_expression(child)?; - } - Ok(()) - } - } - } - - /// Resolve and unify a list of component members against a target component type. - /// - /// This function is used when checking component instances: it walks the provided - /// members (properties, children and specializations), resolves values and - /// unifies them against the `target` component's declared prop types. - /// - /// Returns the `target` TypeId on success. - pub(super) fn resolve_component_members( - &mut self, - values: &mut [ComponentMemberDeclaration], - target: TypeId, - ) -> Result { - let target_ty = self.types_module.get_type(&target).clone(); - let HirType::Component { - props: mut declared_props, - } = target_ty - else { - unreachable!("Expected component type when resolving component members"); - }; - - for value in values.iter_mut() { - match value { - ComponentMemberDeclaration::Property { - index, - value: maybe_expr, - span, - .. - } => { - if let Some(expr) = maybe_expr { - let expr_ty = self.get_type_of_expr(expr)?; - let unified = - self.unify(declared_props[*index].prop_type(), &expr_ty, span)?; - - *declared_props[*index].prop_type_mut() = unified; - } - } - - ComponentMemberDeclaration::Child(child) => { - self.resolve_component_expression(child)? - } - } - } - - *self.types_module.get_type_mut(&target) = HirType::Component { - props: declared_props, - }; - - Ok(target) - } -} diff --git a/crates/checker/src/defaults.rs b/crates/checker/src/defaults.rs deleted file mode 100644 index 1aca97f1..00000000 --- a/crates/checker/src/defaults.rs +++ /dev/null @@ -1,54 +0,0 @@ -//* File specific for defining default types on declarations -use slynx_hir::model::{HirDeclaration, HirDeclarationKind, HirType}; - -use crate::{Result, TypeChecker}; - -impl TypeChecker { - pub fn default_stylesheet(&mut self, decl: &mut HirDeclaration) -> Result<()> { - let HirDeclarationKind::StyleSheet { - ref mut statements, .. - } = decl.kind - else { - unreachable!("default_stylesheet should receive a stylesheet declaration"); - }; - - for statement in &mut *statements { - self.default_style_statement(statement)?; - } - Ok(()) - } - - ///Asserts that the given `decl` is a function and sets default values to all of its statements, and ensure its returns are correctly. - ///If the given `decl` is not a function, a panic is going to be received, since it's obviously a bug - pub fn default_function(&mut self, decl: &mut HirDeclaration) -> Result<()> { - let HirDeclarationKind::Function { - ref mut statements, .. - } = decl.kind - else { - unreachable!("default_function should receive a function declaration"); - }; - let HirType::Function { return_type, .. } = self.types_module.get_type(&decl.ty).clone() - else { - unreachable!("A function should have function type"); - }; - let infer = self.types_module.infer_id(); - for statement in &mut *statements { - self.default_statement(statement, &infer)?; - } - self.ensure_function_returns(statements, return_type, &decl.span)?; - Ok(()) - } - - ///Sets the default types on the given `decl`. This replaces the infer types on everything on the given `decl` with the correct(or default) type - pub fn set_default(&mut self, decl: &mut HirDeclaration) -> Result<()> { - match decl.kind { - HirDeclarationKind::Object | HirDeclarationKind::Alias => {} - HirDeclarationKind::Function { .. } => self.default_function(decl)?, - HirDeclarationKind::ComponentDeclaration { ref mut props, .. } => { - self.resolve_component_members(props, decl.ty)?; - } - HirDeclarationKind::StyleSheet { .. } => self.default_stylesheet(decl)?, - } - Ok(()) - } -} diff --git a/crates/checker/src/error.rs b/crates/checker/src/error.rs deleted file mode 100644 index 16e65978..00000000 --- a/crates/checker/src/error.rs +++ /dev/null @@ -1,138 +0,0 @@ -//! Type checking errors for the Slynx type checker. -//! -//! This module defines the error types used during type checking, including -//! type mismatch errors, cyclic type errors, and component-related errors. - -use common::Span; -use slynx_hir::{DeclarationId, VariableId, model::HirType}; - -/// Represents the reason for an incompatible component error. -/// -/// This enum is used to specify the specific reason why two components are -/// incompatible, such as having a different number of properties. -#[derive(Debug)] -pub enum IncompatibleComponentReason { - DifferentPropAmount { rhs: usize, lhs: usize }, -} - -/// Represents a type error that occurred during type checking. -/// -/// This struct holds the type error kind and the span where the error occurred. -#[derive(Debug)] -pub struct TypeError { - pub kind: TypeErrorKind, - pub span: Span, -} - -/// Represents the kind of type error that occurred. -/// -/// This enum is used to specify the specific type error that occurred, such as -/// a type mismatch or a cyclic type reference. -#[derive(Debug)] -pub enum TypeErrorKind { - CannotCastType { - expected: HirType, - received: HirType, - }, - CiclicType { - ty: HirType, - }, - IncompatibleComponent { - reason: IncompatibleComponentReason, - }, - IncompatibleTypes { - expected: HirType, - received: HirType, - }, - InvalidTupleAccessTarget { - received: HirType, - }, - InvalidTupleIndex { - index: usize, - length: usize, - }, - InvalidFuncallArgLength { - expected_length: usize, - received_length: usize, - }, - InvalidFunctionCallTarget { - declaration: DeclarationId, - received: HirType, - }, - MissingReturnValue { - expected: HirType, - }, - NotARef(VariableId, HirType), - NotAStruct(HirType), - Unrecognized, -} - -impl TypeError { - pub fn invalid_funcall_args(expected: usize, received: usize, span: Span) -> Self { - Self { - span, - kind: TypeErrorKind::InvalidFuncallArgLength { - expected_length: expected, - received_length: received, - }, - } - } -} - -impl std::fmt::Display for TypeError { - /// Formats the type error as a string. - /// - /// This implementation uses the error kind to generate a human-readable error - /// message, including the expected and received types for type mismatch errors. - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let out = match &self.kind { - TypeErrorKind::CannotCastType { expected, received } => { - format!("Could not cast type '{expected:?}' into '{received:?}'") - } - TypeErrorKind::CiclicType { ty } => { - format!("The type '{ty:?}' is cyclic and cannot exist without recursion") - } - TypeErrorKind::IncompatibleComponent { reason } => { - format!("The component is incompatible because of '{reason:?}'") - } - TypeErrorKind::IncompatibleTypes { expected, received } => format!( - "Incompatible types. Was expecting to receive type '{expected:?}' instead got type '{received:?}'" - ), - TypeErrorKind::InvalidTupleAccessTarget { received } => { - format!("Type '{received:?}' does not support tuple-style access") - } - TypeErrorKind::InvalidTupleIndex { index, length } => { - format!( - "Tuple index {index} is out of bounds. The tuple only exposes {length} fields" - ) - } - TypeErrorKind::InvalidFuncallArgLength { - expected_length, - received_length, - } => format!( - "Invalid function call arg length. Expected {expected_length} args but received {received_length}" - ), - TypeErrorKind::InvalidFunctionCallTarget { - declaration, - received, - } => format!( - "Invalid function call target at declaration {declaration:?}. Expected function type but received {received:?}" - ), - TypeErrorKind::MissingReturnValue { expected } => { - format!("Function is missing a return value of type '{expected:?}'") - } - TypeErrorKind::NotARef(v, ty) => format!( - "Variable with id {v:?} has got type {ty:?} instead was expecting to be an object" - ), - TypeErrorKind::NotAStruct(t) => { - format!("Using type {t:?} as a Struct, even though it isn't") - } - TypeErrorKind::Unrecognized => { - "Type checker could not resolve the requested symbol/type".to_string() - } - }; - write!(f, "{out}") - } -} - -impl std::error::Error for TypeError {} diff --git a/crates/checker/src/expr.rs b/crates/checker/src/expr.rs deleted file mode 100644 index daeb5bf2..00000000 --- a/crates/checker/src/expr.rs +++ /dev/null @@ -1,578 +0,0 @@ -//! Type checking logic for expressions and statements. -//! -//! This module implements the core type-checking pass for the Slynx HIR. -//! It handles the resolution of function bodies, component property -//! initialization, and ensures type safety through unification. - -use super::{Result, TypeChecker}; - -use crate::error::{TypeError, TypeErrorKind}; -use common::Span; -use slynx_hir::{ - DeclarationId, TypeId, - model::{ - FieldMethod, HirComponentExpression, HirExpression, HirExpressionKind, - HirSpecializedComponentExpression, HirStatement, HirStatementKind, HirStyleUsage, HirType, - }, -}; -impl TypeChecker { - /// Resolves the struct type from a reference type. - /// - /// This function recursively follows reference types until it finds a struct type. - /// If the type is not a struct, a `TypeError` is returned. - pub fn get_struct_from_ref(&self, ty: &TypeId, span: &Span) -> Result { - match self.types_module.get_type(ty) { - HirType::Reference { rf, .. } => self.get_struct_from_ref(rf, span), - HirType::Struct { .. } => Ok(*ty), - v => Err(TypeError { - kind: TypeErrorKind::NotAStruct(v.clone()), - span: *span, - }), - } - } - - /// Resolves the type of a field access expression. - /// - /// This function resolves the type of a field access expression by following - /// the field method (type or variable) and updating the field index. - fn resolve_field_access_type( - &mut self, - field_ty: &mut TypeId, - field_index: &mut usize, - span: &Span, - ) -> Result { - let HirType::Field(field_method) = self.types_module.get_type(field_ty).clone() else { - return self.resolve(field_ty, span); - }; - match field_method { - FieldMethod::Type(rf, index) => { - *field_index = index; - *field_ty = self - .types_module - .insert_unnamed_type(HirType::Field(FieldMethod::Type(rf, index))); - self.resolve(field_ty, span) - } - FieldMethod::Tuple(rf, index) => { - *field_index = index; - *field_ty = self - .types_module - .insert_unnamed_type(HirType::Field(FieldMethod::Tuple(rf, index))); - self.resolve(field_ty, span) - } - FieldMethod::Variable(variable_id, field_name) => { - // Field accesses first enter the checker attached to the source - // variable name. Resolve that symbolic access once and rewrite it - // into an indexed field lookup for the rest of the pipeline. - let object_ty = *self - .types_module - .get_variable(&variable_id) - .ok_or(TypeError { - kind: TypeErrorKind::Unrecognized, - span: *span, - })?; - let layout_ty = self.get_object_layout_type(&object_ty, span)?; - - let Some(index) = self - .structs - .get(&layout_ty) - .expect("Type should be defined") - .iter() - .position(|field| *field == field_name) - else { - return Err(TypeError { - kind: TypeErrorKind::Unrecognized, - span: *span, - }); - }; - - *field_index = index; - *self.types_module.get_type_mut(field_ty) = - HirType::Field(FieldMethod::Type(object_ty, index)); - self.resolve(field_ty, span) - } - } - } - - /// Gets the signature of the provided `declaration` id expecting its a function type. - /// - /// This function retrieves the function type from the declarations and returns - /// the argument types and return type. If the declaration is not a function, - /// a `TypeError` is returned. - fn get_function_signature( - &self, - declaration: DeclarationId, - span: &Span, - ) -> Result<(Vec, TypeId)> { - let Some(function_ty) = self - .declarations - .get(declaration.as_raw() as usize) - .copied() - else { - return Err(TypeError { - kind: TypeErrorKind::Unrecognized, - span: *span, - }); - }; - - let resolved = self.types_module.get_type(&function_ty).clone(); - let HirType::Function { args, return_type } = resolved else { - return Err(TypeError { - kind: TypeErrorKind::InvalidFunctionCallTarget { - declaration, - received: resolved, - }, - span: *span, - }); - }; - Ok((args, return_type)) - } - ///Asserts that `args` has same length as `expected_args` - fn check_function_call_len( - &self, - args: &[HirExpression], - expected_args: &[TypeId], - span: &Span, - ) -> Result<()> { - if args.len() != expected_args.len() { - return Err(TypeError { - kind: TypeErrorKind::InvalidFuncallArgLength { - expected_length: expected_args.len(), - received_length: args.len(), - }, - span: *span, - }); - } - Ok(()) - } - - /// Checks that `args` have the same types as `expected`. - /// - /// This function checks that each argument in `args` has a type that can be unified with - /// the corresponding type in `expected`. If a type mismatch occurs, a `TypeError` is returned. - fn check_function_call_args( - &mut self, - args: &mut [HirExpression], - expected: &[TypeId], - ) -> Result<()> { - debug_assert_eq!( - args.len(), - expected.len(), - "arity must be checked before arg validation" - ); - for (arg, expected_ty) in args.iter_mut().zip(expected.iter().copied()) { - arg.ty = self.get_type_of_expr(arg)?; - arg.ty = self.unify(&arg.ty, &expected_ty, &arg.span)?; - } - Ok(()) - } - - /// Defaults the provided `args` to their expected types. - /// - /// This function defaults each argument in `args` to its expected type. If a type mismatch - /// occurs, a `TypeError` is returned. - fn default_function_call_args( - &mut self, - args: &mut [HirExpression], - expected: &[TypeId], - ) -> Result<()> { - debug_assert_eq!( - args.len(), - expected.len(), - "arity must be checked before arg defaulting" - ); - - for (arg, expected_ty) in args.iter_mut().zip(expected.iter().copied()) { - self.default_expr(arg)?; - arg.ty = self.unify(&arg.ty, &expected_ty, &arg.span)?; - } - - Ok(()) - } - - pub fn resolve_style_usage(&mut self, style_usage: &mut HirStyleUsage) -> Result<()> { - let HirType::Style { args } = self - .types_module - .get_type(&self.declarations[style_usage.style.as_raw() as usize]) - else { - unreachable!("Type of style should be style"); - }; - let args = args.clone(); - for (idx, param) in style_usage.params.iter_mut().enumerate() { - param.ty = self.unify(&args[idx], ¶m.ty, ¶m.span)?; - } - - Ok(()) - } - - /// Resolves the type of a specialized component expression. - /// - /// This function resolves the type of a specialized component expression by - /// updating the type of the expression and its children. If a type mismatch - /// occurs, a `TypeError` is returned. - pub(super) fn resolve_specialized( - &mut self, - spec: &mut HirSpecializedComponentExpression, - ) -> Result<()> { - match spec { - HirSpecializedComponentExpression::Text { text, style } => { - text.ty = self.get_type_of_expr(text)?; - text.ty = self.unify(&text.ty, &self.types_module.str_id(), &text.span)?; - if let Some(style) = style { - self.resolve_style_usage(style)?; - } - } - HirSpecializedComponentExpression::Div { children, style } => { - for child in children { - self.resolve_component_expression(child)?; - } - if let Some(style) = style { - self.resolve_style_usage(style)?; - } - } - } - - Ok(()) - } - /// Resolves a while statement, checking that the condition is a boolean and the body is a sequence of statements. - fn resolve_statement_while( - &mut self, - condition: &mut HirExpression, - body: &mut [HirStatement], - ty: &TypeId, - ) -> Result<()> { - condition.ty = self.get_type_of_expr(condition)?; - let bool_id = self.types_module.bool_id(); - - condition.ty = self.unify(&condition.ty, &bool_id, &condition.span)?; - - self.resolve_statements(body, ty)?; - Ok(()) - } - /// Resolves an assignment statement, checking that the types match. - fn resolve_statement_assign( - &mut self, - lhs: &mut HirExpression, - value: &mut HirExpression, - span: &Span, - ) -> Result<()> { - let refty = match self.types_module.get_type(&lhs.ty) { - HirType::Field(FieldMethod::Type(_, _)) | HirType::Field(FieldMethod::Tuple(_, _)) => { - lhs.ty - } - HirType::Field(FieldMethod::Variable(..)) => { - let HirExpressionKind::FieldAccess { - ref mut field_index, - .. - } = lhs.kind - else { - unreachable!(); - }; - let _ = self.resolve_field_access_type(&mut lhs.ty, field_index, &lhs.span)?; - lhs.ty - } - HirType::VarReference(_) => lhs.ty, - _ => unreachable!(), - }; - - let ty = self.resolve(&lhs.ty, span)?; - lhs.ty = refty; - value.ty = self.get_type_of_expr(value)?; - value.ty = self.unify(&ty, &value.ty, &value.span)?; - Ok(()) - } - - pub(super) fn resolve_statement( - &mut self, - statement: &mut HirStatement, - return_type: &TypeId, - ) -> Result<()> { - let span = &statement.span; - match &mut statement.kind { - HirStatementKind::While { condition, body } => { - self.resolve_statement_while(condition, body, return_type)? - } - - HirStatementKind::Variable { value, .. } => { - value.ty = self.get_type_of_expr(value)?; - } - - HirStatementKind::Return { expr } => { - expr.ty = self.get_type_of_expr(expr)?; - expr.ty = self.unify(&expr.ty, return_type, span)?; - } - - HirStatementKind::Expression { expr } => { - expr.ty = self.get_type_of_expr(expr)?; - } - - HirStatementKind::Assign { lhs, value } => { - self.resolve_statement_assign(lhs, value, span)? - } - } - Ok(()) - } - - /// Resolves the types of the provided `statements`. - /// - /// This function resolves the types of the provided `statements` by updating - /// the type of each statement and its children. If a type mismatch occurs, - /// a `TypeError` is returned. - pub(super) fn resolve_statements( - &mut self, - statements: &mut [HirStatement], - return_type: &TypeId, - ) -> Result<()> { - for statement in statements { - self.resolve_statement(statement, return_type)?; - } - - Ok(()) - } - - /// Resolves the types of the provided `fields` asserting that `ty` is a type for a struct, - /// and the types of `fields` match the type expected by the fields of the given struct type. - pub(super) fn resolve_object_types( - &mut self, - ty: &HirType, - fields: &mut [HirExpression], - ) -> Result<()> { - let HirType::Struct { fields: fields_tys } = ty else { - unreachable!("When resolving object types, a type 'struct' should be provided"); - }; - - for (idx, f) in fields.iter_mut().enumerate() { - f.ty = self.unify(&fields_tys[idx], &f.ty, &f.span)?; - } - - Ok(()) - } - - /// Resolves the type of a reference expression, returning the type it references. - pub fn get_type_from_ref(&self, ref_ty: TypeId) -> &HirType { - if let HirType::Reference { rf, .. } = self.types_module.get_type(&ref_ty) { - self.types_module.get_type(rf) - } else { - unreachable!("The provided ref_ty should be of type Reference"); - } - } - /// Helper to resolve a branch (then/else) that may be present or absent. - /// Accepts `Option<&mut Vec>` because the else branch is optional - /// in the HIR representation. `expected` is the expected TypeId for statements. - fn resolve_branch( - &mut self, - branch: Option<&mut Vec>, - expected: &TypeId, - ) -> Result { - let Some(stmts) = branch else { - return Ok(self.types_module.void_id()); - }; - let Some((last, rest)) = stmts.split_last_mut() else { - return Ok(self.types_module.void_id()); - }; - for stmt in rest { - self.default_statement(stmt, expected)?; - } - match &mut last.kind { - HirStatementKind::Expression { expr } => self.get_type_of_expr(expr), - _ => Ok(self.types_module.void_id()), - } - } - - /// Retrieves the type of the provided `expr`. Returns infer if it could not be inferred. - pub(super) fn get_type_of_expr(&mut self, expr: &mut HirExpression) -> Result { - let expected = expr.ty; - - let calc = match expr.kind { - HirExpressionKind::Tuple(ref mut fields) => { - let field_types = fields - .iter_mut() - .map(|f| self.get_type_of_expr(f)) - .collect::>>()?; - self.types_module.add_tuple_type(field_types) - } - HirExpressionKind::If { - ref mut condition, - ref mut else_branch, - ref mut then_branch, - } => { - let if_ty = self.resolve_branch(Some(then_branch), &expected)?; - let else_ty = self.resolve_branch(else_branch.as_mut(), &expected)?; - - let result_ty = self.unify(&if_ty, &else_ty, &expr.span)?; - let cond_ty = self.get_type_of_expr(condition)?; - condition.ty = - self.unify(&cond_ty, &self.types_module.bool_id(), &condition.span)?; - - result_ty - } - HirExpressionKind::FunctionCall { - name, - args: ref mut f_args, - } => { - let (args, return_type) = self.get_function_signature(name, &expr.span)?; - - self.check_function_call_len(f_args, &args, &expr.span)?; - self.check_function_call_args(f_args, &args)?; - - return_type - } - HirExpressionKind::Int(_) => self.types_module.int_id(), - HirExpressionKind::Float(_) => self.types_module.float_id(), - HirExpressionKind::StringLiteral(_) => self.types_module.str_id(), - HirExpressionKind::Binary { - ref mut lhs, - ref mut rhs, - ref op, - } => { - let lhs_ty = self.get_type_of_expr(lhs)?; - let rhs_ty = self.get_type_of_expr(rhs)?; - - let out = self.unify(&lhs_ty, &rhs_ty, &expr.span)?; - - if op.is_logical() { - self.types_module.bool_id() - } else { - out - } - } - HirExpressionKind::Identifier(_) => self.resolve(&expr.ty, &expr.span)?, - HirExpressionKind::Component(HirComponentExpression::Specialized(_)) => { - self.types_module.generic_component_id() - } - HirExpressionKind::Component(HirComponentExpression::Normal { name, .. }) => name, - HirExpressionKind::Object { - name, - ref mut fields, - } => { - let obj = self.get_type_from_ref(name).clone(); - self.resolve_object_types(&obj, fields)?; - name - } - - HirExpressionKind::FieldAccess { - ref mut field_index, - expr: ref mut e, - } => { - self.get_type_of_expr(e)?; - self.resolve_field_access_type(&mut expr.ty, field_index, &expr.span)? - } - HirExpressionKind::Bool(_) => self.types_module.bool_id(), - }; - - expr.ty = self.unify(&expected, &calc, &expr.span)?; - Ok(expr.ty) - } - - /// Sets the default type on the provided `expr`. - /// - /// This function sets the default type on the provided `expr` by recursively - /// defaulting each field in the tuple and collecting their types. The resulting - pub(super) fn default_expr(&mut self, expr: &mut HirExpression) -> Result<()> { - match expr.kind { - HirExpressionKind::Tuple(ref mut fields) => { - let types = fields - .iter_mut() - .map(|f| { - self.default_expr(f)?; - Ok(f.ty) - }) - .collect::>>()?; - - let tuple_ty = self.types_module.add_tuple_type(types); - - expr.ty = self.unify(&tuple_ty, &expr.ty, &expr.span)?; - } - HirExpressionKind::If { - ref mut condition, - ref mut then_branch, - ref mut else_branch, - } => { - self.default_expr(condition)?; - condition.ty = - self.unify(&condition.ty, &self.types_module.bool_id(), &condition.span)?; - let if_ty = self.resolve_branch(Some(then_branch), &expr.ty)?; - let else_ty = self.resolve_branch(else_branch.as_mut(), &expr.ty)?; - self.unify(&if_ty, &else_ty, &expr.span)?; - } - HirExpressionKind::FunctionCall { - name, - args: ref mut f_args, - } => { - let (args, return_type) = self.get_function_signature(name, &expr.span)?; - self.check_function_call_len(f_args, &args, &expr.span)?; - self.default_function_call_args(f_args, &args)?; - expr.ty = self.unify(&expr.ty, &return_type, &expr.span)?; - } - HirExpressionKind::Bool(_) => { - expr.ty = self.unify(&expr.ty, &self.types_module.bool_id(), &expr.span)? - } - HirExpressionKind::StringLiteral(_) => { - expr.ty = self.unify(&expr.ty, &self.types_module.str_id(), &expr.span)? - } - HirExpressionKind::Int(_) => { - expr.ty = self.unify(&expr.ty, &self.types_module.int_id(), &expr.span)? - } - HirExpressionKind::Float(_) => { - expr.ty = self.unify(&expr.ty, &self.types_module.float_id(), &expr.span)? - } - HirExpressionKind::Binary { - ref mut lhs, - ref mut rhs, - op, - } => { - self.default_expr(rhs)?; - self.default_expr(lhs)?; - if op.is_logical() { - expr.ty = self.types_module.bool_id(); - } else { - expr.ty = self.unify(&rhs.ty, &lhs.ty, &expr.span)?; - } - } - HirExpressionKind::Identifier(_) => { - expr.ty = self.resolve(&expr.ty, &expr.span)?; - } - - HirExpressionKind::Object { .. } => { - expr.ty = self.resolve(&expr.ty, &expr.span)?; - } - HirExpressionKind::FieldAccess { - expr: ref mut parent, - .. - } => { - parent.ty = self.resolve(&parent.ty, &expr.span)?; - expr.ty = self.resolve(&expr.ty, &expr.span)?; - } - HirExpressionKind::Component(HirComponentExpression::Normal { - name, - ref mut properties, - span, - .. - }) => { - let HirType::Component { props } = self.types_module.get_type(&name) else { - unreachable!("Type of component should be Component"); - }; - let props = props.clone(); - for prop in properties { - prop.expr_mut().ty = - self.unify(props[prop.index()].prop_type(), &prop.expr().ty, &span)?; - } - //todo: default component expression - - expr.ty = name; - } - HirExpressionKind::Component(HirComponentExpression::Specialized( - HirSpecializedComponentExpression::Text { ref mut text, .. }, - )) => { - let str_id = self.types_module.str_id(); - text.ty = self.unify(&text.ty, &str_id, &expr.span)?; - expr.ty = self.types_module.generic_component_id(); //change to SpecializedComponent later - } - HirExpressionKind::Component(HirComponentExpression::Specialized( - HirSpecializedComponentExpression::Div { children: _, .. }, - )) => { - //todo: default component expression - } - } - Ok(()) - } -} diff --git a/crates/checker/src/lib.rs b/crates/checker/src/lib.rs deleted file mode 100644 index 4b99291e..00000000 --- a/crates/checker/src/lib.rs +++ /dev/null @@ -1,423 +0,0 @@ -//! The core type-checking engine for the Slynx compiler. -//! -//! This module implements a two-phase type inference and validation system: -//! -//! 1. **Resolution Phase**: Iterates through the HIR to resolve `Infer` types -//! into concrete types by analyzing expressions, function calls, and -//! component properties. Functions involved in this phase are typically -//! named `resolve_*`. -//! -//! 2. **Default/Fallback Phase**: After the initial pass, any remaining -//! ambiguous types that couldn't be strictly inferred are assigned their -//! language-default types. Functions for this phase are named `set_default` -//! or `default_*`. -//! -//! The `TypeChecker` mutates the `TypesModule` and the `SlynxHir` in-place, -//! ensuring that subsequent compilation stages (like Codegen) have access to -//! fully concrete type information. - -mod decl; -mod defaults; -pub mod error; -mod expr; -mod statement; -mod styles; -use std::collections::HashMap; - -use common::SymbolPointer; - -use crate::error::{IncompatibleComponentReason, TypeError, TypeErrorKind}; - -use common::Span; -use slynx_hir::model::{ComponentProperty, HirStatement, HirStatementKind}; -use slynx_hir::modules::TypesModule; -use slynx_hir::{ - SlynxHir, TypeId, - model::{FieldMethod, HirType}, -}; - -pub type Result = std::result::Result; - -#[derive(Debug)] -///The type checker of slynx. Slynx has 2 phases of type checking by now. The first phase is to get information about the types on all the HIR -///and try to type them properly. Thus, the HIR might come with infers, and the type checker tries to get rid of them with their actual type. This is made with all function -///whose got `resolve` on their names. Since the idea is for them to try to resolve the types. The second part is that, after checking all the IR, if some code could not be infered, -///then it starts setting the default values for every of them. This is made with functions named as `default`. So every infers are get rid because the default type is provided. -pub struct TypeChecker { - ///A an array of declaration types - declarations: Vec, - ///The types module from the HIR to be mutated - types_module: TypesModule, - /// The type of everything that is expected to have some - types: HashMap, - structs: HashMap>, -} - -impl TypeChecker { - fn get_object_layout_type(&self, ty: &TypeId, span: &Span) -> Result { - let mut current = *ty; - - loop { - match self.types_module.get_type(¤t) { - HirType::Reference { rf, .. } => match self.types_module.get_type(rf) { - HirType::Struct { .. } => return Ok(current), - HirType::Reference { .. } => current = *rf, - other => { - return Err(TypeError { - kind: TypeErrorKind::NotAStruct(other.clone()), - span: *span, - }); - } - }, - HirType::VarReference(variable_id) => { - current = - self.types_module - .get_variable(variable_id) - .copied() - .ok_or(TypeError { - kind: TypeErrorKind::Unrecognized, - span: *span, - })?; - } - other => { - return Err(TypeError { - kind: TypeErrorKind::NotAStruct(other.clone()), - span: *span, - }); - } - } - } - } - - fn resolve_tuple_index_type( - &mut self, - ty: &TypeId, - index: usize, - span: &Span, - ) -> Result { - // Tuple access stays explicit all the way to the checker so numeric - // postfixes never get confused with object field lookups. - let resolved_ty = self.resolve(ty, span)?; - let HirType::Tuple { fields } = self.types_module.get_type(&resolved_ty).clone() else { - return Err(TypeError { - kind: TypeErrorKind::InvalidTupleAccessTarget { - received: self.types_module.get_type(&resolved_ty).clone(), - }, - span: *span, - }); - }; - - fields.get(index).copied().ok_or(TypeError { - kind: TypeErrorKind::InvalidTupleIndex { - index, - length: fields.len(), - }, - span: *span, - }) - } - - /// Checks the types of the provided `hir` and mutates them if needed. Any that could not be inferred but, yet is valid, is - /// at the end, returned as it's default type. Returns the type module to be used on the next steps - pub fn check(hir: &mut SlynxHir) -> Result { - let mut inner = Self { - types: HashMap::new(), - structs: std::mem::take(&mut hir.modules.declarations_module.objects), - types_module: std::mem::take(&mut hir.modules.types_module), - declarations: Vec::new(), - }; - // DeclarationId is currently assigned linearly in hoisting order, - // so declaration type lookup can stay append-only here. - for decl in &hir.declarations { - debug_assert_eq!( - decl.id.as_raw() as usize, - inner.declarations.len(), - "declaration id must stay linear with declarations table", - ); - inner.declarations.push(decl.ty); - } - for decl in &mut hir.declarations { - inner.check_decl(decl)?; - } - // for the ones that couldn't be inferred, put their default - for decl in &mut hir.declarations { - inner.set_default(decl)?; - } - - Ok(inner.types_module) - } - - fn substitute(&mut self, id: TypeId, ty: HirType) { - self.types.insert(id, ty); - } - - /// Resolves recursively the names of the types. If A -> B, B -> int; then we assume that A -> int - fn resolve(&mut self, ty: &TypeId, span: &Span) -> Result { - let referedty = self.types_module.get_type(ty).clone(); - match referedty { - HirType::Field(FieldMethod::Type(rf, index)) => { - let ty = self.resolve(&rf, span)?; - let struct_id = self.get_struct_from_ref(&ty, span)?; - if let HirType::Struct { fields } = self.types_module.get_type(&struct_id) { - Ok(fields[index]) - } else { - Err(TypeError { - kind: TypeErrorKind::IncompatibleTypes { - expected: self.types_module.get_type(&ty).clone(), - received: HirType::Struct { fields: Vec::new() }, - }, - span: *span, - }) - } - } - HirType::Field(FieldMethod::Tuple(rf, index)) => { - self.resolve_tuple_index_type(&rf, index, span) - } - HirType::Field(FieldMethod::Variable(var_id, n)) => { - let object_ty = *self.types_module.get_variable(&var_id).ok_or(TypeError { - kind: TypeErrorKind::Unrecognized, - span: *span, - })?; - let layout_ty = self.get_object_layout_type(&object_ty, span)?; - let concrete_type = self - .types_module - .get_type(&self.get_struct_from_ref(&object_ty, span)?); - let s_fields = self.structs.get(&layout_ty).ok_or(TypeError { - kind: TypeErrorKind::Unrecognized, - span: *span, - })?; - let HirType::Struct { fields } = concrete_type else { - unreachable!("Type should be a struct. Fields only happen to structs"); - }; - if let Some(index) = s_fields.iter().position(|name| *name == n) { - Ok(fields[index]) - } else { - Err(TypeError { - kind: TypeErrorKind::Unrecognized, - span: *span, - }) - } - } - HirType::Reference { rf, .. } => Ok(rf), - HirType::VarReference(rf) => { - if let Some(ty) = self.types_module.get_variable(&rf).cloned() { - Ok(self.resolve(&ty, span)?) - } else { - unreachable!("Variable type should be defined here"); - } - } - HirType::Component { props } => { - let mut resolved_props = { - let mut tys = Vec::with_capacity(props.len()); - for prop in props { - let mut prop = prop.clone(); - *prop.prop_type_mut() = self.resolve(prop.prop_type(), span)?; - tys.push(prop) - } - tys - }; - let HirType::Component { props } = self.types_module.get_type_mut(ty) else { - unreachable!(); - }; - props.clear(); - props.append(&mut resolved_props); - Ok(*ty) - } - - _ => Ok(*ty), - } - } - - /// Tries to unify types `a` and `b` if possible - fn unify(&mut self, a: &TypeId, b: &TypeId, span: &Span) -> Result { - let a = self.resolve(a, span)?; - let b = self.resolve(b, span)?; - - match (&a, &b) { - (a, b) if a == b => Ok(*a), - (out, inf) | (inf, out) - if *out != self.types_module.infer_id() && *inf == self.types_module.infer_id() => - { - Ok(*out) - } - (a, b) => { - let concrete_a = self.types_module.get_type(a).clone(); - let concrete_b = self.types_module.get_type(b).clone(); - - match (&concrete_a, &concrete_b) { - (_, HirType::Infer) => Ok(*a), - (HirType::Infer, _) => Ok(*b), - (HirType::Reference { rf, .. }, _) => self.unify_with_ref(*rf, *b, span), - (_, HirType::Reference { rf, .. }) => self.unify_with_ref(*rf, *b, span), - ( - HirType::Component { props: aprops }, - HirType::Component { props: bprops }, - ) => { - if aprops.len() != bprops.len() { - return Err(TypeError { - kind: TypeErrorKind::IncompatibleComponent { - reason: IncompatibleComponentReason::DifferentPropAmount { - rhs: aprops.len(), - lhs: bprops.len(), - }, - }, - span: *span, - }); - } - let mut unified_props = Vec::with_capacity(aprops.len()); - for (prop_a, prop_b) in aprops.iter().zip(bprops.iter()) { - let unified_prop = - self.unify(prop_a.prop_type(), prop_b.prop_type(), span)?; - unified_props.push(ComponentProperty::new( - *prop_a.visibility(), - prop_a.name().to_string(), - unified_prop, - )); - } - let HirType::Component { props } = self.types_module.get_type_mut(a) else { - unreachable!() - }; - props.clear(); - props.extend_from_slice(&unified_props); - let HirType::Component { props } = self.types_module.get_type_mut(b) else { - unreachable!() - }; - props.clear(); - props.extend_from_slice(&unified_props); - Ok(*a) - } - (HirType::Component { .. }, HirType::GenericComponent) => Ok(*a), - (HirType::GenericComponent, HirType::Component { .. }) => Ok(*b), - - (HirType::Struct { fields: f1 }, HirType::Struct { fields: f2 }) => { - if f1.len() != f2.len() { - Err(TypeError { - kind: TypeErrorKind::IncompatibleTypes { - expected: concrete_a, - received: concrete_b, - }, - span: *span, - }) - } else { - for idx in 0..f1.len() { - self.unify(&f1[idx], &f2[idx], span)?; - } - Ok(*a) - } - } - (HirType::Tuple { fields: f1 }, HirType::Tuple { fields: f2 }) => { - if f1.len() != f2.len() { - Err(TypeError { - kind: TypeErrorKind::IncompatibleTypes { - expected: concrete_a, - received: concrete_b, - }, - span: *span, - }) - } else { - let mut new_fields = Vec::with_capacity(f1.len()); - - for (t1, t2) in f1.iter().zip(f2.iter()) { - let unified = self.unify(t1, t2, span)?; - new_fields.push(unified); - } - - Ok(self.types_module.add_tuple_type(new_fields)) - } - } - (HirType::VarReference(rf1), HirType::VarReference(rf2)) => { - let Some(rf1) = self.types_module.get_variable(rf1).cloned() else { - unreachable!("Variable should have already been declared") - }; - let Some(rf2) = self.types_module.get_variable(rf2).cloned() else { - unreachable!("Variable2 should have already been declared") - }; - self.unify(&rf1, &rf2, span) - } - - (_, _) => Err(TypeError { - kind: TypeErrorKind::IncompatibleTypes { - expected: concrete_b, - received: concrete_a, - }, - span: *span, - }), - } - } - } - } - - fn unify_with_ref(&mut self, rf: TypeId, ty: TypeId, span: &Span) -> Result { - let resolved_ref = self.resolve(&rf, span)?; - if !matches!( - self.types_module.get_type(&resolved_ref), - HirType::Reference { .. } - ) { - return self.unify(&resolved_ref, &ty, span); - } - if let HirType::Reference { rf: refe, .. } = self.types_module.get_type(&ty) - && rf == *refe - { - return Ok(ty); - } - let ty = self.types_module.get_type(&ty); - if self.recursive_ty(rf, ty) { - return Err(TypeError { - kind: TypeErrorKind::CiclicType { ty: ty.clone() }, - span: *span, - }); - } - self.substitute(rf, ty.clone()); - let ty = self.types_module.insert_unnamed_type(HirType::Reference { - rf, - generics: Vec::new(), - }); - Ok(ty) - } - - /// Checks if the provided `ty` is recursive - fn recursive_ty(&self, ty_ref: TypeId, ty: &HirType) -> bool { - match ty { - HirType::Reference { rf, .. } => { - if ty_ref == *rf { - true - } else if let Some(resolved) = self.types.get(rf) { - self.recursive_ty(ty_ref, resolved) - } else { - false - } - } - HirType::Component { props } => props.iter().any(|prop| { - self.recursive_ty(ty_ref, self.types_module.get_type(prop.prop_type())) - }), - _ => false, - } - } - - fn ensure_function_returns( - &mut self, - statements: &[HirStatement], - return_type: TypeId, - span: &Span, - ) -> Result<()> { - let return_type = self.resolve(&return_type, span)?; - if return_type == self.types_module.void_id() { - return Ok(()); - } - - // Functions return implicitly through their tail expression today, - // so a non-void function must end with a lowered `Return`. - if matches!( - statements.last().map(|statement| &statement.kind), - Some(HirStatementKind::Return { .. }) - ) { - return Ok(()); - } - - Err(TypeError { - kind: TypeErrorKind::MissingReturnValue { - expected: self.types_module.get_type(&return_type).clone(), - }, - span: *span, - }) - } -} diff --git a/crates/checker/src/statement.rs b/crates/checker/src/statement.rs deleted file mode 100644 index 860fc5db..00000000 --- a/crates/checker/src/statement.rs +++ /dev/null @@ -1,59 +0,0 @@ -//! Type-checking logic for statements in the Slynx compiler. -//! -//! This module provides the implementation for type-checking statements, including -//! variable declarations, assignments, and expressions. It uses the `TypeChecker` -//! context to resolve types and unify them with expected types. - -use crate::{Result, TypeChecker}; -use slynx_hir::{ - TypeId, - model::{HirStatement, HirStatementKind}, -}; - -impl TypeChecker { - ///Sets the default value on the given `statement` - pub(super) fn default_statement( - &mut self, - statement: &mut HirStatement, - expected: &TypeId, - ) -> Result<()> { - match &mut statement.kind { - HirStatementKind::Variable { name, value } => { - // Ensure the initializer expression is fully typed and propagate it to the variable. - self.default_expr(value)?; - - let ty = self.get_type_of_expr(value)?; - - value.ty = ty; - self.types_module.insert_variable(*name, ty); - } - - HirStatementKind::Assign { lhs, value } => { - let ty = self.resolve(&lhs.ty, &lhs.span)?; - value.ty = self.unify(&ty, &value.ty, &value.span)?; - } - - HirStatementKind::Expression { expr } => self.default_expr(expr)?, - - HirStatementKind::Return { expr } => { - self.default_expr(expr)?; - let unify = self.unify(&expr.ty, expected, &statement.span)?; - - expr.ty = unify; - } - - HirStatementKind::While { condition, body } => { - condition.ty = self.get_type_of_expr(condition)?; - let bool_id = self.types_module.bool_id(); - - condition.ty = self.unify(&condition.ty, &bool_id, &condition.span)?; - - for stmt in body { - self.default_statement(stmt, expected)?; - } - } - }; - - Ok(()) - } -} diff --git a/crates/checker/src/styles.rs b/crates/checker/src/styles.rs deleted file mode 100644 index e4d6de14..00000000 --- a/crates/checker/src/styles.rs +++ /dev/null @@ -1,103 +0,0 @@ -//* File specific to implement styles resolving, hoisting, and statment resolval - -use slynx_hir::model::{ - HirDeclaration, HirDeclarationKind, HirStyleBlock, HirStyleStatement, HirStyleUsage, - StylesDefinition, -}; - -use crate::{Result, TypeChecker, error::TypeError}; - -impl TypeChecker { - pub fn check_style_statement(&mut self, statement: &mut HirStyleStatement) -> Result<()> { - let void = self.types_module.void_id(); - match statement { - HirStyleStatement::Statement(statement) => self.resolve_statement(statement, &void), - HirStyleStatement::Styles(blocks) => { - for block in blocks { - for def in &mut block.definitions { - def.expr.ty = self.unify(&def.expected_type, &def.expr.ty, &def.span)?; - } - } - Ok(()) - } - } - } - pub fn check_style_usage(&mut self, usage: &mut HirStyleUsage) -> Result<()> { - let decl_ty = self.declarations[usage.style.as_raw() as usize]; - let params_types = usage - .params - .iter_mut() - .map(|param| self.get_type_of_expr(param)) - .collect::>>()?; - let slynx_hir::model::HirType::Style { args } = self.types_module.get_type(&decl_ty) else { - unreachable!("Type of style should be Style"); - }; - let args = args.clone(); - if usage.params.len() != args.len() { - return Err(TypeError::invalid_funcall_args( - args.len(), - usage.params.len(), - usage.span, - )); - } - for (idx, paramty) in params_types.iter().enumerate() { - let arg = &args[idx]; - let span = usage.params[idx].span; - usage.params[idx].ty = self.unify(paramty, arg, &span)?; - } - - Ok(()) - } - - pub fn check_stylesheet(&mut self, style: &mut HirDeclaration) -> Result<()> { - let HirDeclarationKind::StyleSheet { - ref mut statements, - ref mut usages, - .. - } = style.kind - else { - unimplemented!("check stylesheet should receive a stylesheet"); - }; - for usage in usages { - self.check_style_usage(usage)?; - } - for statement in statements { - self.check_style_statement(statement)?; - } - Ok(()) - } - - ///Sets the default value on the given `definition` style. - pub fn default_style_definition(&mut self, definition: &mut StylesDefinition) -> Result<()> { - self.default_expr(&mut definition.expr)?; - definition.expr.ty = self.unify( - &definition.expr.ty, - &definition.expected_type, - &definition.span, - )?; - Ok(()) - } - ///Defaults all the definitions on the given `block` - pub fn default_style_block(&mut self, block: &mut HirStyleBlock) -> Result<()> { - for definition in &mut block.definitions { - self.default_style_definition(definition)?; - } - - Ok(()) - } - - pub fn default_style_statement(&mut self, statement: &mut HirStyleStatement) -> Result<()> { - match statement { - HirStyleStatement::Statement(s) => { - let infer = self.types_module.infer_id(); - self.default_statement(s, &infer)?; - } - HirStyleStatement::Styles(styleblocks) => { - for block in styleblocks { - self.default_style_block(block)?; - } - } - } - Ok(()) - } -} diff --git a/crates/slynx_ir/Cargo.lock b/crates/ir/Cargo.lock similarity index 100% rename from crates/slynx_ir/Cargo.lock rename to crates/ir/Cargo.lock diff --git a/crates/slynx_ir/Cargo.toml b/crates/ir/Cargo.toml similarity index 100% rename from crates/slynx_ir/Cargo.toml rename to crates/ir/Cargo.toml diff --git a/crates/slynx_ir/README.md b/crates/ir/README.md similarity index 100% rename from crates/slynx_ir/README.md rename to crates/ir/README.md diff --git a/crates/slynx_ir/STYLES_TABLE.md b/crates/ir/STYLES_TABLE.md similarity index 100% rename from crates/slynx_ir/STYLES_TABLE.md rename to crates/ir/STYLES_TABLE.md diff --git a/crates/slynx_ir/docs/component-init-lowering.md b/crates/ir/docs/component-init-lowering.md similarity index 100% rename from crates/slynx_ir/docs/component-init-lowering.md rename to crates/ir/docs/component-init-lowering.md diff --git a/crates/slynx_ir/docs/linerize-the-graph.md b/crates/ir/docs/linerize-the-graph.md similarity index 100% rename from crates/slynx_ir/docs/linerize-the-graph.md rename to crates/ir/docs/linerize-the-graph.md diff --git a/crates/slynx_ir/docs/reactive-graph-generation.md b/crates/ir/docs/reactive-graph-generation.md similarity index 100% rename from crates/slynx_ir/docs/reactive-graph-generation.md rename to crates/ir/docs/reactive-graph-generation.md diff --git a/crates/slynx_ir/docs/stylesheet-lowering.md b/crates/ir/docs/stylesheet-lowering.md similarity index 100% rename from crates/slynx_ir/docs/stylesheet-lowering.md rename to crates/ir/docs/stylesheet-lowering.md diff --git a/crates/slynx_ir/src/ir/api.rs b/crates/ir/src/api.rs similarity index 98% rename from crates/slynx_ir/src/ir/api.rs rename to crates/ir/src/api.rs index de5ef018..28331d96 100644 --- a/crates/slynx_ir/src/ir/api.rs +++ b/crates/ir/src/api.rs @@ -2,7 +2,7 @@ use either::Either::{Left, Right}; use crate::{ Component, Context, ControlFlowGraph, IRPointer, IRTypes, Instruction, Label, Operand, SlynxIR, - Value, ir::instructions::InstructionPtr, + Value, instructions::InstructionPtr, }; impl SlynxIR { diff --git a/crates/slynx_ir/src/cfg/mod.rs b/crates/ir/src/cfg/mod.rs similarity index 100% rename from crates/slynx_ir/src/cfg/mod.rs rename to crates/ir/src/cfg/mod.rs diff --git a/crates/slynx_ir/src/ir/components.rs b/crates/ir/src/components.rs similarity index 99% rename from crates/slynx_ir/src/ir/components.rs rename to crates/ir/src/components.rs index 0596a8b2..15c57fda 100644 --- a/crates/slynx_ir/src/ir/components.rs +++ b/crates/ir/src/components.rs @@ -4,8 +4,7 @@ use slynx_hir::model::{ }; use crate::{ - Context, IRError, IRPointer, IRType, IRTypeId, Instruction, SlynxIR, Value, - ir::temp::TempIRData, + Context, IRError, IRPointer, IRType, IRTypeId, Instruction, SlynxIR, TempIRData, Value, }; /// Holds pointers to the two functions generated for a stylesheet, plus diff --git a/crates/slynx_ir/src/ir/contexts.rs b/crates/ir/src/contexts.rs similarity index 98% rename from crates/slynx_ir/src/ir/contexts.rs rename to crates/ir/src/contexts.rs index 5dc6538f..60cd3c54 100644 --- a/crates/slynx_ir/src/ir/contexts.rs +++ b/crates/ir/src/contexts.rs @@ -4,11 +4,7 @@ use slynx_hir::{ }; use crate::{ - IRError, IRTypeId, SlynxIR, - ir::{ - model::{Context, IRPointer, Instruction, Label, Operand, Value}, - temp::TempIRData, - }, + Context, IRError, IRPointer, IRTypeId, Instruction, Label, Operand, SlynxIR, TempIRData, Value, }; impl SlynxIR { diff --git a/crates/slynx_ir/src/error.rs b/crates/ir/src/error.rs similarity index 100% rename from crates/slynx_ir/src/error.rs rename to crates/ir/src/error.rs diff --git a/crates/slynx_ir/src/ir/helper/contexts.rs b/crates/ir/src/helper/contexts.rs similarity index 97% rename from crates/slynx_ir/src/ir/helper/contexts.rs rename to crates/ir/src/helper/contexts.rs index f429a731..ffafc389 100644 --- a/crates/slynx_ir/src/ir/helper/contexts.rs +++ b/crates/ir/src/helper/contexts.rs @@ -1,9 +1,6 @@ use common::SymbolPointer; -use crate::{ - Component, IRType, IRTypeId, Instruction, SlynxIR, - ir::model::{Context, IRPointer, Label, Value}, -}; +use crate::{Component, Context, IRPointer, IRType, IRTypeId, Instruction, Label, SlynxIR, Value}; impl SlynxIR { #[inline] diff --git a/crates/slynx_ir/src/ir/helper/ir_value.rs b/crates/ir/src/helper/ir_value.rs similarity index 97% rename from crates/slynx_ir/src/ir/helper/ir_value.rs rename to crates/ir/src/helper/ir_value.rs index ca64dcd9..b10fab11 100644 --- a/crates/slynx_ir/src/ir/helper/ir_value.rs +++ b/crates/ir/src/helper/ir_value.rs @@ -1,4 +1,4 @@ -use crate::{Component, IRPointer, IRTypeId, Operand, Slot, SlynxIR, Value, ir::temp::TempIRData}; +use crate::{Component, IRPointer, IRTypeId, Operand, Slot, SlynxIR, TempIRData, Value}; impl SlynxIR { pub fn generate_void_value(&self) -> Value { diff --git a/crates/slynx_ir/src/ir/helper/label.rs b/crates/ir/src/helper/label.rs similarity index 85% rename from crates/slynx_ir/src/ir/helper/label.rs rename to crates/ir/src/helper/label.rs index 2912a8e4..75c6ab32 100644 --- a/crates/slynx_ir/src/ir/helper/label.rs +++ b/crates/ir/src/helper/label.rs @@ -1,7 +1,4 @@ -use crate::{ - SlynxIR, - ir::model::{IRPointer, Label}, -}; +use crate::{IRPointer, Label, SlynxIR}; impl SlynxIR { /// Returns a reference to the label at the given pointer. diff --git a/crates/slynx_ir/src/ir/helper/mod.rs b/crates/ir/src/helper/mod.rs similarity index 100% rename from crates/slynx_ir/src/ir/helper/mod.rs rename to crates/ir/src/helper/mod.rs diff --git a/crates/slynx_ir/src/ir/helper/styles.rs b/crates/ir/src/helper/styles.rs similarity index 95% rename from crates/slynx_ir/src/ir/helper/styles.rs rename to crates/ir/src/helper/styles.rs index 0af1e2c2..c491aac6 100644 --- a/crates/slynx_ir/src/ir/helper/styles.rs +++ b/crates/ir/src/helper/styles.rs @@ -1,31 +1,15 @@ use std::collections::HashSet; use slynx_hir::model::{ - HirDeclaration, HirDeclarationKind, HirExpression, HirStyleBlockKind, HirStyleStatement, - HirStyleUsage, StylesDefinition, + HirDeclaration, HirDeclarationKind, HirStyleBlockKind, HirStyleStatement, HirStyleUsage, + StylesDefinition, }; use crate::{ - Context, IRError, IRPointer, IRTypeId, Instruction, SlynxIR, StyleProperty, Value, - ir::temp::TempIRData, + Context, IRError, IRPointer, IRTypeId, Instruction, PropertySource, ResolvedProperty, SlynxIR, + StyleProperty, TempIRData, Value, }; -/// Describes where a style property value originates. -#[derive(Debug, Clone)] -pub(crate) enum PropertySource<'a> { - /// The property is defined directly by this stylesheet's own `styles` block. - Own(&'a HirExpression), - /// The property is inherited from a parent stylesheet via a `uses` clause. - /// The `usize` is the index into the stylesheet's `usages` array. - Inherited(usize), -} - -#[derive(Debug, Clone)] -pub(crate) struct ResolvedProperty<'a> { - pub property: StyleProperty, - pub source: PropertySource<'a>, -} - impl SlynxIR { /// Collect style property definitions from a list of style statements. pub(crate) fn collect_style_properties<'a>( diff --git a/crates/slynx_ir/src/ir/helper/types.rs b/crates/ir/src/helper/types.rs similarity index 97% rename from crates/slynx_ir/src/ir/helper/types.rs rename to crates/ir/src/helper/types.rs index 71a59c91..784f30d3 100644 --- a/crates/slynx_ir/src/ir/helper/types.rs +++ b/crates/ir/src/helper/types.rs @@ -5,11 +5,8 @@ use slynx_hir::{ }; use crate::{ - Component, IRComponent, IRError, IRType, IRTypeId, SlynxIR, - ir::{ - model::{Context, IRPointer, Instruction, Operand}, - temp::TempIRData, - }, + Component, Context, IRComponent, IRError, IRPointer, IRType, IRTypeId, Instruction, Operand, + SlynxIR, TempIRData, }; impl SlynxIR { diff --git a/crates/slynx_ir/src/ir/instructions.rs b/crates/ir/src/instructions.rs similarity index 99% rename from crates/slynx_ir/src/ir/instructions.rs rename to crates/ir/src/instructions.rs index 54c8213a..b13eb193 100644 --- a/crates/slynx_ir/src/ir/instructions.rs +++ b/crates/ir/src/instructions.rs @@ -3,8 +3,8 @@ use either::Either; use slynx_hir::model::{HirExpression, HirExpressionKind, HirStatement, HirStatementKind}; use crate::{ - IRError, IRPointer, IRTypeId, Instruction, Label, Operand, Slot, SlynxIR, Value, ValueKind, - ir::temp::TempIRData, + IRError, IRPointer, IRTypeId, Instruction, Label, Operand, Slot, SlynxIR, TempIRData, Value, + ValueKind, }; pub type InstructionPtr = diff --git a/crates/slynx_ir/src/ir/mod.rs b/crates/ir/src/ir.rs similarity index 93% rename from crates/slynx_ir/src/ir/mod.rs rename to crates/ir/src/ir.rs index 5e286dad..5233c765 100644 --- a/crates/slynx_ir/src/ir/mod.rs +++ b/crates/ir/src/ir.rs @@ -1,45 +1,34 @@ -mod api; -mod components; -mod contexts; -mod helper; -mod instructions; -mod model; -mod temp; -mod visualize; - use common::SymbolsModule; -pub use model::*; -pub use visualize::*; - use slynx_hir::{ model::{HirDeclaration, HirDeclarationKind, HirType}, modules::TypesModule, }; -use crate::{IRError, IRTypes}; - -use temp::{AuxiliaryStyle, TempIRData}; +use crate::{ + AuxiliaryStyle, Component, Context, Formatter, IRError, IRPointer, IRTypes, Instruction, Label, + Operand, Slot, TempIRData, Value, +}; #[derive(Debug)] ///All the IR containing contexts, labels, instructions and operands pub struct SlynxIR { ///The contexts of this IR - contexts: Vec, + pub(crate) contexts: Vec, ///The Components of this IR - components: Vec, + pub(crate) components: Vec, ///The labels of this IR - labels: Vec