Implement mutability part 2
This commit is contained in:
		
							parent
							
								
									14e0dcbe15
								
							
						
					
					
						commit
						0f424c70d7
					
				@ -4,7 +4,7 @@ fn indirection() -> bool {
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn main() -> u16 {
 | 
			
		||||
fn main() -> i32 {
 | 
			
		||||
    let mut test = 5;
 | 
			
		||||
 | 
			
		||||
    if indirection() {
 | 
			
		||||
 | 
			
		||||
@ -163,8 +163,15 @@ impl mir::Statement {
 | 
			
		||||
                    name.clone(),
 | 
			
		||||
                    StackValue(
 | 
			
		||||
                        match mutable {
 | 
			
		||||
                            true => StackValueKind::Mutable(value),
 | 
			
		||||
                            false => StackValueKind::Immutable(value),
 | 
			
		||||
                            true => StackValueKind::Mutable({
 | 
			
		||||
                                let alloca = scope
 | 
			
		||||
                                    .block
 | 
			
		||||
                                    .build(Instr::Alloca(name.clone(), ty.get_type()))
 | 
			
		||||
                                    .unwrap();
 | 
			
		||||
                                scope.block.build(Instr::Store(alloca, value)).unwrap();
 | 
			
		||||
                                alloca
 | 
			
		||||
                            }),
 | 
			
		||||
                        },
 | 
			
		||||
                        ty.get_type(),
 | 
			
		||||
                    ),
 | 
			
		||||
@ -172,13 +179,15 @@ impl mir::Statement {
 | 
			
		||||
                None
 | 
			
		||||
            }
 | 
			
		||||
            mir::StmtKind::Set(var, val) => {
 | 
			
		||||
                if let Some(StackValue(kind, ty)) = scope.stack_values.get(&var.1).cloned() {
 | 
			
		||||
                if let Some(StackValue(kind, _)) = scope.stack_values.get(&var.1).cloned() {
 | 
			
		||||
                    match kind {
 | 
			
		||||
                        StackValueKind::Immutable(ptr) => {
 | 
			
		||||
                        StackValueKind::Immutable(_) => {
 | 
			
		||||
                            panic!("Tried to mutate an immutable variable")
 | 
			
		||||
                        }
 | 
			
		||||
                        StackValueKind::Mutable(ptr) => {
 | 
			
		||||
                            let expression = val.codegen(scope).unwrap();
 | 
			
		||||
                            Some(scope.block.build(Instr::Store(ptr, expression)).unwrap())
 | 
			
		||||
                        }
 | 
			
		||||
                        StackValueKind::Mutable(_) => panic!(""),
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
                    panic!("")
 | 
			
		||||
@ -251,7 +260,7 @@ impl mir::Expression {
 | 
			
		||||
    fn codegen<'ctx, 'a>(&self, scope: &mut Scope<'ctx, 'a>) -> Option<InstructionValue> {
 | 
			
		||||
        match &self.0 {
 | 
			
		||||
            mir::ExprKind::Variable(varref) => {
 | 
			
		||||
                varref.0.is_known().expect("variable type unknown");
 | 
			
		||||
                varref.0.known().expect("variable type unknown");
 | 
			
		||||
                let v = scope
 | 
			
		||||
                    .stack_values
 | 
			
		||||
                    .get(&varref.1)
 | 
			
		||||
@ -269,13 +278,13 @@ impl mir::Expression {
 | 
			
		||||
                    .return_type()
 | 
			
		||||
                    .expect("No ret type in lhs?")
 | 
			
		||||
                    .1
 | 
			
		||||
                    .is_known()
 | 
			
		||||
                    .known()
 | 
			
		||||
                    .expect("lhs ret type is unknown");
 | 
			
		||||
                rhs_exp
 | 
			
		||||
                    .return_type()
 | 
			
		||||
                    .expect("No ret type in rhs?")
 | 
			
		||||
                    .1
 | 
			
		||||
                    .is_known()
 | 
			
		||||
                    .known()
 | 
			
		||||
                    .expect("rhs ret type is unknown");
 | 
			
		||||
 | 
			
		||||
                let lhs = lhs_exp.codegen(scope).expect("lhs has no return value");
 | 
			
		||||
@ -293,7 +302,7 @@ impl mir::Expression {
 | 
			
		||||
            }
 | 
			
		||||
            mir::ExprKind::FunctionCall(call) => {
 | 
			
		||||
                call.return_type
 | 
			
		||||
                    .is_known()
 | 
			
		||||
                    .known()
 | 
			
		||||
                    .expect("function return type unknown");
 | 
			
		||||
 | 
			
		||||
                let params = call
 | 
			
		||||
 | 
			
		||||
@ -69,7 +69,7 @@ pub enum VagueType {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl TypeKind {
 | 
			
		||||
    pub fn is_known(&self) -> Result<TypeKind, VagueType> {
 | 
			
		||||
    pub fn known(&self) -> Result<TypeKind, VagueType> {
 | 
			
		||||
        if let TypeKind::Vague(vague) = self {
 | 
			
		||||
            Err(*vague)
 | 
			
		||||
        } else {
 | 
			
		||||
 | 
			
		||||
@ -107,7 +107,7 @@ impl<T: Clone + std::fmt::Debug> Storage<T> {
 | 
			
		||||
#[derive(Clone, Default, Debug)]
 | 
			
		||||
pub struct Scope {
 | 
			
		||||
    pub function_returns: Storage<ScopeFunction>,
 | 
			
		||||
    pub variables: Storage<TypeKind>,
 | 
			
		||||
    pub variables: Storage<ScopeVariable>,
 | 
			
		||||
    /// Hard Return type of this scope, if inside a function
 | 
			
		||||
    pub return_type_hint: Option<TypeKind>,
 | 
			
		||||
}
 | 
			
		||||
@ -118,6 +118,12 @@ pub struct ScopeFunction {
 | 
			
		||||
    pub params: Vec<TypeKind>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Debug)]
 | 
			
		||||
pub struct ScopeVariable {
 | 
			
		||||
    pub ty: TypeKind,
 | 
			
		||||
    pub mutable: bool,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Scope {
 | 
			
		||||
    pub fn inner(&self) -> Scope {
 | 
			
		||||
        Scope {
 | 
			
		||||
@ -223,7 +229,16 @@ impl Module {
 | 
			
		||||
impl FunctionDefinition {
 | 
			
		||||
    fn pass<T: Pass>(&mut self, pass: &mut T, state: &mut State<T::TError>, scope: &mut Scope) {
 | 
			
		||||
        for param in &self.parameters {
 | 
			
		||||
            scope.variables.set(param.0.clone(), param.1).ok();
 | 
			
		||||
            scope
 | 
			
		||||
                .variables
 | 
			
		||||
                .set(
 | 
			
		||||
                    param.0.clone(),
 | 
			
		||||
                    ScopeVariable {
 | 
			
		||||
                        ty: param.1,
 | 
			
		||||
                        mutable: false,
 | 
			
		||||
                    },
 | 
			
		||||
                )
 | 
			
		||||
                .ok();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        pass.function(self, PassState::from(state, scope));
 | 
			
		||||
@ -268,7 +283,13 @@ impl Statement {
 | 
			
		||||
        match &mut self.0 {
 | 
			
		||||
            StmtKind::Let(variable_reference, mutable, _) => scope
 | 
			
		||||
                .variables
 | 
			
		||||
                .set(variable_reference.1.clone(), variable_reference.0)
 | 
			
		||||
                .set(
 | 
			
		||||
                    variable_reference.1.clone(),
 | 
			
		||||
                    ScopeVariable {
 | 
			
		||||
                        ty: variable_reference.0,
 | 
			
		||||
                        mutable: *mutable,
 | 
			
		||||
                    },
 | 
			
		||||
                )
 | 
			
		||||
                .ok(),
 | 
			
		||||
            StmtKind::Set(variable_reference, expression) => None, // TODO
 | 
			
		||||
            StmtKind::Import(_) => todo!(),
 | 
			
		||||
 | 
			
		||||
@ -7,7 +7,7 @@ use TypeKind::*;
 | 
			
		||||
use VagueType::*;
 | 
			
		||||
 | 
			
		||||
use super::{
 | 
			
		||||
    pass::{Pass, PassState, ScopeFunction},
 | 
			
		||||
    pass::{Pass, PassState, ScopeFunction, ScopeVariable},
 | 
			
		||||
    types::ReturnType,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -33,6 +33,8 @@ pub enum ErrorKind {
 | 
			
		||||
    FunctionAlreadyDefined(String),
 | 
			
		||||
    #[error("Variable not defined: {0}")]
 | 
			
		||||
    VariableAlreadyDefined(String),
 | 
			
		||||
    #[error("Variable not mutable: {0}")]
 | 
			
		||||
    VariableNotMutable(String),
 | 
			
		||||
    #[error("Function {0} was given {1} parameters, but {2} were expected")]
 | 
			
		||||
    InvalidAmountParameters(String, usize, usize),
 | 
			
		||||
}
 | 
			
		||||
@ -59,7 +61,13 @@ impl FunctionDefinition {
 | 
			
		||||
            let res = state
 | 
			
		||||
                .scope
 | 
			
		||||
                .variables
 | 
			
		||||
                .set(param.0.clone(), param_t)
 | 
			
		||||
                .set(
 | 
			
		||||
                    param.0.clone(),
 | 
			
		||||
                    ScopeVariable {
 | 
			
		||||
                        ty: param_t,
 | 
			
		||||
                        mutable: false,
 | 
			
		||||
                    },
 | 
			
		||||
                )
 | 
			
		||||
                .or(Err(ErrorKind::VariableAlreadyDefined(param.0.clone())));
 | 
			
		||||
            state.ok(res, self.signature());
 | 
			
		||||
        }
 | 
			
		||||
@ -108,9 +116,19 @@ impl Block {
 | 
			
		||||
                        variable_reference.2 + expression.1,
 | 
			
		||||
                    );
 | 
			
		||||
 | 
			
		||||
                    // Make sure expression/variable type is NOT vague anymore
 | 
			
		||||
                    let res_t =
 | 
			
		||||
                        state.or_else(res_t.or_default(), Vague(Unknown), variable_reference.2);
 | 
			
		||||
                    let res_t = if res_t.known().is_err() {
 | 
			
		||||
                        // Unable to infer variable type even from expression! Default it
 | 
			
		||||
                        let res_t =
 | 
			
		||||
                            state.or_else(res_t.or_default(), Vague(Unknown), variable_reference.2);
 | 
			
		||||
 | 
			
		||||
                        // Re-typecheck and coerce expression to default type
 | 
			
		||||
                        let expr_res = expression.typecheck(&mut state, Some(res_t));
 | 
			
		||||
                        state.ok(expr_res, expression.1);
 | 
			
		||||
 | 
			
		||||
                        res_t
 | 
			
		||||
                    } else {
 | 
			
		||||
                        res_t
 | 
			
		||||
                    };
 | 
			
		||||
 | 
			
		||||
                    // Update typing to be more accurate
 | 
			
		||||
                    variable_reference.0 = res_t;
 | 
			
		||||
@ -119,15 +137,56 @@ impl Block {
 | 
			
		||||
                    let res = state
 | 
			
		||||
                        .scope
 | 
			
		||||
                        .variables
 | 
			
		||||
                        .set(variable_reference.1.clone(), variable_reference.0)
 | 
			
		||||
                        .set(
 | 
			
		||||
                            variable_reference.1.clone(),
 | 
			
		||||
                            ScopeVariable {
 | 
			
		||||
                                ty: variable_reference.0,
 | 
			
		||||
                                mutable: *mutable,
 | 
			
		||||
                            },
 | 
			
		||||
                        )
 | 
			
		||||
                        .or(Err(ErrorKind::VariableAlreadyDefined(
 | 
			
		||||
                            variable_reference.1.clone(),
 | 
			
		||||
                        )));
 | 
			
		||||
                    state.ok(res, variable_reference.2);
 | 
			
		||||
                    None
 | 
			
		||||
                }
 | 
			
		||||
                StmtKind::Set(variable_reference, expression) => None, // TODO
 | 
			
		||||
                StmtKind::Import(_) => todo!(),
 | 
			
		||||
                StmtKind::Set(variable_reference, expression) => {
 | 
			
		||||
                    if let Some(var) = state.scope.variables.get(&variable_reference.1).cloned() {
 | 
			
		||||
                        // Typecheck expression and coerce to variable type
 | 
			
		||||
                        let res = expression.typecheck(&mut state, Some(var.ty));
 | 
			
		||||
 | 
			
		||||
                        // If expression resolution itself was erronous, resolve as
 | 
			
		||||
                        // Unknown.
 | 
			
		||||
                        let expr_ty = state.or_else(res, Vague(Unknown), expression.1);
 | 
			
		||||
 | 
			
		||||
                        // Make sure the expression and variable type to really
 | 
			
		||||
                        // be the same
 | 
			
		||||
                        let res_t = state.or_else(
 | 
			
		||||
                            expr_ty.collapse_into(&variable_reference.0),
 | 
			
		||||
                            Vague(Unknown),
 | 
			
		||||
                            variable_reference.2 + expression.1,
 | 
			
		||||
                        );
 | 
			
		||||
 | 
			
		||||
                        // Update typing to be more accurate
 | 
			
		||||
                        variable_reference.0 = res_t;
 | 
			
		||||
 | 
			
		||||
                        if !var.mutable {
 | 
			
		||||
                            state.ok::<_, Infallible>(
 | 
			
		||||
                                Err(ErrorKind::VariableNotMutable(variable_reference.1.clone())),
 | 
			
		||||
                                variable_reference.2,
 | 
			
		||||
                            );
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        None
 | 
			
		||||
                    } else {
 | 
			
		||||
                        state.ok::<_, Infallible>(
 | 
			
		||||
                            Err(ErrorKind::VariableNotDefined(variable_reference.1.clone())),
 | 
			
		||||
                            variable_reference.2,
 | 
			
		||||
                        );
 | 
			
		||||
                        None
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                StmtKind::Import(_) => todo!(), // TODO
 | 
			
		||||
                StmtKind::Expression(expression) => {
 | 
			
		||||
                    let res = expression.typecheck(&mut state, None);
 | 
			
		||||
                    state.or_else(res, Void, expression.1);
 | 
			
		||||
@ -180,7 +239,8 @@ impl Expression {
 | 
			
		||||
                        .scope
 | 
			
		||||
                        .variables
 | 
			
		||||
                        .get(&var_ref.1)
 | 
			
		||||
                        .copied()
 | 
			
		||||
                        .map(|var| &var.ty)
 | 
			
		||||
                        .cloned()
 | 
			
		||||
                        .ok_or(ErrorKind::VariableNotDefined(var_ref.1.clone())),
 | 
			
		||||
                    Vague(Unknown),
 | 
			
		||||
                    var_ref.2,
 | 
			
		||||
@ -341,7 +401,7 @@ impl TypeKind {
 | 
			
		||||
    /// Assert that a type is already known and not vague. Return said type or
 | 
			
		||||
    /// error.
 | 
			
		||||
    fn assert_known(&self) -> Result<TypeKind, ErrorKind> {
 | 
			
		||||
        self.is_known().map_err(ErrorKind::TypeIsVague)
 | 
			
		||||
        self.known().map_err(ErrorKind::TypeIsVague)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Try to collapse a type on itself producing a default type if one exists,
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user