From 0f424c70d76b23a065790dc1ed05f009fa985d79 Mon Sep 17 00:00:00 2001 From: sofia Date: Sat, 12 Jul 2025 14:37:10 +0300 Subject: [PATCH] Implement mutability part 2 --- reid/examples/reid/mutable.reid | 2 +- reid/src/codegen.rs | 25 +++++++---- reid/src/mir/mod.rs | 2 +- reid/src/mir/pass.rs | 27 +++++++++-- reid/src/mir/typecheck.rs | 80 ++++++++++++++++++++++++++++----- 5 files changed, 113 insertions(+), 23 deletions(-) diff --git a/reid/examples/reid/mutable.reid b/reid/examples/reid/mutable.reid index 4d75cd4..cdd4228 100644 --- a/reid/examples/reid/mutable.reid +++ b/reid/examples/reid/mutable.reid @@ -4,7 +4,7 @@ fn indirection() -> bool { return true; } -fn main() -> u16 { +fn main() -> i32 { let mut test = 5; if indirection() { diff --git a/reid/src/codegen.rs b/reid/src/codegen.rs index 6eedc50..a26e748 100644 --- a/reid/src/codegen.rs +++ b/reid/src/codegen.rs @@ -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 { 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 diff --git a/reid/src/mir/mod.rs b/reid/src/mir/mod.rs index 82d96d8..aa2cdcb 100644 --- a/reid/src/mir/mod.rs +++ b/reid/src/mir/mod.rs @@ -69,7 +69,7 @@ pub enum VagueType { } impl TypeKind { - pub fn is_known(&self) -> Result { + pub fn known(&self) -> Result { if let TypeKind::Vague(vague) = self { Err(*vague) } else { diff --git a/reid/src/mir/pass.rs b/reid/src/mir/pass.rs index 90d234d..d89810b 100644 --- a/reid/src/mir/pass.rs +++ b/reid/src/mir/pass.rs @@ -107,7 +107,7 @@ impl Storage { #[derive(Clone, Default, Debug)] pub struct Scope { pub function_returns: Storage, - pub variables: Storage, + pub variables: Storage, /// Hard Return type of this scope, if inside a function pub return_type_hint: Option, } @@ -118,6 +118,12 @@ pub struct ScopeFunction { pub params: Vec, } +#[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(&mut self, pass: &mut T, state: &mut State, 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!(), diff --git a/reid/src/mir/typecheck.rs b/reid/src/mir/typecheck.rs index 5821d83..dab0cba 100644 --- a/reid/src/mir/typecheck.rs +++ b/reid/src/mir/typecheck.rs @@ -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 { - 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,