Implement mutability part 2
This commit is contained in:
parent
14e0dcbe15
commit
0f424c70d7
@ -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() {
|
||||||
|
@ -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
|
||||||
|
@ -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 {
|
||||||
|
@ -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!(),
|
||||||
|
@ -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,
|
||||||
|
Loading…
Reference in New Issue
Block a user