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;
}
fn main() -> u16 {
fn main() -> i32 {
let mut test = 5;
if indirection() {

View File

@ -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

View File

@ -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 {

View File

@ -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!(),

View File

@ -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,