Move scopehints to own file
This commit is contained in:
		
							parent
							
								
									be7fa71b53
								
							
						
					
					
						commit
						873948a0c4
					
				@ -6,6 +6,7 @@ use crate::token_stream::TokenRange;
 | 
			
		||||
 | 
			
		||||
mod display;
 | 
			
		||||
pub mod pass;
 | 
			
		||||
mod scopehints;
 | 
			
		||||
pub mod typecheck;
 | 
			
		||||
pub mod types;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										122
									
								
								reid/src/mir/scopehints.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										122
									
								
								reid/src/mir/scopehints.rs
									
									
									
									
									
										Normal file
									
								
							@ -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<ScopeHint, ErrorKind> {
 | 
			
		||||
        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<HashMap<String, (bool, usize)>>,
 | 
			
		||||
    /// Simple list of types that variables can refrence
 | 
			
		||||
    type_hints: RefCell<HashMap<usize, TypeKind>>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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<ScopeHint<'outer>, 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<ScopeHint<'outer>, 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<ScopeHint<'outer>, 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<TypeRef<'scope>, 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)?))
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -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<HashMap<String, usize>>,
 | 
			
		||||
    /// Simple list of types that variables can refrence
 | 
			
		||||
    type_hints: RefCell<HashMap<usize, TypeKind>>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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<ScopeHint<'outer>, 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<ScopeHint<'outer>, 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<TypeKind, ErrorKind> {
 | 
			
		||||
    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.
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user