Implement mutability part 2

This commit is contained in:
Sofia 2025-07-12 14:37:10 +03:00
parent 14e0dcbe15
commit 0f424c70d7
5 changed files with 113 additions and 23 deletions

View File

@ -4,7 +4,7 @@ fn indirection() -> bool {
return true; return true;
} }
fn main() -> u16 { fn main() -> i32 {
let mut test = 5; let mut test = 5;
if indirection() { if indirection() {

View File

@ -163,8 +163,15 @@ impl mir::Statement {
name.clone(), name.clone(),
StackValue( StackValue(
match mutable { match mutable {
true => StackValueKind::Mutable(value),
false => StackValueKind::Immutable(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(), ty.get_type(),
), ),
@ -172,13 +179,15 @@ impl mir::Statement {
None None
} }
mir::StmtKind::Set(var, val) => { 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 { match kind {
StackValueKind::Immutable(ptr) => { StackValueKind::Immutable(_) => {
panic!("Tried to mutate an immutable variable")
}
StackValueKind::Mutable(ptr) => {
let expression = val.codegen(scope).unwrap(); let expression = val.codegen(scope).unwrap();
Some(scope.block.build(Instr::Store(ptr, expression)).unwrap()) Some(scope.block.build(Instr::Store(ptr, expression)).unwrap())
} }
StackValueKind::Mutable(_) => panic!(""),
} }
} else { } else {
panic!("") panic!("")
@ -251,7 +260,7 @@ impl mir::Expression {
fn codegen<'ctx, 'a>(&self, scope: &mut Scope<'ctx, 'a>) -> Option<InstructionValue> { fn codegen<'ctx, 'a>(&self, scope: &mut Scope<'ctx, 'a>) -> Option<InstructionValue> {
match &self.0 { match &self.0 {
mir::ExprKind::Variable(varref) => { mir::ExprKind::Variable(varref) => {
varref.0.is_known().expect("variable type unknown"); varref.0.known().expect("variable type unknown");
let v = scope let v = scope
.stack_values .stack_values
.get(&varref.1) .get(&varref.1)
@ -269,13 +278,13 @@ impl mir::Expression {
.return_type() .return_type()
.expect("No ret type in lhs?") .expect("No ret type in lhs?")
.1 .1
.is_known() .known()
.expect("lhs ret type is unknown"); .expect("lhs ret type is unknown");
rhs_exp rhs_exp
.return_type() .return_type()
.expect("No ret type in rhs?") .expect("No ret type in rhs?")
.1 .1
.is_known() .known()
.expect("rhs ret type is unknown"); .expect("rhs ret type is unknown");
let lhs = lhs_exp.codegen(scope).expect("lhs has no return value"); let lhs = lhs_exp.codegen(scope).expect("lhs has no return value");
@ -293,7 +302,7 @@ impl mir::Expression {
} }
mir::ExprKind::FunctionCall(call) => { mir::ExprKind::FunctionCall(call) => {
call.return_type call.return_type
.is_known() .known()
.expect("function return type unknown"); .expect("function return type unknown");
let params = call let params = call

View File

@ -69,7 +69,7 @@ pub enum VagueType {
} }
impl TypeKind { impl TypeKind {
pub fn is_known(&self) -> Result<TypeKind, VagueType> { pub fn known(&self) -> Result<TypeKind, VagueType> {
if let TypeKind::Vague(vague) = self { if let TypeKind::Vague(vague) = self {
Err(*vague) Err(*vague)
} else { } else {

View File

@ -107,7 +107,7 @@ impl<T: Clone + std::fmt::Debug> Storage<T> {
#[derive(Clone, Default, Debug)] #[derive(Clone, Default, Debug)]
pub struct Scope { pub struct Scope {
pub function_returns: Storage<ScopeFunction>, pub function_returns: Storage<ScopeFunction>,
pub variables: Storage<TypeKind>, pub variables: Storage<ScopeVariable>,
/// Hard Return type of this scope, if inside a function /// Hard Return type of this scope, if inside a function
pub return_type_hint: Option<TypeKind>, pub return_type_hint: Option<TypeKind>,
} }
@ -118,6 +118,12 @@ pub struct ScopeFunction {
pub params: Vec<TypeKind>, pub params: Vec<TypeKind>,
} }
#[derive(Clone, Debug)]
pub struct ScopeVariable {
pub ty: TypeKind,
pub mutable: bool,
}
impl Scope { impl Scope {
pub fn inner(&self) -> Scope { pub fn inner(&self) -> Scope {
Scope { Scope {
@ -223,7 +229,16 @@ impl Module {
impl FunctionDefinition { impl FunctionDefinition {
fn pass<T: Pass>(&mut self, pass: &mut T, state: &mut State<T::TError>, scope: &mut Scope) { fn pass<T: Pass>(&mut self, pass: &mut T, state: &mut State<T::TError>, scope: &mut Scope) {
for param in &self.parameters { 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)); pass.function(self, PassState::from(state, scope));
@ -268,7 +283,13 @@ impl Statement {
match &mut self.0 { match &mut self.0 {
StmtKind::Let(variable_reference, mutable, _) => scope StmtKind::Let(variable_reference, mutable, _) => scope
.variables .variables
.set(variable_reference.1.clone(), variable_reference.0) .set(
variable_reference.1.clone(),
ScopeVariable {
ty: variable_reference.0,
mutable: *mutable,
},
)
.ok(), .ok(),
StmtKind::Set(variable_reference, expression) => None, // TODO StmtKind::Set(variable_reference, expression) => None, // TODO
StmtKind::Import(_) => todo!(), StmtKind::Import(_) => todo!(),

View File

@ -7,7 +7,7 @@ use TypeKind::*;
use VagueType::*; use VagueType::*;
use super::{ use super::{
pass::{Pass, PassState, ScopeFunction}, pass::{Pass, PassState, ScopeFunction, ScopeVariable},
types::ReturnType, types::ReturnType,
}; };
@ -33,6 +33,8 @@ pub enum ErrorKind {
FunctionAlreadyDefined(String), FunctionAlreadyDefined(String),
#[error("Variable not defined: {0}")] #[error("Variable not defined: {0}")]
VariableAlreadyDefined(String), VariableAlreadyDefined(String),
#[error("Variable not mutable: {0}")]
VariableNotMutable(String),
#[error("Function {0} was given {1} parameters, but {2} were expected")] #[error("Function {0} was given {1} parameters, but {2} were expected")]
InvalidAmountParameters(String, usize, usize), InvalidAmountParameters(String, usize, usize),
} }
@ -59,7 +61,13 @@ impl FunctionDefinition {
let res = state let res = state
.scope .scope
.variables .variables
.set(param.0.clone(), param_t) .set(
param.0.clone(),
ScopeVariable {
ty: param_t,
mutable: false,
},
)
.or(Err(ErrorKind::VariableAlreadyDefined(param.0.clone()))); .or(Err(ErrorKind::VariableAlreadyDefined(param.0.clone())));
state.ok(res, self.signature()); state.ok(res, self.signature());
} }
@ -108,10 +116,20 @@ impl Block {
variable_reference.2 + expression.1, variable_reference.2 + expression.1,
); );
// Make sure expression/variable type is NOT vague anymore let res_t = if res_t.known().is_err() {
// Unable to infer variable type even from expression! Default it
let res_t = let res_t =
state.or_else(res_t.or_default(), Vague(Unknown), variable_reference.2); 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 // Update typing to be more accurate
variable_reference.0 = res_t; variable_reference.0 = res_t;
@ -119,15 +137,56 @@ impl Block {
let res = state let res = state
.scope .scope
.variables .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( .or(Err(ErrorKind::VariableAlreadyDefined(
variable_reference.1.clone(), variable_reference.1.clone(),
))); )));
state.ok(res, variable_reference.2); state.ok(res, variable_reference.2);
None None
} }
StmtKind::Set(variable_reference, expression) => None, // TODO StmtKind::Set(variable_reference, expression) => {
StmtKind::Import(_) => todo!(), 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) => { StmtKind::Expression(expression) => {
let res = expression.typecheck(&mut state, None); let res = expression.typecheck(&mut state, None);
state.or_else(res, Void, expression.1); state.or_else(res, Void, expression.1);
@ -180,7 +239,8 @@ impl Expression {
.scope .scope
.variables .variables
.get(&var_ref.1) .get(&var_ref.1)
.copied() .map(|var| &var.ty)
.cloned()
.ok_or(ErrorKind::VariableNotDefined(var_ref.1.clone())), .ok_or(ErrorKind::VariableNotDefined(var_ref.1.clone())),
Vague(Unknown), Vague(Unknown),
var_ref.2, var_ref.2,
@ -341,7 +401,7 @@ impl TypeKind {
/// Assert that a type is already known and not vague. Return said type or /// Assert that a type is already known and not vague. Return said type or
/// error. /// error.
fn assert_known(&self) -> Result<TypeKind, ErrorKind> { 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, /// Try to collapse a type on itself producing a default type if one exists,