diff --git a/reid/src/mir/mod.rs b/reid/src/mir/mod.rs index aa2cdcb..7024878 100644 --- a/reid/src/mir/mod.rs +++ b/reid/src/mir/mod.rs @@ -6,6 +6,7 @@ use crate::token_stream::TokenRange; mod display; pub mod pass; +mod scopehints; pub mod typecheck; pub mod types; diff --git a/reid/src/mir/scopehints.rs b/reid/src/mir/scopehints.rs new file mode 100644 index 0000000..a8e1773 --- /dev/null +++ b/reid/src/mir/scopehints.rs @@ -0,0 +1,122 @@ +use std::{cell::RefCell, collections::HashMap}; + +use super::{ + typecheck::{Collapsable, ErrorKind}, + TypeKind, +}; + +#[derive(Debug, Clone)] +pub struct ScopeHint<'scope>(usize, &'scope ScopeHints<'scope>); + +impl<'scope> ScopeHint<'scope> { + pub fn resolve(&self) -> TypeRef { + let mut scope = self.1; + while !scope.type_hints.borrow().contains_key(&self.0) { + scope = scope.outer.as_ref().unwrap(); + } + let ty = scope.type_hints.borrow().get(&self.0).unwrap().clone(); + match ty.known() { + Ok(narrow) => TypeRef::Literal(narrow), + Err(_) => TypeRef::Hint(self.clone()), + } + } + + pub fn narrow(&self, ty_ref: &TypeRef) -> Result { + match ty_ref { + TypeRef::Hint(other) => self.1.combine_vars(self, other), + TypeRef::Literal(ty) => self.1.narrow_to_type(self, ty), + } + } +} + +#[derive(Debug, Default)] +pub struct ScopeHints<'outer> { + outer: Option<&'outer ScopeHints<'outer>>, + /// Mapping of what types variables point to + variables: RefCell>, + /// Simple list of types that variables can refrence + type_hints: RefCell>, +} + +impl<'outer> ScopeHints<'outer> { + fn get_idx(&self) -> usize { + self.type_hints.borrow().len() + self.outer.as_ref().map(|o| o.get_idx()).unwrap_or(0) + } + + pub fn new_var( + &'outer self, + name: String, + mutable: bool, + initial_ty: TypeKind, + ) -> Result, ErrorKind> { + if self.variables.borrow().contains_key(&name) { + return Err(ErrorKind::VariableAlreadyDefined(name)); + } + let idx = self.get_idx(); + self.variables.borrow_mut().insert(name, (mutable, idx)); + self.type_hints.borrow_mut().insert(idx, initial_ty); + Ok(ScopeHint(idx, self)) + } + + fn narrow_to_type( + &'outer self, + hint: &ScopeHint, + ty: &TypeKind, + ) -> Result, ErrorKind> { + let mut hints = self.type_hints.borrow_mut(); + let existing = hints.get_mut(&hint.0).unwrap(); + *existing = existing.collapse_into(&ty)?; + Ok(ScopeHint(hint.0, self)) + } + + fn combine_vars( + &'outer self, + hint1: &ScopeHint, + hint2: &ScopeHint, + ) -> Result, ErrorKind> { + let ty = self.type_hints.borrow().get(&hint2.0).unwrap().clone(); + self.narrow_to_type(&hint1, &ty)?; + for (_, (_, idx)) in self.variables.borrow_mut().iter_mut() { + if *idx == hint2.0 { + *idx = hint1.0; + } + } + Ok(ScopeHint(hint1.0, self)) + } + + pub fn inner(&'outer self) -> ScopeHints<'outer> { + ScopeHints { + outer: Some(self), + variables: Default::default(), + type_hints: Default::default(), + } + } + + pub fn find_hint(&'outer self, name: &String) -> Option<(bool, ScopeHint<'outer>)> { + self.variables + .borrow() + .get(name) + .map(|(mutable, idx)| (*mutable, ScopeHint(*idx, self))) + } +} + +pub enum TypeRef<'scope> { + Hint(ScopeHint<'scope>), + Literal(TypeKind), +} + +impl<'scope> TypeRef<'scope> { + pub fn narrow( + &'scope self, + other: &'scope TypeRef<'scope>, + ) -> Result, ErrorKind> { + match (self, other) { + (TypeRef::Hint(hint), unk) | (unk, TypeRef::Hint(hint)) => { + Ok(TypeRef::Hint(hint.narrow(unk)?)) + } + (TypeRef::Literal(lit1), TypeRef::Literal(lit2)) => { + Ok(TypeRef::Literal(lit1.collapse_into(lit2)?)) + } + } + } +} diff --git a/reid/src/mir/typecheck.rs b/reid/src/mir/typecheck.rs index f39b820..3d4dfc6 100644 --- a/reid/src/mir/typecheck.rs +++ b/reid/src/mir/typecheck.rs @@ -8,6 +8,7 @@ use VagueType::*; use super::{ pass::{Pass, PassState, ScopeFunction, ScopeVariable, Storage}, + scopehints::ScopeHints, types::ReturnType, }; @@ -43,78 +44,6 @@ pub enum ErrorKind { /// MIR. pub struct TypeCheck; -#[derive(Debug, Clone)] -pub struct ScopeHint<'scope>(usize, &'scope ScopeHints<'scope>); - -impl<'scope> ScopeHint<'scope> { - fn resolve(&self) -> TypeKind { - let mut scope = self.1; - while !scope.type_hints.borrow().contains_key(&self.0) { - scope = scope.outer.as_ref().unwrap(); - } - scope.type_hints.borrow().get(&self.0).unwrap().clone() - } -} - -#[derive(Debug, Default)] -struct ScopeHints<'outer> { - outer: Option<&'outer ScopeHints<'outer>>, - /// Mapping of what types variables point to - variables: RefCell>, - /// Simple list of types that variables can refrence - type_hints: RefCell>, -} - -impl<'outer> ScopeHints<'outer> { - fn get_idx(&self) -> usize { - self.type_hints.borrow().len() + self.outer.as_ref().map(|o| o.get_idx()).unwrap_or(0) - } - - fn new_var(&'outer self, name: String, initial_ty: TypeKind) -> ScopeHint<'outer> { - if self.variables.borrow().contains_key(&name) { - panic!("Variable was already defined!") - } - let idx = self.get_idx(); - self.variables.borrow_mut().insert(name, idx); - self.type_hints.borrow_mut().insert(idx, initial_ty); - ScopeHint(idx, self) - } - - fn narrow_hint( - &'outer self, - hint: &ScopeHint, - ty: &TypeKind, - ) -> Result, ErrorKind> { - let mut hints = self.type_hints.borrow_mut(); - let existing = hints.get_mut(&hint.0).unwrap(); - *existing = existing.collapse_into(&ty)?; - Ok(ScopeHint(hint.0, self)) - } - - fn combine_vars( - &'outer self, - hint1: &ScopeHint, - hint2: &ScopeHint, - ) -> Result, ErrorKind> { - let ty = self.type_hints.borrow().get(&hint2.0).unwrap().clone(); - self.narrow_hint(&hint1, &ty)?; - for (_, val) in self.variables.borrow_mut().iter_mut() { - if *val == hint2.0 { - *val = hint1.0; - } - } - Ok(ScopeHint(hint1.0, self)) - } - - fn inner(&'outer self) -> ScopeHints<'outer> { - ScopeHints { - outer: Some(self), - variables: Default::default(), - type_hints: Default::default(), - } - } -} - impl Pass for TypeCheck { type TError = ErrorKind; @@ -154,7 +83,8 @@ impl FunctionDefinition { }; match inferred { - Ok(t) => try_collapse(&return_type, &t) + Ok(t) => return_type + .collapse_into(&t) .or(Err(ErrorKind::ReturnTypeMismatch(return_type, t))), Err(e) => Ok(state.or_else(Err(e), return_type, self.block_meta())), } @@ -388,7 +318,7 @@ impl Expression { // Make sure function return type is the same as the claimed // return type - let ret_t = try_collapse(&f.ret, &function_call.return_type)?; + let ret_t = f.ret.collapse_into(&function_call.return_type)?; // Update typing to be more accurate function_call.return_type = ret_t; Ok(ret_t) @@ -502,12 +432,6 @@ impl TypeKind { } } -fn try_collapse(lhs: &TypeKind, rhs: &TypeKind) -> Result { - lhs.collapse_into(rhs) - .or(rhs.collapse_into(lhs)) - .or(Err(ErrorKind::TypesIncompatible(*lhs, *rhs))) -} - pub trait Collapsable: Sized + Clone { /// Try to narrow two types into one singular type. E.g. Vague(Number) and /// I32 could be narrowed to just I32.