Separate pass-common code to pass.rs

This commit is contained in:
Sofia 2025-07-08 21:44:04 +03:00
parent 2e99ec3a80
commit 14283afe59
8 changed files with 429 additions and 192 deletions

View File

@ -9,5 +9,5 @@ fn fibonacci(value: i32) -> i32 {
if value < 3 {
return 1;
}
return fibonacci(value - 1) + fibonacci(value - 2);
return 5 + fibonacci(value - 2);
}

View File

@ -1,4 +1,4 @@
use reid::mir::*;
use reid::mir::{self, *};
use reid_lib::Context;
fn main() {
@ -152,21 +152,22 @@ fn main() {
),
};
let mir_context = mir::Context {
modules: vec![Module {
name: "test module".to_owned(),
imports: vec![],
functions: vec![fibonacci, main],
}],
};
println!("test1");
let module = Module {
name: "test module".to_owned(),
imports: vec![],
functions: vec![fibonacci, main],
};
println!("test2");
let context = Context::new();
let codegen_module = module.codegen(&context);
let codegen = mir_context.codegen(&context);
println!("test2");
codegen.compile();
println!("test3");
codegen_module.context.compile();
// match codegen_module.module.print_to_string() {
// Ok(v) => println!("{}", v),
// Err(e) => println!("Err: {:?}", e),

View File

@ -3,8 +3,16 @@ use crate::{
mir::{self, StmtKind, VariableReference},
};
impl mir::Context {
pub fn from(modules: Vec<ast::Module>) -> mir::Context {
mir::Context {
modules: modules.iter().map(|m| m.process()).collect(),
}
}
}
impl ast::Module {
pub fn process(&self) -> mir::Module {
fn process(&self) -> mir::Module {
let mut imports = Vec::new();
let mut functions = Vec::new();

View File

@ -7,13 +7,42 @@ use reid_lib::{
use crate::mir::{self, types::ReturnType, TypeKind, VariableReference};
#[derive(Debug)]
pub struct CodegenContext<'ctx> {
pub modules: Vec<ModuleCodegen<'ctx>>,
}
impl<'ctx> CodegenContext<'ctx> {
pub fn compile(&self) {
for module in &self.modules {
module.context.compile();
}
}
}
impl mir::Context {
pub fn codegen<'ctx>(&self, context: &'ctx Context) -> CodegenContext<'ctx> {
let mut modules = Vec::new();
for module in &self.modules {
modules.push(module.codegen(context));
}
CodegenContext { modules }
}
}
pub struct ModuleCodegen<'ctx> {
pub context: &'ctx Context,
pub module: Module<'ctx>,
}
impl<'ctx> std::fmt::Debug for ModuleCodegen<'ctx> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.context.fmt(f)
}
}
impl mir::Module {
pub fn codegen<'ctx>(&self, context: &'ctx Context) -> ModuleCodegen<'ctx> {
fn codegen<'ctx>(&self, context: &'ctx Context) -> ModuleCodegen<'ctx> {
let mut module = context.module(&self.name);
let mut functions = HashMap::new();

View File

@ -1,3 +1,4 @@
use mir::typecheck::TypeCheck;
use reid_lib::Context;
use crate::{ast::TopLevelStatement, lexer::Token, token_stream::TokenStream};
@ -22,7 +23,7 @@ pub enum ReidError {
#[error(transparent)]
ParserError(#[from] token_stream::Error),
#[error("Errors during typecheck: {0:?}")]
TypeCheckErrors(Vec<mir::typecheck::Error>),
TypeCheckErrors(Vec<mir::pass::Error<mir::typecheck::ErrorKind>>),
// #[error(transparent)]
// CodegenError(#[from] codegen::Error),
}
@ -48,24 +49,24 @@ pub fn compile(source: &str) -> Result<String, ReidError> {
};
dbg!(&ast_module);
let mut mir_module = ast_module.process();
let mut mir_context = mir::Context::from(vec![ast_module]);
dbg!(&mir_module);
dbg!(&mir_context);
let state = mir_module.typecheck();
dbg!(&mir_module);
let state = mir_context.pass(&mut TypeCheck {});
dbg!(&mir_context);
dbg!(&state);
if !state.errors.is_empty() {
return Err(ReidError::TypeCheckErrors(state.errors));
}
dbg!(&mir_module);
dbg!(&mir_context);
let mut context = Context::new();
let codegen_module = mir_module.codegen(&mut context);
let codegen_modules = mir_context.codegen(&mut context);
dbg!(&codegen_module.context);
codegen_module.context.compile();
dbg!(&codegen_modules);
codegen_modules.compile();
Ok(String::new())

View File

@ -3,6 +3,7 @@
/// type-checked beforehand.
use crate::token_stream::TokenRange;
pub mod pass;
pub mod typecheck;
pub mod types;
@ -215,3 +216,8 @@ pub struct Module {
pub imports: Vec<Import>,
pub functions: Vec<FunctionDefinition>,
}
#[derive(Debug)]
pub struct Context {
pub modules: Vec<Module>,
}

279
reid/src/mir/pass.rs Normal file
View File

@ -0,0 +1,279 @@
use std::collections::HashMap;
use std::error::Error as STDError;
use super::*;
#[derive(thiserror::Error, Debug, Clone)]
pub enum SimplePassError {
#[error("Function not defined: {0}")]
FunctionAlreadyDefined(String),
#[error("Variable not defined: {0}")]
VariableAlreadyDefined(String),
}
#[derive(Debug, Clone)]
pub struct Error<TErr: STDError> {
metadata: Metadata,
kind: TErr,
}
impl<TErr: STDError> std::fmt::Display for Error<TErr> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Error at {}: {}", self.metadata, self.kind)
}
}
impl<TErr: STDError> STDError for Error<TErr> {
fn source(&self) -> Option<&(dyn STDError + 'static)> {
self.kind.source()
}
}
#[derive(Clone, Debug)]
pub struct Storage<T: std::fmt::Debug>(HashMap<String, T>);
#[derive(Debug)]
pub struct State<TErr: STDError> {
pub errors: Vec<Error<TErr>>,
}
impl<TErr: STDError> State<TErr> {
fn new() -> State<TErr> {
State {
errors: Default::default(),
}
}
fn or_else<U, T: Into<Metadata> + Clone + Copy>(
&mut self,
result: Result<U, TErr>,
default: U,
meta: T,
) -> U {
match result {
Ok(t) => t,
Err(e) => {
self.errors.push(Error {
metadata: meta.into(),
kind: e,
});
default
}
}
}
fn ok<T: Into<Metadata> + Clone + Copy, U>(
&mut self,
result: Result<U, TErr>,
meta: T,
) -> Option<U> {
match result {
Ok(v) => Some(v),
Err(e) => {
self.errors.push(Error {
metadata: meta.into(),
kind: e,
});
None
}
}
}
}
impl<T: std::fmt::Debug> Default for Storage<T> {
fn default() -> Self {
Self(Default::default())
}
}
impl<T: Clone + std::fmt::Debug> Storage<T> {
pub fn set(&mut self, key: String, value: T) -> Result<T, ()> {
if let Some(_) = self.0.get(&key) {
Err(())
} else {
self.0.insert(key, value.clone());
Ok(value)
}
}
pub fn get(&self, key: &String) -> Option<&T> {
self.0.get(key)
}
}
#[derive(Clone, Default, Debug)]
pub struct Scope {
pub function_returns: Storage<ScopeFunction>,
pub variables: Storage<TypeKind>,
/// Hard Return type of this scope, if inside a function
pub return_type_hint: Option<TypeKind>,
}
#[derive(Clone, Debug)]
pub struct ScopeFunction {
pub ret: TypeKind,
pub params: Vec<TypeKind>,
}
impl Scope {
pub fn inner(&self) -> Scope {
Scope {
function_returns: self.function_returns.clone(),
variables: self.variables.clone(),
return_type_hint: self.return_type_hint,
}
}
}
pub struct PassState<'st, 'sc, TError: STDError + Clone> {
state: &'st mut State<TError>,
pub scope: &'sc mut Scope,
inner: Vec<Scope>,
}
impl<'st, 'sc, TError: STDError + Clone> PassState<'st, 'sc, TError> {
fn from(state: &'st mut State<TError>, scope: &'sc mut Scope) -> Self {
PassState {
state,
scope,
inner: Vec::new(),
}
}
pub fn or_else<U, TMeta: Into<Metadata> + Clone + Copy>(
&mut self,
result: Result<U, TError>,
default: U,
meta: TMeta,
) -> U {
self.state.or_else(result, default, meta)
}
pub fn ok<TMeta: Into<Metadata> + Clone + Copy, U>(
&mut self,
result: Result<U, TError>,
meta: TMeta,
) -> Option<U> {
self.state.ok(result, meta)
}
pub fn inner(&mut self) -> PassState<TError> {
self.inner.push(self.scope.inner());
let scope = self.inner.last_mut().unwrap();
PassState {
state: self.state,
scope,
inner: Vec::new(),
}
}
}
pub trait Pass {
type TError: STDError + Clone;
fn module(&mut self, _module: &mut Module, mut _state: PassState<Self::TError>) {}
fn function(
&mut self,
_function: &mut FunctionDefinition,
mut _state: PassState<Self::TError>,
) {
}
fn block(&mut self, _block: &mut Block, mut _state: PassState<Self::TError>) {}
fn stmt(&mut self, _stmt: &mut Statement, mut _state: PassState<Self::TError>) {}
fn expr(&mut self, _expr: &mut Expression, mut _state: PassState<Self::TError>) {}
}
impl Context {
pub fn pass<T: Pass>(&mut self, pass: &mut T) -> State<T::TError> {
let mut state = State::new();
let mut scope = Scope::default();
for module in &mut self.modules {
module.pass(pass, &mut state, &mut scope);
}
state
}
}
impl Module {
fn pass<T: Pass>(&mut self, pass: &mut T, state: &mut State<T::TError>, scope: &mut Scope) {
for function in &self.functions {
scope
.function_returns
.set(
function.name.clone(),
ScopeFunction {
ret: function.return_type,
params: function.parameters.iter().map(|v| v.1).collect(),
},
)
.ok();
}
pass.module(self, PassState::from(state, scope));
for function in &mut self.functions {
function.pass(pass, state, scope);
}
}
}
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();
}
pass.function(self, PassState::from(state, scope));
match &mut self.kind {
FunctionDefinitionKind::Local(block, _) => {
scope.return_type_hint = Some(self.return_type);
block.pass(pass, state, scope);
}
FunctionDefinitionKind::Extern => {}
};
}
}
impl Block {
fn pass<T: Pass>(&mut self, pass: &mut T, state: &mut State<T::TError>, scope: &mut Scope) {
let mut scope = scope.inner();
for statement in &mut self.statements {
statement.pass(pass, state, &mut scope);
}
pass.block(self, PassState::from(state, &mut scope));
}
}
impl Statement {
fn pass<T: Pass>(&mut self, pass: &mut T, state: &mut State<T::TError>, scope: &mut Scope) {
match &mut self.0 {
StmtKind::Let(_, expression) => {
expression.pass(pass, state, scope);
}
StmtKind::Import(_) => todo!(),
StmtKind::Expression(expression) => {
expression.pass(pass, state, scope);
}
}
pass.stmt(self, PassState::from(state, scope));
match &mut self.0 {
StmtKind::Let(variable_reference, _) => scope
.variables
.set(variable_reference.1.clone(), variable_reference.0)
.ok(),
StmtKind::Import(_) => todo!(),
StmtKind::Expression(_) => None,
};
}
}
impl Expression {
fn pass<T: Pass>(&mut self, pass: &mut T, state: &mut State<T::TError>, scope: &mut Scope) {
pass.expr(self, PassState::from(state, scope));
}
}

View File

@ -1,27 +1,11 @@
use std::{collections::HashMap, convert::Infallible, iter};
use std::{convert::Infallible, iter};
/// This module contains code relevant to doing a type checking pass on the MIR.
use crate::{mir::*, util::try_all};
use TypeKind::*;
use VagueType::*;
#[derive(Debug, Clone)]
pub struct Error {
metadata: Metadata,
kind: ErrorKind,
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Error at {}: {}", self.metadata, self.kind)
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
self.kind.source()
}
}
use super::pass::{Pass, PassState, ScopeFunction};
#[derive(thiserror::Error, Debug, Clone)]
pub enum ErrorKind {
@ -29,6 +13,8 @@ pub enum ErrorKind {
Null,
#[error("Type is vague: {0}")]
TypeIsVague(VagueType),
#[error("Can not coerce {0} to vague type {1}")]
HintIsVague(TypeKind, VagueType),
#[error("Types {0} and {1} are incompatible")]
TypesIncompatible(TypeKind, TypeKind),
#[error("Variable not defined: {0}")]
@ -37,150 +23,42 @@ pub enum ErrorKind {
FunctionNotDefined(String),
#[error("Type is vague: {0}")]
ReturnTypeMismatch(TypeKind, TypeKind),
#[error("Function not defined: {0}")]
FunctionAlreadyDefined(String),
#[error("Variable not defined: {0}")]
VariableAlreadyDefined(String),
}
#[derive(Clone)]
pub struct TypeStorage<T>(HashMap<String, T>);
pub struct TypeCheck {}
impl<T> Default for TypeStorage<T> {
fn default() -> Self {
Self(Default::default())
}
}
impl Pass for TypeCheck {
type TError = ErrorKind;
impl<T: Collapsable> TypeStorage<T> {
fn set(&mut self, key: String, value: T) -> Result<T, ErrorKind> {
if let Some(inner) = self.0.get(&key) {
match value.collapse_into(inner) {
Ok(collapsed) => {
self.0.insert(key, collapsed.clone());
Ok(collapsed)
}
Err(e) => Err(e),
}
} else {
self.0.insert(key, value.clone());
Ok(value)
}
}
fn get(&self, key: &String) -> Option<&T> {
self.0.get(key)
}
}
#[derive(Debug)]
pub struct State {
pub errors: Vec<Error>,
}
impl State {
fn new() -> State {
State {
errors: Default::default(),
}
}
fn or_else<T: Into<Metadata> + Clone + Copy>(
&mut self,
result: Result<TypeKind, ErrorKind>,
default: TypeKind,
meta: T,
) -> TypeKind {
match result {
Ok(t) => t,
Err(e) => {
self.errors.push(Error {
metadata: meta.into(),
kind: e,
});
default
}
}
}
fn ok<T: Into<Metadata> + Clone + Copy, U>(&mut self, result: Result<U, ErrorKind>, meta: T) {
if let Err(e) = result {
self.errors.push(Error {
metadata: meta.into(),
kind: e,
});
}
}
}
#[derive(Clone, Default)]
pub struct Scope {
function_returns: TypeStorage<ScopeFunction>,
variables: TypeStorage<TypeKind>,
/// Hard Return type of this scope, if inside a function
return_type_hint: Option<TypeKind>,
}
#[derive(Clone)]
pub struct ScopeFunction {
ret: TypeKind,
params: Vec<TypeKind>,
}
impl Scope {
fn inner(&self) -> Scope {
Scope {
function_returns: self.function_returns.clone(),
variables: self.variables.clone(),
return_type_hint: self.return_type_hint,
}
}
}
#[derive(Clone)]
pub enum Inferred {
Type(TypeKind),
Unresolved(u32),
}
impl Module {
pub fn typecheck(&mut self) -> State {
let mut state = State::new();
let mut scope = Scope::default();
for function in &self.functions {
state.ok(
scope.function_returns.set(
function.name.clone(),
ScopeFunction {
ret: function.return_type,
params: function.parameters.iter().map(|v| v.1).collect(),
},
),
function.signature(),
);
}
for function in &mut self.functions {
let res = function.typecheck(&mut state, &mut scope);
fn module(&mut self, module: &mut Module, mut state: PassState<ErrorKind>) {
for function in &mut module.functions {
let res = function.typecheck(&mut state);
state.ok(res, function.block_meta());
}
state
}
}
impl FunctionDefinition {
fn typecheck(&mut self, state: &mut State, scope: &mut Scope) -> Result<TypeKind, ErrorKind> {
fn typecheck(&mut self, state: &mut PassState<ErrorKind>) -> Result<TypeKind, ErrorKind> {
for param in &self.parameters {
let param_t = state.or_else(param.1.assert_known(), Vague(Unknown), self.signature());
state.ok(
scope.variables.set(param.0.clone(), param_t),
self.signature(),
);
let res = state
.scope
.variables
.set(param.0.clone(), param_t)
.or(Err(ErrorKind::VariableAlreadyDefined(param.0.clone())));
state.ok(res, self.signature());
}
let return_type = self.return_type.clone();
let inferred = match &mut self.kind {
FunctionDefinitionKind::Local(block, _) => {
scope.return_type_hint = Some(self.return_type);
block.typecheck(state, scope, Some(return_type))
state.scope.return_type_hint = Some(self.return_type);
block.typecheck(state, Some(return_type))
}
FunctionDefinitionKind::Extern => Ok(Vague(Unknown)),
};
@ -196,16 +74,15 @@ impl FunctionDefinition {
impl Block {
fn typecheck(
&mut self,
state: &mut State,
scope: &mut Scope,
state: &mut PassState<ErrorKind>,
hint_t: Option<TypeKind>,
) -> Result<TypeKind, ErrorKind> {
let mut scope = scope.inner();
let mut state = state.inner();
for statement in &mut self.statements {
match &mut statement.0 {
StmtKind::Let(variable_reference, expression) => {
let res = expression.typecheck(state, &mut scope, Some(variable_reference.0));
let res = expression.typecheck(&mut state, Some(variable_reference.0));
// If expression resolution itself was erronous, resolve as
// Unknown.
@ -220,22 +97,24 @@ impl Block {
// Make sure expression/variable type is NOT vague anymore
let res_t =
state.or_else(res_t.assert_known(), Vague(Unknown), variable_reference.2);
state.or_else(res_t.or_default(), Vague(Unknown), variable_reference.2);
// Update typing to be more accurate
variable_reference.0 = res_t;
// Variable might already be defined, note error
state.ok(
scope
.variables
.set(variable_reference.1.clone(), variable_reference.0),
variable_reference.2,
);
let res = state
.scope
.variables
.set(variable_reference.1.clone(), variable_reference.0)
.or(Err(ErrorKind::VariableAlreadyDefined(
variable_reference.1.clone(),
)));
state.ok(res, variable_reference.2);
}
StmtKind::Import(_) => todo!(),
StmtKind::Expression(expression) => {
let res = expression.typecheck(state, &mut scope, hint_t);
let res = expression.typecheck(&mut state, None);
state.ok(res, expression.1);
}
}
@ -243,10 +122,10 @@ impl Block {
if let Some((return_kind, expr)) = &mut self.return_expression {
let ret_hint_t = match return_kind {
ReturnKind::Hard => scope.return_type_hint,
ReturnKind::Hard => state.scope.return_type_hint,
ReturnKind::Soft => hint_t,
};
let res = expr.typecheck(state, &mut scope, ret_hint_t);
let res = expr.typecheck(&mut state, ret_hint_t);
Ok(state.or_else(res, Vague(Unknown), expr.1))
} else {
Ok(Void)
@ -257,14 +136,16 @@ impl Block {
impl Expression {
fn typecheck(
&mut self,
state: &mut State,
scope: &mut Scope,
state: &mut PassState<ErrorKind>,
hint_t: Option<TypeKind>,
) -> Result<TypeKind, ErrorKind> {
match &mut self.0 {
ExprKind::Variable(var_ref) => {
dbg!(&state.scope);
let existing = state.or_else(
scope
state
.scope
.variables
.get(&var_ref.1)
.copied()
@ -289,15 +170,23 @@ impl Expression {
ExprKind::BinOp(op, lhs, rhs) => {
// TODO make sure lhs and rhs can actually do this binary
// operation once relevant
let lhs_res = lhs.typecheck(state, scope, None); // TODO
let lhs_res = lhs.typecheck(state, None); // TODO
let lhs_type = state.or_else(lhs_res, Vague(Unknown), lhs.1);
let rhs_res = rhs.typecheck(state, scope, Some(lhs_type)); // TODO
let rhs_res = rhs.typecheck(state, Some(lhs_type)); // TODO
let rhs_type = state.or_else(rhs_res, Vague(Unknown), rhs.1);
if let Some(collapsed) = state.ok(rhs_type.collapse_into(&rhs_type), self.1) {
// Try to coerce both sides again with collapsed type
lhs.typecheck(state, Some(collapsed)).ok();
rhs.typecheck(state, Some(collapsed)).ok();
}
let res = lhs_type.binop_type(&op, &rhs_type)?;
Ok(res)
}
ExprKind::FunctionCall(function_call) => {
let true_function = scope
let true_function = state
.scope
.function_returns
.get(&function_call.name)
.cloned()
@ -313,7 +202,7 @@ impl Expression {
for (param, true_param_t) in
function_call.parameters.iter_mut().zip(true_params_iter)
{
let param_res = param.typecheck(state, scope, Some(true_param_t));
let param_res = param.typecheck(state, Some(true_param_t));
let param_t = state.or_else(param_res, Vague(Unknown), param.1);
state.ok(param_t.collapse_into(&true_param_t), param.1);
}
@ -330,21 +219,21 @@ impl Expression {
}
ExprKind::If(IfExpression(cond, lhs, rhs)) => {
// TODO make sure cond_res is Boolean here
let cond_res = cond.typecheck(state, scope, Some(Bool));
let cond_res = cond.typecheck(state, Some(Bool));
let cond_t = state.or_else(cond_res, Vague(Unknown), cond.1);
state.ok(cond_t.collapse_into(&Bool), cond.1);
let lhs_res = lhs.typecheck(state, scope, hint_t);
let lhs_res = lhs.typecheck(state, hint_t);
let lhs_type = state.or_else(lhs_res, Vague(Unknown), lhs.meta);
let rhs_type = if let Some(rhs) = rhs {
let res = rhs.typecheck(state, scope, hint_t);
let res = rhs.typecheck(state, hint_t);
state.or_else(res, Vague(Unknown), rhs.meta)
} else {
Vague(Unknown)
};
lhs_type.collapse_into(&rhs_type)
}
ExprKind::Block(block) => block.typecheck(state, scope, hint_t),
ExprKind::Block(block) => block.typecheck(state, hint_t),
}
}
}
@ -359,6 +248,9 @@ impl Literal {
(L::I16(_), I16) => self,
(L::Vague(VagueL::Number(v)), I32) => L::I32(v as i32),
(L::Vague(VagueL::Number(v)), I16) => L::I16(v as i16),
// Default type for number literal if unable to find true type.
(L::Vague(VagueL::Number(v)), Vague(Number)) => L::I32(v as i32),
(_, Vague(_)) => self,
_ => Err(ErrorKind::TypesIncompatible(self.as_type(), hint))?,
})
} else {
@ -368,10 +260,24 @@ impl Literal {
}
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)
}
/// Try to collapse a type on itself producing a default type if one exists,
/// Error if not.
fn or_default(&self) -> Result<TypeKind, ErrorKind> {
match self {
Vague(vague_type) => match vague_type {
Unknown => Err(ErrorKind::TypeIsVague(*vague_type)),
Number => Ok(TypeKind::I32),
},
_ => Ok(*self),
}
}
fn binop_type(&self, op: &BinaryOperator, other: &TypeKind) -> Result<TypeKind, ErrorKind> {
let res = self.collapse_into(other)?;
Ok(match op {
@ -401,6 +307,13 @@ impl Collapsable for TypeKind {
}
match (self, other) {
(Vague(Number), other) | (other, Vague(Number)) => match other {
Vague(Unknown) => Ok(Vague(Number)),
Vague(Number) => Ok(Vague(Number)),
I32 => Ok(I32),
I16 => Ok(I16),
_ => Err(ErrorKind::TypesIncompatible(*self, *other)),
},
(Vague(Unknown), other) | (other, Vague(Unknown)) => Ok(other.clone()),
_ => Err(ErrorKind::TypesIncompatible(*self, *other)),
}