Make passes actually return errors well

This commit is contained in:
Sofia 2025-07-20 15:25:21 +03:00
parent e4845c4084
commit 575abe8172
9 changed files with 132 additions and 49 deletions

View File

@ -41,6 +41,9 @@ Currently missing relevant features (TODOs) are:
- Debug Information
- Borrows+Pointers
Smaller features:
- Easier way to initialize arrays with a single value
### Why "Reid"
[ᚱ is an Elder Futhark rune](https://en.wikipedia.org/wiki/Raido) which means

View File

@ -1,10 +1,21 @@
extern fn puts(message: string) -> i32;
struct DivT {
quot: i32,
rem: i32,
}
extern fn div(numerator: i32, denominator: i32) -> DivT;
pub fn print(message: string) {
puts(message);
}
pub fn intdiv(numerator: i32, denominator: i32) -> DivT {
return div(numerator, denominator);
}
fn main() -> u16 {
return 0;
}

View File

@ -121,7 +121,7 @@ pub fn perform_all_passes<'map>(
#[cfg(debug_assertions)]
println!("{}", &context);
let state = context.pass(&mut LinkerPass { module_map });
let state = context.pass(&mut LinkerPass { module_map })?;
#[cfg(debug_assertions)]
println!("{}", &context);
@ -137,7 +137,7 @@ pub fn perform_all_passes<'map>(
let refs = TypeRefs::default();
let state = context.pass(&mut TypeInference { refs: &refs });
let state = context.pass(&mut TypeInference { refs: &refs })?;
#[cfg(debug_assertions)]
dbg!(&refs);
@ -157,7 +157,7 @@ pub fn perform_all_passes<'map>(
));
}
let state = context.pass(&mut TypeCheck { refs: &refs });
let state = context.pass(&mut TypeCheck { refs: &refs })?;
#[cfg(debug_assertions)]
println!("{}", &context);
@ -189,13 +189,19 @@ pub fn compile_and_pass<'map>(
let path = path.canonicalize().unwrap();
let name = path.file_name().unwrap().to_str().unwrap().to_owned();
let (id, tokens) = parse_module(source, name, module_map).unwrap();
let (id, tokens) = parse_module(source, name, module_map)?;
let module = compile_module(id, &tokens, module_map, Some(path.clone()), true)?;
let mut mir_context = mir::Context::from(vec![module], path.parent().unwrap().to_owned());
dbg!(&mir_context);
println!("Context: {}", &mir_context);
perform_all_passes(&mut mir_context, module_map)?;
dbg!(&mir_context);
println!("Context: {}", &mir_context);
let mut context = Context::new(format!("Reid ({})", env!("CARGO_PKG_VERSION")));
let codegen_modules = mir_context.codegen(&mut context, &module_map);

View File

@ -7,10 +7,15 @@ use std::{
rc::Rc,
};
use crate::{compile_module, error_raporting::ModuleMap, lexer::FullToken, parse_module};
use crate::{
compile_module,
error_raporting::{ModuleMap, ReidError},
lexer::FullToken,
parse_module,
};
use super::{
pass::{Pass, PassState},
pass::{Pass, PassResult, PassState},
r#impl::EqualsIssue,
Context, FunctionDefinition, Import, Metadata, Module,
};
@ -41,14 +46,16 @@ pub enum ErrorKind {
FunctionIsPrivate(String, String),
}
pub fn compile_std(module_map: &mut ModuleMap) -> (super::Module, Vec<FullToken>) {
let (id, tokens) = parse_module(STD_SOURCE, "standard_library", module_map).unwrap();
let module = compile_module(id, &tokens, module_map, None, false).unwrap();
pub fn compile_std(
module_map: &mut ModuleMap,
) -> Result<(super::Module, Vec<FullToken>), ReidError> {
let (id, tokens) = parse_module(STD_SOURCE, "standard_library", module_map)?;
let module = compile_module(id, &tokens, module_map, None, false)?;
let mut mir_context = super::Context::from(vec![module], Default::default());
let std_compiled = mir_context.modules.remove(0);
(std_compiled, tokens)
Ok((std_compiled, tokens))
}
/// Struct used to implement a type-checking pass that can be performed on the
@ -62,7 +69,7 @@ type LinkerPassState<'st, 'sc> = PassState<'st, 'sc, (), ErrorKind>;
impl<'map> Pass for LinkerPass<'map> {
type Data = ();
type TError = ErrorKind;
fn context(&mut self, context: &mut Context, mut state: LinkerPassState) {
fn context(&mut self, context: &mut Context, mut state: LinkerPassState) -> PassResult {
let mains = context
.modules
.iter()
@ -70,16 +77,16 @@ impl<'map> Pass for LinkerPass<'map> {
.collect::<Vec<_>>();
if mains.len() > 1 {
state.note_errors(&vec![ErrorKind::MultipleMainsAtStart], Metadata::default());
return;
return Ok(());
}
let Some(main) = mains.first() else {
state.note_errors(&vec![ErrorKind::NoMainDefined], Metadata::default());
return;
return Ok(());
};
let Some(_) = main.functions.iter().find(|f| f.name == "main") else {
state.note_errors(&vec![ErrorKind::NoMainFunction], Metadata::default());
return;
return Ok(());
};
let mut modules = HashMap::<String, Rc<RefCell<_>>>::new();
@ -95,10 +102,10 @@ impl<'map> Pass for LinkerPass<'map> {
modules.insert(module.name.clone(), Rc::new(RefCell::new((module, tokens))));
}
// modules.insert(
// "std".to_owned(),
// Rc::new(RefCell::new(compile_std(&mut self.module_map))),
// );
modules.insert(
"std".to_owned(),
Rc::new(RefCell::new(compile_std(&mut self.module_map)?)),
);
let mut modules_to_process: Vec<Rc<RefCell<(Module, Vec<FullToken>)>>> =
modules.values().cloned().collect();
@ -243,5 +250,7 @@ impl<'map> Pass for LinkerPass<'map> {
.into_values()
.map(|v| Rc::into_inner(v).unwrap().into_inner().0)
.collect();
Ok(())
}
}

View File

@ -5,6 +5,8 @@ use std::collections::HashMap;
use std::convert::Infallible;
use std::error::Error as STDError;
use crate::error_raporting::ReidError;
use super::*;
#[derive(thiserror::Error, Debug, Clone)]
@ -205,33 +207,65 @@ impl<'st, 'sc, Data: Clone + Default, TError: STDError + Clone> PassState<'st, '
}
}
pub type PassResult = Result<(), ReidError>;
pub trait Pass {
type Data: Clone + Default;
type TError: STDError + Clone;
fn context(&mut self, _context: &mut Context, mut _state: PassState<Self::Data, Self::TError>) {
fn context(
&mut self,
_context: &mut Context,
mut _state: PassState<Self::Data, Self::TError>,
) -> PassResult {
Ok(())
}
fn module(
&mut self,
_module: &mut Module,
mut _state: PassState<Self::Data, Self::TError>,
) -> PassResult {
Ok(())
}
fn module(&mut self, _module: &mut Module, mut _state: PassState<Self::Data, Self::TError>) {}
fn function(
&mut self,
_function: &mut FunctionDefinition,
mut _state: PassState<Self::Data, Self::TError>,
) {
) -> PassResult {
Ok(())
}
fn block(
&mut self,
_block: &mut Block,
mut _state: PassState<Self::Data, Self::TError>,
) -> PassResult {
Ok(())
}
fn stmt(
&mut self,
_stmt: &mut Statement,
mut _state: PassState<Self::Data, Self::TError>,
) -> PassResult {
Ok(())
}
fn expr(
&mut self,
_expr: &mut Expression,
mut _state: PassState<Self::Data, Self::TError>,
) -> PassResult {
Ok(())
}
fn block(&mut self, _block: &mut Block, mut _state: PassState<Self::Data, Self::TError>) {}
fn stmt(&mut self, _stmt: &mut Statement, mut _state: PassState<Self::Data, Self::TError>) {}
fn expr(&mut self, _expr: &mut Expression, mut _state: PassState<Self::Data, Self::TError>) {}
}
impl Context {
pub fn pass<T: Pass>(&mut self, pass: &mut T) -> State<T::TError> {
pub fn pass<T: Pass>(&mut self, pass: &mut T) -> Result<State<T::TError>, ReidError> {
let mut state = State::new();
let mut scope = Scope::default();
pass.context(self, PassState::from(&mut state, &mut scope));
pass.context(self, PassState::from(&mut state, &mut scope))?;
for module in &mut self.modules {
module.pass(pass, &mut state, &mut scope.inner());
module.pass(pass, &mut state, &mut scope.inner())?;
}
state
Ok(state)
}
}
@ -241,7 +275,7 @@ impl Module {
pass: &mut T,
state: &mut State<T::TError>,
scope: &mut Scope<T::Data>,
) {
) -> PassResult {
for typedef in &self.typedefs {
let kind = match &typedef.kind {
TypeDefinitionKind::Struct(fields) => TypeDefinitionKind::Struct(fields.clone()),
@ -262,11 +296,12 @@ impl Module {
.ok();
}
pass.module(self, PassState::from(state, scope));
pass.module(self, PassState::from(state, scope))?;
for function in &mut self.functions {
function.pass(pass, state, &mut scope.inner());
function.pass(pass, state, &mut scope.inner())?;
}
Ok(())
}
}
@ -276,7 +311,7 @@ impl FunctionDefinition {
pass: &mut T,
state: &mut State<T::TError>,
scope: &mut Scope<T::Data>,
) {
) -> PassResult {
for param in &self.parameters {
scope
.variables
@ -290,15 +325,16 @@ impl FunctionDefinition {
.ok();
}
pass.function(self, PassState::from(state, scope));
pass.function(self, PassState::from(state, scope))?;
match &mut self.kind {
FunctionDefinitionKind::Local(block, _) => {
scope.return_type_hint = Some(self.return_type.clone());
block.pass(pass, state, scope);
block.pass(pass, state, scope)?;
}
FunctionDefinitionKind::Extern(_) => {}
};
Ok(())
}
}
@ -308,14 +344,14 @@ impl Block {
pass: &mut T,
state: &mut State<T::TError>,
scope: &mut Scope<T::Data>,
) {
) -> PassResult {
let mut scope = scope.inner();
for statement in &mut self.statements {
statement.pass(pass, state, &mut scope);
statement.pass(pass, state, &mut scope)?;
}
pass.block(self, PassState::from(state, &mut scope));
pass.block(self, PassState::from(state, &mut scope))
}
}
@ -325,21 +361,21 @@ impl Statement {
pass: &mut T,
state: &mut State<T::TError>,
scope: &mut Scope<T::Data>,
) {
) -> PassResult {
match &mut self.0 {
StmtKind::Let(_, _, expression) => {
expression.pass(pass, state, scope);
expression.pass(pass, state, scope)?;
}
StmtKind::Set(_, expression) => {
expression.pass(pass, state, scope);
expression.pass(pass, state, scope)?;
}
StmtKind::Import(_) => {} // Never exists at this stage
StmtKind::Expression(expression) => {
expression.pass(pass, state, scope);
expression.pass(pass, state, scope)?;
}
}
pass.stmt(self, PassState::from(state, scope));
pass.stmt(self, PassState::from(state, scope))?;
match &mut self.0 {
StmtKind::Let(variable_reference, mutable, _) => {
@ -358,6 +394,7 @@ impl Statement {
StmtKind::Import(_) => {} // Never exists at this stage
StmtKind::Expression(_) => {}
};
Ok(())
}
}
@ -367,7 +404,8 @@ impl Expression {
pass: &mut T,
state: &mut State<T::TError>,
scope: &mut Scope<T::Data>,
) {
pass.expr(self, PassState::from(state, scope));
) -> PassResult {
pass.expr(self, PassState::from(state, scope))?;
Ok(())
}
}

View File

@ -6,7 +6,7 @@ use crate::{mir::*, util::try_all};
use VagueType as Vague;
use super::{
pass::{Pass, PassState, ScopeFunction, ScopeVariable},
pass::{Pass, PassResult, PassState, ScopeFunction, ScopeVariable},
typerefs::TypeRefs,
};
@ -70,7 +70,7 @@ impl<'t> Pass for TypeCheck<'t> {
type Data = ();
type TError = ErrorKind;
fn module(&mut self, module: &mut Module, mut state: TypecheckPassState) {
fn module(&mut self, module: &mut Module, mut state: TypecheckPassState) -> PassResult {
let mut defmap = HashMap::new();
for typedef in &module.typedefs {
let TypeDefinition { name, kind, meta } = &typedef;
@ -107,6 +107,7 @@ impl<'t> Pass for TypeCheck<'t> {
let res = function.typecheck(&self.refs, &mut state.inner());
state.ok(res, function.block_meta());
}
Ok(())
}
}

View File

@ -9,7 +9,7 @@ use std::{convert::Infallible, iter};
use crate::{mir::TypeKind, util::try_all};
use super::{
pass::{Pass, PassState},
pass::{Pass, PassResult, PassState},
r#impl::pick_return,
typecheck::ErrorKind,
typerefs::{ScopeTypeRefs, TypeRef, TypeRefs},
@ -32,11 +32,12 @@ impl<'t> Pass for TypeInference<'t> {
type Data = ();
type TError = ErrorKind;
fn module(&mut self, module: &mut Module, mut state: TypeInferencePassState) {
fn module(&mut self, module: &mut Module, mut state: TypeInferencePassState) -> PassResult {
for function in &mut module.functions {
let res = function.infer_types(&self.refs, &mut state.inner());
state.ok(res, function.block_meta());
}
Ok(())
}
}

View File

@ -14,7 +14,9 @@ fn compiles() {
#[test]
fn passes_all_passes() {
let mut map = Default::default();
let (mut std, _) = compile_std(&mut map);
let Ok((mut std, _)) = compile_std(&mut map) else {
panic!()
};
// Needed to pass linker-pass
std.is_main = true;

12
reid_src/std_test.reid Normal file
View File

@ -0,0 +1,12 @@
import std::print;
import std::intdiv;
fn main() {
let hello = "hello world";
print(intdiv(10, 5).quot);
print(hello);
}