Split typechecking into two passes, finish first pass
This commit is contained in:
		
							parent
							
								
									873948a0c4
								
							
						
					
					
						commit
						61ab5002e4
					
				@ -67,6 +67,8 @@ pub enum VagueType {
 | 
				
			|||||||
    Unknown,
 | 
					    Unknown,
 | 
				
			||||||
    #[error("Number")]
 | 
					    #[error("Number")]
 | 
				
			||||||
    Number,
 | 
					    Number,
 | 
				
			||||||
 | 
					    #[error("Hinted({0})")]
 | 
				
			||||||
 | 
					    Hinted(usize),
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl TypeKind {
 | 
					impl TypeKind {
 | 
				
			||||||
@ -158,6 +160,14 @@ impl Literal {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl VagueLiteral {
 | 
				
			||||||
 | 
					    pub fn as_type(self: &VagueLiteral) -> VagueType {
 | 
				
			||||||
 | 
					        match self {
 | 
				
			||||||
 | 
					            VagueLiteral::Number(_) => VagueType::Number,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Debug, Clone, Copy)]
 | 
					#[derive(Debug, Clone, Copy)]
 | 
				
			||||||
pub enum BinaryOperator {
 | 
					pub enum BinaryOperator {
 | 
				
			||||||
    Add,
 | 
					    Add,
 | 
				
			||||||
 | 
				
			|||||||
@ -1,46 +1,77 @@
 | 
				
			|||||||
use std::{cell::RefCell, collections::HashMap};
 | 
					use std::{cell::RefCell, collections::HashMap, fmt::Error, rc::Rc};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use super::{
 | 
					use super::{
 | 
				
			||||||
    typecheck::{Collapsable, ErrorKind},
 | 
					    typecheck::{Collapsable, ErrorKind},
 | 
				
			||||||
    TypeKind,
 | 
					    BinaryOperator, Literal, TypeKind, VagueLiteral, VagueType,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Debug, Clone)]
 | 
					#[derive(Clone)]
 | 
				
			||||||
pub struct ScopeHint<'scope>(usize, &'scope ScopeHints<'scope>);
 | 
					pub struct ScopeHint<'scope>(TypeIdRef, &'scope ScopeHints<'scope>);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl<'scope> ScopeHint<'scope> {
 | 
					impl<'scope> ScopeHint<'scope> {
 | 
				
			||||||
    pub fn resolve(&self) -> TypeRef {
 | 
					    pub unsafe fn raw_type(&self) -> TypeKind {
 | 
				
			||||||
        let mut scope = self.1;
 | 
					        if let Some(ty) = self.1.types.hints.borrow().get(*self.0.borrow()) {
 | 
				
			||||||
        while !scope.type_hints.borrow().contains_key(&self.0) {
 | 
					            *ty
 | 
				
			||||||
            scope = scope.outer.as_ref().unwrap();
 | 
					        } else {
 | 
				
			||||||
        }
 | 
					            panic!("TODO")
 | 
				
			||||||
        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> {
 | 
					    pub fn narrow(&mut self, ty_ref: &TypeRef) -> Result<ScopeHint<'scope>, ErrorKind> {
 | 
				
			||||||
        match ty_ref {
 | 
					        match ty_ref {
 | 
				
			||||||
            TypeRef::Hint(other) => self.1.combine_vars(self, other),
 | 
					            TypeRef::Hint(other) => self.1.combine_vars(self, other),
 | 
				
			||||||
            TypeRef::Literal(ty) => self.1.narrow_to_type(self, ty),
 | 
					            TypeRef::Literal(ty) => self.1.narrow_to_type(self, ty),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn as_type(&self) -> TypeKind {
 | 
				
			||||||
 | 
					        TypeKind::Vague(super::VagueType::Hinted(*self.0.borrow()))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<'scope> std::fmt::Debug for ScopeHint<'scope> {
 | 
				
			||||||
 | 
					    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | 
				
			||||||
 | 
					        f.debug_tuple("Hint")
 | 
				
			||||||
 | 
					            .field(&self.0)
 | 
				
			||||||
 | 
					            .field(unsafe { &self.raw_type() })
 | 
				
			||||||
 | 
					            .finish()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type TypeIdRef = Rc<RefCell<usize>>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Debug, Default)]
 | 
					#[derive(Debug, Default)]
 | 
				
			||||||
 | 
					pub struct TypeHints {
 | 
				
			||||||
 | 
					    /// Simple list of types that variables can refrence
 | 
				
			||||||
 | 
					    hints: RefCell<Vec<TypeKind>>,
 | 
				
			||||||
 | 
					    types: RefCell<Vec<TypeIdRef>>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl TypeHints {
 | 
				
			||||||
 | 
					    pub fn new(&self, ty: TypeKind) -> TypeIdRef {
 | 
				
			||||||
 | 
					        let idx = self.hints.borrow().len();
 | 
				
			||||||
 | 
					        let typecell = Rc::new(RefCell::new(idx));
 | 
				
			||||||
 | 
					        self.types.borrow_mut().push(typecell.clone());
 | 
				
			||||||
 | 
					        self.hints.borrow_mut().push(ty);
 | 
				
			||||||
 | 
					        typecell
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug)]
 | 
				
			||||||
pub struct ScopeHints<'outer> {
 | 
					pub struct ScopeHints<'outer> {
 | 
				
			||||||
 | 
					    types: &'outer TypeHints,
 | 
				
			||||||
    outer: Option<&'outer ScopeHints<'outer>>,
 | 
					    outer: Option<&'outer ScopeHints<'outer>>,
 | 
				
			||||||
    /// Mapping of what types variables point to
 | 
					    /// Mapping of what types variables point to
 | 
				
			||||||
    variables: RefCell<HashMap<String, (bool, usize)>>,
 | 
					    variables: RefCell<HashMap<String, (bool, TypeIdRef)>>,
 | 
				
			||||||
    /// Simple list of types that variables can refrence
 | 
					 | 
				
			||||||
    type_hints: RefCell<HashMap<usize, TypeKind>>,
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl<'outer> ScopeHints<'outer> {
 | 
					impl<'outer> ScopeHints<'outer> {
 | 
				
			||||||
    fn get_idx(&self) -> usize {
 | 
					    pub fn from(types: &'outer TypeHints) -> ScopeHints<'outer> {
 | 
				
			||||||
        self.type_hints.borrow().len() + self.outer.as_ref().map(|o| o.get_idx()).unwrap_or(0)
 | 
					        ScopeHints {
 | 
				
			||||||
 | 
					            types,
 | 
				
			||||||
 | 
					            outer: Default::default(),
 | 
				
			||||||
 | 
					            variables: Default::default(),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn new_var(
 | 
					    pub fn new_var(
 | 
				
			||||||
@ -52,21 +83,29 @@ impl<'outer> ScopeHints<'outer> {
 | 
				
			|||||||
        if self.variables.borrow().contains_key(&name) {
 | 
					        if self.variables.borrow().contains_key(&name) {
 | 
				
			||||||
            return Err(ErrorKind::VariableAlreadyDefined(name));
 | 
					            return Err(ErrorKind::VariableAlreadyDefined(name));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        let idx = self.get_idx();
 | 
					        let idx = self.types.new(initial_ty);
 | 
				
			||||||
        self.variables.borrow_mut().insert(name, (mutable, idx));
 | 
					        self.variables
 | 
				
			||||||
        self.type_hints.borrow_mut().insert(idx, initial_ty);
 | 
					            .borrow_mut()
 | 
				
			||||||
 | 
					            .insert(name, (mutable, idx.clone()));
 | 
				
			||||||
        Ok(ScopeHint(idx, self))
 | 
					        Ok(ScopeHint(idx, self))
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn new_vague(&'outer self, vague: &VagueType) -> ScopeHint<'outer> {
 | 
				
			||||||
 | 
					        let idx = self.types.new(TypeKind::Vague(*vague));
 | 
				
			||||||
 | 
					        ScopeHint(idx, self)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn narrow_to_type(
 | 
					    fn narrow_to_type(
 | 
				
			||||||
        &'outer self,
 | 
					        &'outer self,
 | 
				
			||||||
        hint: &ScopeHint,
 | 
					        hint: &ScopeHint,
 | 
				
			||||||
        ty: &TypeKind,
 | 
					        ty: &TypeKind,
 | 
				
			||||||
    ) -> Result<ScopeHint<'outer>, ErrorKind> {
 | 
					    ) -> Result<ScopeHint<'outer>, ErrorKind> {
 | 
				
			||||||
        let mut hints = self.type_hints.borrow_mut();
 | 
					        unsafe {
 | 
				
			||||||
        let existing = hints.get_mut(&hint.0).unwrap();
 | 
					            let mut hints = self.types.hints.borrow_mut();
 | 
				
			||||||
        *existing = existing.collapse_into(&ty)?;
 | 
					            let existing = hints.get_unchecked_mut(*hint.0.borrow());
 | 
				
			||||||
        Ok(ScopeHint(hint.0, self))
 | 
					            *existing = existing.collapse_into(&ty)?;
 | 
				
			||||||
 | 
					            Ok(ScopeHint(hint.0.clone(), self))
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn combine_vars(
 | 
					    fn combine_vars(
 | 
				
			||||||
@ -74,21 +113,28 @@ impl<'outer> ScopeHints<'outer> {
 | 
				
			|||||||
        hint1: &ScopeHint,
 | 
					        hint1: &ScopeHint,
 | 
				
			||||||
        hint2: &ScopeHint,
 | 
					        hint2: &ScopeHint,
 | 
				
			||||||
    ) -> Result<ScopeHint<'outer>, ErrorKind> {
 | 
					    ) -> Result<ScopeHint<'outer>, ErrorKind> {
 | 
				
			||||||
        let ty = self.type_hints.borrow().get(&hint2.0).unwrap().clone();
 | 
					        unsafe {
 | 
				
			||||||
        self.narrow_to_type(&hint1, &ty)?;
 | 
					            let ty = self
 | 
				
			||||||
        for (_, (_, idx)) in self.variables.borrow_mut().iter_mut() {
 | 
					                .types
 | 
				
			||||||
            if *idx == hint2.0 {
 | 
					                .hints
 | 
				
			||||||
                *idx = hint1.0;
 | 
					                .borrow()
 | 
				
			||||||
 | 
					                .get_unchecked(*hint2.0.borrow())
 | 
				
			||||||
 | 
					                .clone();
 | 
				
			||||||
 | 
					            self.narrow_to_type(&hint1, &ty)?;
 | 
				
			||||||
 | 
					            for idx in self.types.types.borrow_mut().iter_mut() {
 | 
				
			||||||
 | 
					                if *idx == hint2.0 {
 | 
				
			||||||
 | 
					                    *idx.borrow_mut() = *hint1.0.borrow();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					            Ok(ScopeHint(hint1.0.clone(), self))
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        Ok(ScopeHint(hint1.0, self))
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn inner(&'outer self) -> ScopeHints<'outer> {
 | 
					    pub fn inner(&'outer self) -> ScopeHints<'outer> {
 | 
				
			||||||
        ScopeHints {
 | 
					        ScopeHints {
 | 
				
			||||||
 | 
					            types: self.types,
 | 
				
			||||||
            outer: Some(self),
 | 
					            outer: Some(self),
 | 
				
			||||||
            variables: Default::default(),
 | 
					            variables: Default::default(),
 | 
				
			||||||
            type_hints: Default::default(),
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -96,20 +142,35 @@ impl<'outer> ScopeHints<'outer> {
 | 
				
			|||||||
        self.variables
 | 
					        self.variables
 | 
				
			||||||
            .borrow()
 | 
					            .borrow()
 | 
				
			||||||
            .get(name)
 | 
					            .get(name)
 | 
				
			||||||
            .map(|(mutable, idx)| (*mutable, ScopeHint(*idx, self)))
 | 
					            .map(|(mutable, idx)| (*mutable, ScopeHint(idx.clone(), self)))
 | 
				
			||||||
 | 
					            .or(self.outer.map(|o| o.find_hint(name)).flatten())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn binop(
 | 
				
			||||||
 | 
					        &'outer self,
 | 
				
			||||||
 | 
					        op: &BinaryOperator,
 | 
				
			||||||
 | 
					        lhs: &mut TypeRef<'outer>,
 | 
				
			||||||
 | 
					        rhs: &mut TypeRef<'outer>,
 | 
				
			||||||
 | 
					    ) -> Result<TypeRef<'outer>, ErrorKind> {
 | 
				
			||||||
 | 
					        let ty = lhs.narrow(rhs)?;
 | 
				
			||||||
 | 
					        Ok(match op {
 | 
				
			||||||
 | 
					            BinaryOperator::Add => ty,
 | 
				
			||||||
 | 
					            BinaryOperator::Minus => ty,
 | 
				
			||||||
 | 
					            BinaryOperator::Mult => ty,
 | 
				
			||||||
 | 
					            BinaryOperator::And => TypeRef::Literal(TypeKind::Bool),
 | 
				
			||||||
 | 
					            BinaryOperator::Cmp(_) => TypeRef::Literal(TypeKind::Bool),
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug)]
 | 
				
			||||||
pub enum TypeRef<'scope> {
 | 
					pub enum TypeRef<'scope> {
 | 
				
			||||||
    Hint(ScopeHint<'scope>),
 | 
					    Hint(ScopeHint<'scope>),
 | 
				
			||||||
    Literal(TypeKind),
 | 
					    Literal(TypeKind),
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl<'scope> TypeRef<'scope> {
 | 
					impl<'scope> TypeRef<'scope> {
 | 
				
			||||||
    pub fn narrow(
 | 
					    pub fn narrow(&mut self, other: &mut TypeRef<'scope>) -> Result<TypeRef<'scope>, ErrorKind> {
 | 
				
			||||||
        &'scope self,
 | 
					 | 
				
			||||||
        other: &'scope TypeRef<'scope>,
 | 
					 | 
				
			||||||
    ) -> Result<TypeRef<'scope>, ErrorKind> {
 | 
					 | 
				
			||||||
        match (self, other) {
 | 
					        match (self, other) {
 | 
				
			||||||
            (TypeRef::Hint(hint), unk) | (unk, TypeRef::Hint(hint)) => {
 | 
					            (TypeRef::Hint(hint), unk) | (unk, TypeRef::Hint(hint)) => {
 | 
				
			||||||
                Ok(TypeRef::Hint(hint.narrow(unk)?))
 | 
					                Ok(TypeRef::Hint(hint.narrow(unk)?))
 | 
				
			||||||
@ -119,4 +180,27 @@ impl<'scope> TypeRef<'scope> {
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn from_type(hints: &'scope ScopeHints<'scope>, ty: TypeKind) -> TypeRef<'scope> {
 | 
				
			||||||
 | 
					        match &ty.known() {
 | 
				
			||||||
 | 
					            Ok(ty) => TypeRef::Literal(*ty),
 | 
				
			||||||
 | 
					            Err(vague) => match &vague {
 | 
				
			||||||
 | 
					                super::VagueType::Hinted(idx) => TypeRef::Hint(ScopeHint(
 | 
				
			||||||
 | 
					                    unsafe { hints.types.types.borrow().get_unchecked(*idx).clone() },
 | 
				
			||||||
 | 
					                    hints,
 | 
				
			||||||
 | 
					                )),
 | 
				
			||||||
 | 
					                _ => TypeRef::Hint(hints.new_vague(vague)),
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn from_literal(
 | 
				
			||||||
 | 
					        hints: &'scope ScopeHints<'scope>,
 | 
				
			||||||
 | 
					        lit: Literal,
 | 
				
			||||||
 | 
					    ) -> Result<TypeRef<'scope>, ErrorKind> {
 | 
				
			||||||
 | 
					        Ok(match lit {
 | 
				
			||||||
 | 
					            Literal::Vague(vague) => TypeRef::Hint(hints.new_vague(&vague.as_type())),
 | 
				
			||||||
 | 
					            _ => TypeRef::Literal(lit.as_type()),
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,9 @@
 | 
				
			|||||||
//! This module contains code relevant to doing a type checking pass on the MIR.
 | 
					//! This module contains code relevant to doing a type checking pass on the MIR.
 | 
				
			||||||
//! During typechecking relevant types are also coerced if possible.
 | 
					//! During typechecking relevant types are also coerced if possible.
 | 
				
			||||||
use std::{cell::RefCell, collections::HashMap, convert::Infallible, iter, marker::PhantomData};
 | 
					use std::{
 | 
				
			||||||
 | 
					    cell::RefCell, collections::HashMap, convert::Infallible, iter, marker::PhantomData,
 | 
				
			||||||
 | 
					    thread::scope,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::{mir::*, util::try_all};
 | 
					use crate::{mir::*, util::try_all};
 | 
				
			||||||
use TypeKind::*;
 | 
					use TypeKind::*;
 | 
				
			||||||
@ -8,8 +11,8 @@ use VagueType::*;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
use super::{
 | 
					use super::{
 | 
				
			||||||
    pass::{Pass, PassState, ScopeFunction, ScopeVariable, Storage},
 | 
					    pass::{Pass, PassState, ScopeFunction, ScopeVariable, Storage},
 | 
				
			||||||
    scopehints::ScopeHints,
 | 
					    scopehints::{ScopeHints, TypeHints, TypeRef},
 | 
				
			||||||
    types::ReturnType,
 | 
					    types::{pick_return, ReturnType},
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(thiserror::Error, Debug, Clone)]
 | 
					#[derive(thiserror::Error, Debug, Clone)]
 | 
				
			||||||
@ -77,7 +80,16 @@ impl FunctionDefinition {
 | 
				
			|||||||
        let inferred = match &mut self.kind {
 | 
					        let inferred = match &mut self.kind {
 | 
				
			||||||
            FunctionDefinitionKind::Local(block, _) => {
 | 
					            FunctionDefinitionKind::Local(block, _) => {
 | 
				
			||||||
                state.scope.return_type_hint = Some(self.return_type);
 | 
					                state.scope.return_type_hint = Some(self.return_type);
 | 
				
			||||||
                block.typecheck(state, &ScopeHints::default(), Some(return_type))
 | 
					
 | 
				
			||||||
 | 
					                let types = TypeHints::default();
 | 
				
			||||||
 | 
					                let hints = ScopeHints::from(&types);
 | 
				
			||||||
 | 
					                if let Ok(_) = block.infer_hints(state, &hints) {
 | 
				
			||||||
 | 
					                    dbg!(&block, &hints);
 | 
				
			||||||
 | 
					                    // block.typecheck(state, &hints, Some(return_type))
 | 
				
			||||||
 | 
					                    Ok(Vague(Unknown))
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    Ok(Vague(Unknown))
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            FunctionDefinitionKind::Extern => Ok(Vague(Unknown)),
 | 
					            FunctionDefinitionKind::Extern => Ok(Vague(Unknown)),
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
@ -92,6 +104,69 @@ impl FunctionDefinition {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Block {
 | 
					impl Block {
 | 
				
			||||||
 | 
					    fn infer_hints<'s>(
 | 
				
			||||||
 | 
					        &mut self,
 | 
				
			||||||
 | 
					        state: &mut PassState<ErrorKind>,
 | 
				
			||||||
 | 
					        outer_hints: &'s ScopeHints,
 | 
				
			||||||
 | 
					    ) -> Result<(ReturnKind, TypeRef<'s>), ErrorKind> {
 | 
				
			||||||
 | 
					        let mut state = state.inner();
 | 
				
			||||||
 | 
					        let inner_hints = outer_hints.inner();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for statement in &mut self.statements {
 | 
				
			||||||
 | 
					            match &mut statement.0 {
 | 
				
			||||||
 | 
					                StmtKind::Let(var, mutable, expr) => {
 | 
				
			||||||
 | 
					                    let mut var_ref =
 | 
				
			||||||
 | 
					                        state.ok(inner_hints.new_var(var.1.clone(), *mutable, var.0), var.2);
 | 
				
			||||||
 | 
					                    if let Some(var_ref) = &var_ref {
 | 
				
			||||||
 | 
					                        var.0 = var_ref.as_type();
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    let inferred = expr.infer_hints(&mut state, &inner_hints);
 | 
				
			||||||
 | 
					                    let mut expr_ty_ref = state.ok(inferred, expr.1);
 | 
				
			||||||
 | 
					                    if let (Some(var_ref), Some(expr_ty_ref)) =
 | 
				
			||||||
 | 
					                        (var_ref.as_mut(), expr_ty_ref.as_mut())
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        state.ok(var_ref.narrow(&expr_ty_ref), var.2 + expr.1);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                StmtKind::Set(var, expr) => {
 | 
				
			||||||
 | 
					                    let var_ref = inner_hints.find_hint(&var.1);
 | 
				
			||||||
 | 
					                    dbg!(&var_ref);
 | 
				
			||||||
 | 
					                    if let Some((_, var_ref)) = &var_ref {
 | 
				
			||||||
 | 
					                        var.0 = var_ref.as_type()
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    let inferred = expr.infer_hints(&mut state, &inner_hints);
 | 
				
			||||||
 | 
					                    let expr_ty_ref = state.ok(inferred, expr.1);
 | 
				
			||||||
 | 
					                    if let (Some((_, mut var_ref)), Some(expr_ty_ref)) = (var_ref, expr_ty_ref) {
 | 
				
			||||||
 | 
					                        state.ok(var_ref.narrow(&expr_ty_ref), var.2 + expr.1);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                StmtKind::Import(_) => todo!(),
 | 
				
			||||||
 | 
					                StmtKind::Expression(expr) => {
 | 
				
			||||||
 | 
					                    let expr_res = expr.infer_hints(&mut state, &inner_hints);
 | 
				
			||||||
 | 
					                    state.ok(expr_res, expr.1);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if let Some(ret_expr) = &mut self.return_expression {
 | 
				
			||||||
 | 
					            let ret_res = ret_expr.1.infer_hints(&mut state, &inner_hints);
 | 
				
			||||||
 | 
					            state.ok(ret_res, ret_expr.1 .1);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let (kind, ty) = self.return_type().ok().unwrap_or((ReturnKind::Soft, Void));
 | 
				
			||||||
 | 
					        let mut ret_type_ref = TypeRef::from_type(&outer_hints, ty);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if kind == ReturnKind::Hard {
 | 
				
			||||||
 | 
					            if let Some(hint) = state.scope.return_type_hint {
 | 
				
			||||||
 | 
					                state.ok(
 | 
				
			||||||
 | 
					                    ret_type_ref.narrow(&mut TypeRef::from_type(outer_hints, hint)),
 | 
				
			||||||
 | 
					                    self.meta,
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        Ok((kind, ret_type_ref))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn typecheck(
 | 
					    fn typecheck(
 | 
				
			||||||
        &mut self,
 | 
					        &mut self,
 | 
				
			||||||
        state: &mut PassState<ErrorKind>,
 | 
					        state: &mut PassState<ErrorKind>,
 | 
				
			||||||
@ -230,6 +305,92 @@ impl Block {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Expression {
 | 
					impl Expression {
 | 
				
			||||||
 | 
					    fn infer_hints<'s>(
 | 
				
			||||||
 | 
					        &mut self,
 | 
				
			||||||
 | 
					        state: &mut PassState<ErrorKind>,
 | 
				
			||||||
 | 
					        hints: &'s ScopeHints<'s>,
 | 
				
			||||||
 | 
					    ) -> Result<TypeRef<'s>, ErrorKind> {
 | 
				
			||||||
 | 
					        match &mut self.0 {
 | 
				
			||||||
 | 
					            ExprKind::Variable(var) => {
 | 
				
			||||||
 | 
					                let hint = hints
 | 
				
			||||||
 | 
					                    .find_hint(&var.1)
 | 
				
			||||||
 | 
					                    .map(|(_, hint)| hint)
 | 
				
			||||||
 | 
					                    .ok_or(ErrorKind::VariableNotDefined(var.1.clone()));
 | 
				
			||||||
 | 
					                if let Ok(hint) = &hint {
 | 
				
			||||||
 | 
					                    var.0 = hint.as_type()
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                Ok(TypeRef::Hint(hint?))
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            ExprKind::Literal(literal) => TypeRef::from_literal(hints, *literal),
 | 
				
			||||||
 | 
					            ExprKind::BinOp(op, lhs, rhs) => {
 | 
				
			||||||
 | 
					                let mut lhs_ref = lhs.infer_hints(state, hints)?;
 | 
				
			||||||
 | 
					                let mut rhs_ref = rhs.infer_hints(state, hints)?;
 | 
				
			||||||
 | 
					                hints.binop(op, &mut lhs_ref, &mut rhs_ref)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            ExprKind::FunctionCall(function_call) => {
 | 
				
			||||||
 | 
					                let fn_call = state
 | 
				
			||||||
 | 
					                    .scope
 | 
				
			||||||
 | 
					                    .function_returns
 | 
				
			||||||
 | 
					                    .get(&function_call.name)
 | 
				
			||||||
 | 
					                    .ok_or(ErrorKind::FunctionNotDefined(function_call.name.clone()))?
 | 
				
			||||||
 | 
					                    .clone();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                let true_params_iter = fn_call.params.iter().chain(iter::repeat(&Vague(Unknown)));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                for (param_expr, param_t) in
 | 
				
			||||||
 | 
					                    function_call.parameters.iter_mut().zip(true_params_iter)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    let expr_res = param_expr.infer_hints(state, hints);
 | 
				
			||||||
 | 
					                    if let Some(mut param_ref) = state.ok(expr_res, param_expr.1) {
 | 
				
			||||||
 | 
					                        state.ok(
 | 
				
			||||||
 | 
					                            param_ref.narrow(&mut TypeRef::from_type(hints, *param_t)),
 | 
				
			||||||
 | 
					                            param_expr.1,
 | 
				
			||||||
 | 
					                        );
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                Ok(TypeRef::from_type(hints, fn_call.ret))
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            ExprKind::If(IfExpression(cond, lhs, rhs)) => {
 | 
				
			||||||
 | 
					                let cond_res = cond.infer_hints(state, hints);
 | 
				
			||||||
 | 
					                let cond_hints = state.ok(cond_res, cond.1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if let Some(mut cond_hints) = cond_hints {
 | 
				
			||||||
 | 
					                    state.ok(cond_hints.narrow(&mut TypeRef::Literal(Bool)), cond.1);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                let lhs_res = lhs.infer_hints(state, hints);
 | 
				
			||||||
 | 
					                let lhs_hints = state.ok(lhs_res, cond.1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if let Some(rhs) = rhs {
 | 
				
			||||||
 | 
					                    let rhs_res = rhs.infer_hints(state, hints);
 | 
				
			||||||
 | 
					                    let rhs_hints = state.ok(rhs_res, cond.1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    if let (Some(mut lhs_hints), Some(mut rhs_hints)) = (lhs_hints, rhs_hints) {
 | 
				
			||||||
 | 
					                        state.ok(lhs_hints.1.narrow(&mut rhs_hints.1), self.1);
 | 
				
			||||||
 | 
					                        Ok(pick_return(lhs_hints, rhs_hints).1)
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        // Failed to retrieve types from either
 | 
				
			||||||
 | 
					                        Ok(TypeRef::from_type(hints, Vague(Unknown)))
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    if let Some((_, type_ref)) = lhs_hints {
 | 
				
			||||||
 | 
					                        Ok(type_ref)
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        Ok(TypeRef::from_type(hints, Vague(Unknown)))
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            ExprKind::Block(block) => {
 | 
				
			||||||
 | 
					                let block_ref = block.infer_hints(state, hints)?;
 | 
				
			||||||
 | 
					                match block_ref.0 {
 | 
				
			||||||
 | 
					                    ReturnKind::Hard => Ok(TypeRef::from_type(hints, Void)),
 | 
				
			||||||
 | 
					                    ReturnKind::Soft => Ok(block_ref.1),
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn typecheck(
 | 
					    fn typecheck(
 | 
				
			||||||
        &mut self,
 | 
					        &mut self,
 | 
				
			||||||
        state: &mut PassState<ErrorKind>,
 | 
					        state: &mut PassState<ErrorKind>,
 | 
				
			||||||
@ -415,6 +576,7 @@ impl TypeKind {
 | 
				
			|||||||
            Vague(vague_type) => match vague_type {
 | 
					            Vague(vague_type) => match vague_type {
 | 
				
			||||||
                Unknown => Err(ErrorKind::TypeIsVague(*vague_type)),
 | 
					                Unknown => Err(ErrorKind::TypeIsVague(*vague_type)),
 | 
				
			||||||
                Number => Ok(TypeKind::I32),
 | 
					                Number => Ok(TypeKind::I32),
 | 
				
			||||||
 | 
					                Hinted(_) => panic!("Hinted default!"),
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            _ => Ok(*self),
 | 
					            _ => Ok(*self),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
				
			|||||||
@ -1,11 +1,14 @@
 | 
				
			|||||||
 | 
					use crate::mir::typecheck::Collapsable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use super::*;
 | 
					use super::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Debug, Clone)]
 | 
					#[derive(Debug, Clone)]
 | 
				
			||||||
pub enum ReturnTypeOther {
 | 
					pub enum ReturnTypeOther {
 | 
				
			||||||
    Import(TokenRange),
 | 
					    Import(Metadata),
 | 
				
			||||||
    Let(TokenRange),
 | 
					    Let(Metadata),
 | 
				
			||||||
    EmptyBlock(TokenRange),
 | 
					    Set(Metadata),
 | 
				
			||||||
    NoBlockReturn(TokenRange),
 | 
					    EmptyBlock(Metadata),
 | 
				
			||||||
 | 
					    NoBlockReturn(Metadata),
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub trait ReturnType {
 | 
					pub trait ReturnType {
 | 
				
			||||||
@ -14,9 +17,25 @@ pub trait ReturnType {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
impl ReturnType for Block {
 | 
					impl ReturnType for Block {
 | 
				
			||||||
    fn return_type(&self) -> Result<(ReturnKind, TypeKind), ReturnTypeOther> {
 | 
					    fn return_type(&self) -> Result<(ReturnKind, TypeKind), ReturnTypeOther> {
 | 
				
			||||||
 | 
					        let mut early_return = None;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for statement in &self.statements {
 | 
				
			||||||
 | 
					            let ret = statement.return_type();
 | 
				
			||||||
 | 
					            if let Ok((ReturnKind::Hard, _)) = ret {
 | 
				
			||||||
 | 
					                early_return = early_return.or(ret.ok());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // TODO should actually probably prune all instructions after this one
 | 
				
			||||||
 | 
					        // as to not cause problems in codegen later (when unable to delete the
 | 
				
			||||||
 | 
					        // block)
 | 
				
			||||||
 | 
					        if let Some((ReturnKind::Hard, ret_ty)) = early_return {
 | 
				
			||||||
 | 
					            return Ok((ReturnKind::Hard, ret_ty));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.return_expression
 | 
					        self.return_expression
 | 
				
			||||||
            .as_ref()
 | 
					            .as_ref()
 | 
				
			||||||
            .ok_or(ReturnTypeOther::NoBlockReturn(self.meta.range))
 | 
					            .ok_or(ReturnTypeOther::NoBlockReturn(self.meta))
 | 
				
			||||||
            .and_then(|(kind, stmt)| Ok((*kind, stmt.return_type()?.1)))
 | 
					            .and_then(|(kind, stmt)| Ok((*kind, stmt.return_type()?.1)))
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -25,10 +44,16 @@ impl ReturnType for Statement {
 | 
				
			|||||||
    fn return_type(&self) -> Result<(ReturnKind, TypeKind), ReturnTypeOther> {
 | 
					    fn return_type(&self) -> Result<(ReturnKind, TypeKind), ReturnTypeOther> {
 | 
				
			||||||
        use StmtKind::*;
 | 
					        use StmtKind::*;
 | 
				
			||||||
        match &self.0 {
 | 
					        match &self.0 {
 | 
				
			||||||
            Expression(e) => e.return_type(),
 | 
					            Let(var, _, expr) => if_hard(
 | 
				
			||||||
            Set(_, _) => todo!(),
 | 
					                expr.return_type()?,
 | 
				
			||||||
            Import(_) => Err(ReturnTypeOther::Import(self.1.range)),
 | 
					                Err(ReturnTypeOther::Let(var.2 + expr.1)),
 | 
				
			||||||
            Let(_, _, _) => Err(ReturnTypeOther::Let(self.1.range)),
 | 
					            ),
 | 
				
			||||||
 | 
					            Set(var, expr) => if_hard(
 | 
				
			||||||
 | 
					                expr.return_type()?,
 | 
				
			||||||
 | 
					                Err(ReturnTypeOther::Set(var.2 + expr.1)),
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            Import(_) => todo!(),
 | 
				
			||||||
 | 
					            Expression(expression) => expression.return_type(),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -43,12 +68,7 @@ impl ReturnType for Expression {
 | 
				
			|||||||
                let then_r = then_e.return_type()?;
 | 
					                let then_r = then_e.return_type()?;
 | 
				
			||||||
                let else_r = else_e.return_type()?;
 | 
					                let else_r = else_e.return_type()?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                let kind = if then_r.0 == ReturnKind::Hard && else_r.0 == ReturnKind::Hard {
 | 
					                Ok(pick_return(then_r, else_r))
 | 
				
			||||||
                    ReturnKind::Hard
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    ReturnKind::Soft
 | 
					 | 
				
			||||||
                };
 | 
					 | 
				
			||||||
                Ok((kind, then_r.1))
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            Block(block) => block.return_type(),
 | 
					            Block(block) => block.return_type(),
 | 
				
			||||||
            FunctionCall(fcall) => fcall.return_type(),
 | 
					            FunctionCall(fcall) => fcall.return_type(),
 | 
				
			||||||
@ -87,3 +107,24 @@ impl ReturnType for FunctionCall {
 | 
				
			|||||||
        Ok((ReturnKind::Soft, self.return_type.clone()))
 | 
					        Ok((ReturnKind::Soft, self.return_type.clone()))
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn if_hard<TErr>(
 | 
				
			||||||
 | 
					    return_type: (ReturnKind, TypeKind),
 | 
				
			||||||
 | 
					    default: Result<(ReturnKind, TypeKind), TErr>,
 | 
				
			||||||
 | 
					) -> Result<(ReturnKind, TypeKind), TErr> {
 | 
				
			||||||
 | 
					    if let (ReturnKind::Hard, _) = return_type {
 | 
				
			||||||
 | 
					        Ok(return_type)
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        default
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn pick_return<T>(lhs: (ReturnKind, T), rhs: (ReturnKind, T)) -> (ReturnKind, T) {
 | 
				
			||||||
 | 
					    use ReturnKind::*;
 | 
				
			||||||
 | 
					    match (lhs.0, rhs.0) {
 | 
				
			||||||
 | 
					        (Hard, Hard) => (Hard, lhs.1),
 | 
				
			||||||
 | 
					        (Hard, Soft) => (Soft, rhs.1),
 | 
				
			||||||
 | 
					        (Soft, Hard) => (Soft, lhs.1),
 | 
				
			||||||
 | 
					        (_, _) => (Soft, lhs.1),
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user