diff --git a/crates/hir-analysis/src/ty/ty_check/mod.rs b/crates/hir-analysis/src/ty/ty_check/mod.rs index 3ab507ffd..e1edd4950 100644 --- a/crates/hir-analysis/src/ty/ty_check/mod.rs +++ b/crates/hir-analysis/src/ty/ty_check/mod.rs @@ -1,12 +1,15 @@ mod env; use env::ThCheckEnv; -use hir::hir_def::{Body, Expr, ExprId, Func, LitKind, Partial, PatId, Stmt, StmtId}; +use hir::{ + hir_def::{Body, Expr, ExprId, Func, LitKind, Partial, Pat, PatId, Stmt, StmtId}, + span::DynLazySpan, +}; use rustc_hash::FxHashMap; use super::{ diagnostics::{FuncBodyDiagAccumulator, TyCheckDiag}, - ty_def::{InvalidCause, Kind, TyId, TyVarUniverse}, + ty_def::{InvalidCause, Kind, TyId, TyVar, TyVarUniverse}, ty_lower::lower_hir_ty, unify::{UnificationError, UnificationTable}, }; @@ -75,15 +78,7 @@ impl<'db> TyChecker<'db> { }; let actual = match expr_data { - Expr::Lit(lit) => match lit { - LitKind::Bool(_) => TyId::bool(self.db), - LitKind::Int(_) => self.table.new_var(TyVarUniverse::Integral, &Kind::Star), - LitKind::String(s) => { - let len_bytes = s.len_bytes(self.db.as_hir_db()); - self.table - .new_var(TyVarUniverse::String(len_bytes), &Kind::Star) - } - }, + Expr::Lit(lit) => self.lit_ty(lit), Expr::Block(stmts) => { if stmts.is_empty() { @@ -115,12 +110,90 @@ impl<'db> TyChecker<'db> { Expr::Match(..) => todo!(), }; + self.unify_ty(expr, actual, expected) + } + + fn check_stmt(&mut self, stmt: StmtId, expected: TyId) -> TyId { + let Partial::Present(stmt_data) = self.env.stmt_data(stmt) else { + return TyId::invalid(self.db, InvalidCause::Other); + }; + + match stmt_data { + Stmt::Let(..) => todo!(), + Stmt::Assign(..) => todo!(), + Stmt::For(..) => todo!(), + Stmt::While(..) => todo!(), + Stmt::Continue => todo!(), + Stmt::Break => todo!(), + Stmt::Return(..) => todo!(), + Stmt::Expr(expr) => self.check_expr_ty(*expr, expected), + } + } + + fn check_pat(&mut self, pat: PatId, expected: TyId) -> TyId { + let Partial::Present(pat_data) = pat.data(self.db.as_hir_db(), self.env.body()) else { + let actual = TyId::invalid(self.db, InvalidCause::Other); + return self.unify_ty(pat, actual, expected); + }; + + match pat_data { + Pat::WildCard => { + let ty_var = self.table.new_var(TyVarUniverse::General, &Kind::Star); + self.unify_ty(pat, ty_var, expected) + } + + Pat::Rest => todo!(), + + Pat::Lit(lit) => { + let actual = match lit { + Partial::Present(lit) => self.lit_ty(lit), + Partial::Absent => TyId::invalid(self.db, InvalidCause::Other), + }; + self.unify_ty(pat, actual, expected) + } + + Pat::Tuple(tup) => { + let len = tup.len(); + todo!() + } + + Pat::Path(path) => todo!(), + + Pat::PathTuple(pat, tup) => todo!(), + + Pat::Record(path, fields) => todo!(), + + Pat::Or(lhs, rhs) => { + self.check_pat(*lhs, expected); + self.check_pat(*rhs, expected) + } + } + } + + fn lit_ty(&mut self, lit: &LitKind) -> TyId { + match lit { + LitKind::Bool(_) => TyId::bool(self.db), + LitKind::Int(_) => self.table.new_var(TyVarUniverse::Integral, &Kind::Star), + LitKind::String(s) => { + let len_bytes = s.len_bytes(self.db.as_hir_db()); + self.table + .new_var(TyVarUniverse::String(len_bytes), &Kind::Star) + } + } + } + + fn unify_ty(&mut self, t: T, actual: TyId, expected: TyId) -> TyId + where + T: Into, + { + let t = t.into(); + let actual = match self.table.unify(expected, actual) { Ok(()) => { let actual = actual.apply_subst(self.db, &mut self.table); - self.env.type_expr(expr, actual); actual } + Err(UnificationError::TypeMismatch) => { let actual = actual.apply_subst(self.db, &mut self.table); let expected = expected.apply_subst(self.db, &mut self.table); @@ -128,7 +201,7 @@ impl<'db> TyChecker<'db> { self.db, TyCheckDiag::type_mismatch( self.db, - expr.lazy_span(self.env.body()).into(), + t.lazy_span(self.env.body()), expected, actual, ) @@ -140,32 +213,19 @@ impl<'db> TyChecker<'db> { Err(UnificationError::OccursCheckFailed) => { FuncBodyDiagAccumulator::push( self.db, - TyCheckDiag::InfiniteOccurrence(expr.lazy_span(self.env.body()).into()).into(), + TyCheckDiag::InfiniteOccurrence(t.lazy_span(self.env.body())).into(), ); TyId::invalid(self.db, InvalidCause::Other) } }; - self.env.type_expr(expr, actual); - actual - } - - fn check_stmt(&mut self, stmt: StmtId, expected: TyId) -> TyId { - let Partial::Present(stmt_data) = self.env.stmt_data(stmt) else { - return TyId::invalid(self.db, InvalidCause::Other); - }; - - match stmt_data { - Stmt::Let(..) => todo!(), - Stmt::Assign(..) => todo!(), - Stmt::For(..) => todo!(), - Stmt::While(..) => todo!(), - Stmt::Continue => todo!(), - Stmt::Break => todo!(), - Stmt::Return(..) => todo!(), - Stmt::Expr(expr) => self.check_expr_ty(*expr, expected), + match t { + Typeable::Expr(expr) => self.env.type_expr(expr, actual), + Typeable::Pat(pat) => self.env.type_pat(pat, actual), } + + actual } } @@ -199,3 +259,18 @@ impl TypedBody { } } } + +#[derive(Clone, Copy, PartialEq, Eq, derive_more::From)] +enum Typeable { + Expr(ExprId), + Pat(PatId), +} + +impl Typeable { + fn lazy_span(self, body: Body) -> DynLazySpan { + match self { + Self::Expr(expr) => expr.lazy_span(body).into(), + Self::Pat(pat) => pat.lazy_span(body).into(), + } + } +} diff --git a/crates/hir/src/hir_def/pat.rs b/crates/hir/src/hir_def/pat.rs index 1bf6156fb..dc52c9b61 100644 --- a/crates/hir/src/hir_def/pat.rs +++ b/crates/hir/src/hir_def/pat.rs @@ -1,8 +1,7 @@ use cranelift_entity::entity_impl; -use crate::{span::pat::LazyPatSpan, HirDb}; - use super::{Body, IdentId, LitKind, Partial, PathId}; +use crate::{span::pat::LazyPatSpan, HirDb}; #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum Pat { @@ -53,3 +52,8 @@ pub struct RecordPatField { pub label: Partial, pub pat: PatId, } + +pub enum Bar { + X(i32), + Y(i32), +}