From be3c415a573afc7430b1e874ff7ec4f720518242 Mon Sep 17 00:00:00 2001 From: sofia Date: Thu, 24 Jul 2025 21:54:55 +0300 Subject: [PATCH] Implement void returns --- examples/hello_world.reid | 4 +- reid/src/allocator.rs | 4 +- reid/src/ast/mod.rs | 4 +- reid/src/ast/parse.rs | 6 +-- reid/src/ast/process.rs | 16 +++++-- reid/src/codegen.rs | 38 +++++++++------ reid/src/mir/fmt.rs | 10 +++- reid/src/mir/implement.rs | 22 +++++++-- reid/src/mir/mod.rs | 2 +- reid/src/mir/typecheck.rs | 14 ++++-- reid/src/mir/typeinference.rs | 6 ++- reid/tests/e2e.rs | 90 ++++++++++++++++++++++++++--------- 12 files changed, 152 insertions(+), 64 deletions(-) diff --git a/examples/hello_world.reid b/examples/hello_world.reid index 1e97fe6..c9abe1e 100644 --- a/examples/hello_world.reid +++ b/examples/hello_world.reid @@ -7,7 +7,7 @@ import std::new_string; import std::add_num_to_str; import std::concat_strings; -fn main() -> u8 { +fn main() { let mut test = from_str("hello"); concat_strings(&mut test, from_str(" world")); @@ -21,5 +21,5 @@ fn main() -> u8 { free_string(&test); - return 8; + return; } diff --git a/reid/src/allocator.rs b/reid/src/allocator.rs index 79181c7..64e7a39 100644 --- a/reid/src/allocator.rs +++ b/reid/src/allocator.rs @@ -89,7 +89,9 @@ impl mir::Block { } if let Some((_, ret_expr)) = &self.return_expression { - allocated.extend(ret_expr.allocate(scope)); + if let Some(ret_expr) = ret_expr { + allocated.extend(ret_expr.allocate(scope)); + } } allocated diff --git a/reid/src/ast/mod.rs b/reid/src/ast/mod.rs index d135995..b4531ce 100644 --- a/reid/src/ast/mod.rs +++ b/reid/src/ast/mod.rs @@ -164,7 +164,7 @@ pub struct StructExpression { #[derive(Debug, Clone)] pub struct Block( pub Vec, - pub Option<(ReturnType, Expression)>, + pub Option<(ReturnType, Option)>, pub TokenRange, ); @@ -177,7 +177,7 @@ pub enum BlockLevelStatement { _i: ImportStatement, }, Expression(Expression), - Return(ReturnType, Expression), + Return(ReturnType, Option), ForLoop(String, TokenRange, Expression, Expression, Block), WhileLoop(Expression, Block), } diff --git a/reid/src/ast/parse.rs b/reid/src/ast/parse.rs index 30cc031..af70249 100644 --- a/reid/src/ast/parse.rs +++ b/reid/src/ast/parse.rs @@ -556,7 +556,7 @@ impl Parse for Block { stream.expect(Token::BraceOpen)?; while !matches!(stream.peek(), Some(Token::BraceClose)) { - if let Some((_, e)) = return_stmt.take() { + if let Some((_, Some(e))) = return_stmt.take() { // Special list of expressions that are simply not warned about, // if semicolon is missing. if !matches!(e, Expression(ExpressionKind::IfExpr(_), _)) { @@ -697,7 +697,7 @@ impl Parse for BlockLevelStatement { }, Some(Token::ReturnKeyword) => { stream.next(); - let exp = stream.parse()?; + let exp = stream.parse().ok(); stream.expect(Token::Semi)?; Stmt::Return(ReturnType::Hard, exp) } @@ -717,7 +717,7 @@ impl Parse for BlockLevelStatement { if stream.expect(Token::Semi).is_ok() { Stmt::Expression(e) } else { - Stmt::Return(ReturnType::Soft, e) + Stmt::Return(ReturnType::Soft, Some(e)) } } } diff --git a/reid/src/ast/process.rs b/reid/src/ast/process.rs index 55134ea..36b6410 100644 --- a/reid/src/ast/process.rs +++ b/reid/src/ast/process.rs @@ -3,8 +3,8 @@ use std::path::PathBuf; use crate::{ ast::{self}, mir::{ - self, CustomTypeKey, ModuleMap, NamedVariableRef, SourceModuleId, StmtKind, StructField, - StructType, WhileStatement, + self, CustomTypeKey, ModuleMap, NamedVariableRef, ReturnKind, SourceModuleId, StmtKind, + StructField, StructType, WhileStatement, }, }; @@ -166,7 +166,11 @@ impl ast::Block { (StmtKind::Expression(e.process(module_id)), e.1) } ast::BlockLevelStatement::Return(_, e) => { - (StmtKind::Expression(e.process(module_id)), e.1) + if let Some(e) = e { + (StmtKind::Expression(e.process(module_id)), e.1) + } else { + panic!(); // Should never happen? + } } ast::BlockLevelStatement::ForLoop(counter, counter_range, start, end, block) => { let counter_var = NamedVariableRef( @@ -249,7 +253,11 @@ impl ast::Block { } let return_expression = if let Some(r) = &self.1 { - Some((r.0.into(), Box::new(r.1.process(module_id)))) + if let Some(expr) = &r.1 { + Some((r.0.into(), Some(Box::new(expr.process(module_id))))) + } else { + Some((ReturnKind::Hard, None)) + } } else { None }; diff --git a/reid/src/codegen.rs b/reid/src/codegen.rs index af85bd0..aa0bc2a 100644 --- a/reid/src/codegen.rs +++ b/reid/src/codegen.rs @@ -632,12 +632,12 @@ impl FunctionDefinitionKind { } if let Some(debug) = &scope.debug { - let location = &block - .return_meta() - .into_debug(scope.tokens, debug.scope) - .unwrap(); - let location = debug.info.location(&debug.scope, *location); - scope.block.set_terminator_location(location).unwrap(); + if let Some(location) = + &block.return_meta().into_debug(scope.tokens, debug.scope) + { + let location = debug.info.location(&debug.scope, *location); + scope.block.set_terminator_location(location).unwrap(); + } } } mir::FunctionDefinitionKind::Extern(_) => {} @@ -664,17 +664,25 @@ impl mir::Block { } if let Some((kind, expr)) = &self.return_expression { - let ret = expr.codegen(&mut scope, &mut state.load(true))?; - match kind { - mir::ReturnKind::Hard => { - if let Some(ret) = ret { - scope.block.terminate(Term::Ret(ret.instr())).unwrap(); - } else { - scope.block.terminate(Term::RetVoid).unwrap(); + if let Some(expr) = expr { + let ret = expr.codegen(&mut scope, &mut state.load(true))?; + match kind { + mir::ReturnKind::Hard => { + if let Some(ret) = ret { + scope.block.terminate(Term::Ret(ret.instr())).unwrap(); + } else { + scope.block.terminate(Term::RetVoid).unwrap(); + } + Ok(None) } - Ok(None) + mir::ReturnKind::Soft => Ok(ret), } - mir::ReturnKind::Soft => Ok(ret), + } else { + match kind { + mir::ReturnKind::Hard => scope.block.terminate(Term::RetVoid).unwrap(), + mir::ReturnKind::Soft => {} + } + Ok(None) } } else { Ok(None) diff --git a/reid/src/mir/fmt.rs b/reid/src/mir/fmt.rs index fc3dcc7..5b86600 100644 --- a/reid/src/mir/fmt.rs +++ b/reid/src/mir/fmt.rs @@ -142,9 +142,15 @@ impl Display for Block { write!(inner_f, "{}", statement)?; } if let Some(ret) = &self.return_expression { + let ret_fmt = if let Some(ret) = &ret.1 { + format!("{}", ret) + } else { + String::from("void") + }; + match ret.0 { - ReturnKind::Hard => writeln!(inner_f, "Return(Hard): {}", ret.1), - ReturnKind::Soft => writeln!(inner_f, "Return(Soft): {}", ret.1), + ReturnKind::Hard => writeln!(inner_f, "Return(Hard): {}", ret_fmt), + ReturnKind::Soft => writeln!(inner_f, "Return(Soft): {}", ret_fmt), }?; } else { writeln!(inner_f, "No Return")?; diff --git a/reid/src/mir/implement.rs b/reid/src/mir/implement.rs index a7fbe06..a80bf54 100644 --- a/reid/src/mir/implement.rs +++ b/reid/src/mir/implement.rs @@ -367,7 +367,7 @@ impl StructType { enum BlockReturn<'b> { Early(&'b Statement), - Normal(ReturnKind, &'b Expression), + Normal(ReturnKind, &'b Option>), } impl Block { @@ -394,7 +394,7 @@ impl Block { pub fn return_meta(&self) -> Metadata { self.return_expression .as_ref() - .map(|e| e.1 .1) + .map(|e| e.1.as_ref().map(|e| e.1).unwrap_or(Metadata::default())) .or(self.statements.last().map(|s| s.1)) .unwrap_or(self.meta) } @@ -420,15 +420,27 @@ impl Block { self.return_expression .as_ref() .ok_or(ReturnTypeOther::NoBlockReturn(self.meta)) - .and_then(|(kind, stmt)| Ok((*kind, stmt.return_type(refs, mod_id)?.1))) + .and_then(|(kind, stmt)| { + Ok(( + *kind, + stmt.as_ref() + .and_then(|s| s.return_type(refs, mod_id).ok()) + .map(|s| s.1) + .unwrap_or(TypeKind::Void), + )) + }) } pub fn backing_var(&self) -> Option<&NamedVariableRef> { match self.return_expr().ok()? { BlockReturn::Early(statement) => statement.backing_var(), BlockReturn::Normal(kind, expr) => { - if kind == ReturnKind::Soft { - expr.backing_var() + if let Some(expr) = expr { + if kind == ReturnKind::Soft { + expr.backing_var() + } else { + None + } } else { None } diff --git a/reid/src/mir/mod.rs b/reid/src/mir/mod.rs index b43eef9..9b2ace7 100644 --- a/reid/src/mir/mod.rs +++ b/reid/src/mir/mod.rs @@ -328,7 +328,7 @@ impl FunctionDefinition { pub struct Block { /// List of non-returning statements pub statements: Vec, - pub return_expression: Option<(ReturnKind, Box)>, + pub return_expression: Option<(ReturnKind, Option>)>, pub meta: Metadata, } diff --git a/reid/src/mir/typecheck.rs b/reid/src/mir/typecheck.rs index 0dedaf1..31c9269 100644 --- a/reid/src/mir/typecheck.rs +++ b/reid/src/mir/typecheck.rs @@ -462,11 +462,15 @@ impl Block { ReturnKind::Hard => state.scope.return_type_hint.clone(), ReturnKind::Soft => hint_t.cloned(), }; - let res = expr.typecheck(&mut state, &typerefs, ret_hint_t.as_ref()); - Ok(( - *return_kind, - state.or_else(res, TypeKind::Vague(Vague::Unknown), expr.1), - )) + if let Some(expr) = expr { + let res = expr.typecheck(&mut state, &typerefs, ret_hint_t.as_ref()); + Ok(( + *return_kind, + state.or_else(res, TypeKind::Vague(Vague::Unknown), expr.1), + )) + } else { + Ok((*return_kind, TypeKind::Void)) + } } else { Ok((ReturnKind::Soft, TypeKind::Void)) } diff --git a/reid/src/mir/typeinference.rs b/reid/src/mir/typeinference.rs index f94f368..aff4e19 100644 --- a/reid/src/mir/typeinference.rs +++ b/reid/src/mir/typeinference.rs @@ -266,8 +266,10 @@ impl Block { // If there is a return expression, infer it's type if let Some(ret_expr) = &mut self.return_expression { - let ret_res = ret_expr.1.infer_types(&mut state, &inner_refs); - state.ok(ret_res, ret_expr.1 .1); + if let Some(expr) = &mut ret_expr.1 { + let ret_res = expr.infer_types(&mut state, &inner_refs); + state.ok(ret_res, expr.1); + } } // Fetch the declared return type diff --git a/reid/tests/e2e.rs b/reid/tests/e2e.rs index 5b16a47..839adb6 100644 --- a/reid/tests/e2e.rs +++ b/reid/tests/e2e.rs @@ -17,7 +17,7 @@ use util::assert_err; mod util; -fn test(source: &str, name: &str, expected_exit_code: i32) { +fn test(source: &str, name: &str, expected_exit_code: Option) { assert_err(assert_err(std::panic::catch_unwind(|| { let mut map = Default::default(); let (id, tokens) = assert_err(parse_module(source, name, &mut map)); @@ -50,7 +50,9 @@ fn test(source: &str, name: &str, expected_exit_code: i32) { let executed = Command::new(&out_path).output(); std::fs::remove_file(out_path).unwrap(); - assert_eq!(expected_exit_code, executed.unwrap().status.code().unwrap()); + if let Some(expected_exit_code) = expected_exit_code { + assert_eq!(expected_exit_code, executed.unwrap().status.code().unwrap()); + } Ok::<(), ()>(()) }))) @@ -58,81 +60,125 @@ fn test(source: &str, name: &str, expected_exit_code: i32) { #[test] fn arithmetic_compiles_well() { - test(include_str!("../../examples/arithmetic.reid"), "test", 48); + test( + include_str!("../../examples/arithmetic.reid"), + "test", + Some(48), + ); } #[test] fn array_structs_compiles_well() { - test(include_str!("../../examples/array_structs.reid"), "test", 5); + test( + include_str!("../../examples/array_structs.reid"), + "test", + Some(5), + ); } #[test] fn array_compiles_well() { - test(include_str!("../../examples/array.reid"), "test", 3); + test(include_str!("../../examples/array.reid"), "test", Some(3)); } #[test] fn borrow_compiles_well() { - test(include_str!("../../examples/borrow.reid"), "test", 17); + test(include_str!("../../examples/borrow.reid"), "test", Some(17)); } #[test] fn borrow_hard_compiles_well() { - test(include_str!("../../examples/borrow_hard.reid"), "test", 17); + test( + include_str!("../../examples/borrow_hard.reid"), + "test", + Some(17), + ); } #[test] fn cast_compiles_well() { - test(include_str!("../../examples/cast.reid"), "test", 6); + test(include_str!("../../examples/cast.reid"), "test", Some(6)); } #[test] fn char_compiles_well() { - test(include_str!("../../examples/char.reid"), "test", 98); + test(include_str!("../../examples/char.reid"), "test", Some(98)); } #[test] fn div_mod_compiles_well() { - test(include_str!("../../examples/div_mod.reid"), "test", 12); + test( + include_str!("../../examples/div_mod.reid"), + "test", + Some(12), + ); } #[test] fn fibonacci_compiles_well() { - test(include_str!("../../examples/fibonacci.reid"), "test", 1); + test( + include_str!("../../examples/fibonacci.reid"), + "test", + Some(1), + ); } #[test] fn float_compiles_well() { - test(include_str!("../../examples/float.reid"), "test", 1); + test(include_str!("../../examples/float.reid"), "test", Some(1)); } #[test] fn hello_world_compiles_well() { - test(include_str!("../../examples/hello_world.reid"), "test", 8); + test( + include_str!("../../examples/hello_world.reid"), + "test", + None, + ); } #[test] fn mutable_compiles_well() { - test(include_str!("../../examples/mutable.reid"), "test", 21); + test( + include_str!("../../examples/mutable.reid"), + "test", + Some(21), + ); } #[test] fn ptr_compiles_well() { - test(include_str!("../../examples/ptr.reid"), "test", 5); + test(include_str!("../../examples/ptr.reid"), "test", Some(5)); } #[test] fn std_test_compiles_well() { - test(include_str!("../../examples/std_test.reid"), "test", 3); + test( + include_str!("../../examples/std_test.reid"), + "test", + Some(3), + ); } #[test] fn strings_compiles_well() { - test(include_str!("../../examples/strings.reid"), "test", 5); + test(include_str!("../../examples/strings.reid"), "test", Some(5)); } #[test] fn struct_compiles_well() { - test(include_str!("../../examples/struct.reid"), "test", 17); + test(include_str!("../../examples/struct.reid"), "test", Some(17)); } #[test] fn loops_compiles_well() { - test(include_str!("../../examples/loops.reid"), "test", 10); + test(include_str!("../../examples/loops.reid"), "test", Some(10)); } #[test] fn ptr_hard_compiles_well() { - test(include_str!("../../examples/ptr_hard.reid"), "test", 0); + test( + include_str!("../../examples/ptr_hard.reid"), + "test", + Some(0), + ); } #[test] fn loop_hard_compiles_well() { - test(include_str!("../../examples/loop_hard.reid"), "test", 0); + test( + include_str!("../../examples/loop_hard.reid"), + "test", + Some(0), + ); } #[test] fn custom_binop_compiles_well() { - test(include_str!("../../examples/custom_binop.reid"), "test", 21); + test( + include_str!("../../examples/custom_binop.reid"), + "test", + Some(21), + ); }