Implement void returns

This commit is contained in:
Sofia 2025-07-24 21:54:55 +03:00
parent 5b59d0689b
commit be3c415a57
12 changed files with 152 additions and 64 deletions

View File

@ -7,7 +7,7 @@ import std::new_string;
import std::add_num_to_str; import std::add_num_to_str;
import std::concat_strings; import std::concat_strings;
fn main() -> u8 { fn main() {
let mut test = from_str("hello"); let mut test = from_str("hello");
concat_strings(&mut test, from_str(" world")); concat_strings(&mut test, from_str(" world"));
@ -21,5 +21,5 @@ fn main() -> u8 {
free_string(&test); free_string(&test);
return 8; return;
} }

View File

@ -89,8 +89,10 @@ impl mir::Block {
} }
if let Some((_, ret_expr)) = &self.return_expression { if let Some((_, ret_expr)) = &self.return_expression {
if let Some(ret_expr) = ret_expr {
allocated.extend(ret_expr.allocate(scope)); allocated.extend(ret_expr.allocate(scope));
} }
}
allocated allocated
} }

View File

@ -164,7 +164,7 @@ pub struct StructExpression {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Block( pub struct Block(
pub Vec<BlockLevelStatement>, pub Vec<BlockLevelStatement>,
pub Option<(ReturnType, Expression)>, pub Option<(ReturnType, Option<Expression>)>,
pub TokenRange, pub TokenRange,
); );
@ -177,7 +177,7 @@ pub enum BlockLevelStatement {
_i: ImportStatement, _i: ImportStatement,
}, },
Expression(Expression), Expression(Expression),
Return(ReturnType, Expression), Return(ReturnType, Option<Expression>),
ForLoop(String, TokenRange, Expression, Expression, Block), ForLoop(String, TokenRange, Expression, Expression, Block),
WhileLoop(Expression, Block), WhileLoop(Expression, Block),
} }

View File

@ -556,7 +556,7 @@ impl Parse for Block {
stream.expect(Token::BraceOpen)?; stream.expect(Token::BraceOpen)?;
while !matches!(stream.peek(), Some(Token::BraceClose)) { 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, // Special list of expressions that are simply not warned about,
// if semicolon is missing. // if semicolon is missing.
if !matches!(e, Expression(ExpressionKind::IfExpr(_), _)) { if !matches!(e, Expression(ExpressionKind::IfExpr(_), _)) {
@ -697,7 +697,7 @@ impl Parse for BlockLevelStatement {
}, },
Some(Token::ReturnKeyword) => { Some(Token::ReturnKeyword) => {
stream.next(); stream.next();
let exp = stream.parse()?; let exp = stream.parse().ok();
stream.expect(Token::Semi)?; stream.expect(Token::Semi)?;
Stmt::Return(ReturnType::Hard, exp) Stmt::Return(ReturnType::Hard, exp)
} }
@ -717,7 +717,7 @@ impl Parse for BlockLevelStatement {
if stream.expect(Token::Semi).is_ok() { if stream.expect(Token::Semi).is_ok() {
Stmt::Expression(e) Stmt::Expression(e)
} else { } else {
Stmt::Return(ReturnType::Soft, e) Stmt::Return(ReturnType::Soft, Some(e))
} }
} }
} }

View File

@ -3,8 +3,8 @@ use std::path::PathBuf;
use crate::{ use crate::{
ast::{self}, ast::{self},
mir::{ mir::{
self, CustomTypeKey, ModuleMap, NamedVariableRef, SourceModuleId, StmtKind, StructField, self, CustomTypeKey, ModuleMap, NamedVariableRef, ReturnKind, SourceModuleId, StmtKind,
StructType, WhileStatement, StructField, StructType, WhileStatement,
}, },
}; };
@ -166,7 +166,11 @@ impl ast::Block {
(StmtKind::Expression(e.process(module_id)), e.1) (StmtKind::Expression(e.process(module_id)), e.1)
} }
ast::BlockLevelStatement::Return(_, e) => { ast::BlockLevelStatement::Return(_, e) => {
if let Some(e) = e {
(StmtKind::Expression(e.process(module_id)), e.1) (StmtKind::Expression(e.process(module_id)), e.1)
} else {
panic!(); // Should never happen?
}
} }
ast::BlockLevelStatement::ForLoop(counter, counter_range, start, end, block) => { ast::BlockLevelStatement::ForLoop(counter, counter_range, start, end, block) => {
let counter_var = NamedVariableRef( let counter_var = NamedVariableRef(
@ -249,7 +253,11 @@ impl ast::Block {
} }
let return_expression = if let Some(r) = &self.1 { 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 { } else {
None None
}; };

View File

@ -632,14 +632,14 @@ impl FunctionDefinitionKind {
} }
if let Some(debug) = &scope.debug { if let Some(debug) = &scope.debug {
let location = &block if let Some(location) =
.return_meta() &block.return_meta().into_debug(scope.tokens, debug.scope)
.into_debug(scope.tokens, debug.scope) {
.unwrap();
let location = debug.info.location(&debug.scope, *location); let location = debug.info.location(&debug.scope, *location);
scope.block.set_terminator_location(location).unwrap(); scope.block.set_terminator_location(location).unwrap();
} }
} }
}
mir::FunctionDefinitionKind::Extern(_) => {} mir::FunctionDefinitionKind::Extern(_) => {}
mir::FunctionDefinitionKind::Intrinsic(_) => {} mir::FunctionDefinitionKind::Intrinsic(_) => {}
}; };
@ -664,6 +664,7 @@ impl mir::Block {
} }
if let Some((kind, expr)) = &self.return_expression { if let Some((kind, expr)) = &self.return_expression {
if let Some(expr) = expr {
let ret = expr.codegen(&mut scope, &mut state.load(true))?; let ret = expr.codegen(&mut scope, &mut state.load(true))?;
match kind { match kind {
mir::ReturnKind::Hard => { mir::ReturnKind::Hard => {
@ -676,6 +677,13 @@ impl mir::Block {
} }
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 { } else {
Ok(None) Ok(None)
} }

View File

@ -142,9 +142,15 @@ impl Display for Block {
write!(inner_f, "{}", statement)?; write!(inner_f, "{}", statement)?;
} }
if let Some(ret) = &self.return_expression { 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 { match ret.0 {
ReturnKind::Hard => writeln!(inner_f, "Return(Hard): {}", ret.1), ReturnKind::Hard => writeln!(inner_f, "Return(Hard): {}", ret_fmt),
ReturnKind::Soft => writeln!(inner_f, "Return(Soft): {}", ret.1), ReturnKind::Soft => writeln!(inner_f, "Return(Soft): {}", ret_fmt),
}?; }?;
} else { } else {
writeln!(inner_f, "No Return")?; writeln!(inner_f, "No Return")?;

View File

@ -367,7 +367,7 @@ impl StructType {
enum BlockReturn<'b> { enum BlockReturn<'b> {
Early(&'b Statement), Early(&'b Statement),
Normal(ReturnKind, &'b Expression), Normal(ReturnKind, &'b Option<Box<Expression>>),
} }
impl Block { impl Block {
@ -394,7 +394,7 @@ impl Block {
pub fn return_meta(&self) -> Metadata { pub fn return_meta(&self) -> Metadata {
self.return_expression self.return_expression
.as_ref() .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)) .or(self.statements.last().map(|s| s.1))
.unwrap_or(self.meta) .unwrap_or(self.meta)
} }
@ -420,18 +420,30 @@ impl Block {
self.return_expression self.return_expression
.as_ref() .as_ref()
.ok_or(ReturnTypeOther::NoBlockReturn(self.meta)) .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> { pub fn backing_var(&self) -> Option<&NamedVariableRef> {
match self.return_expr().ok()? { match self.return_expr().ok()? {
BlockReturn::Early(statement) => statement.backing_var(), BlockReturn::Early(statement) => statement.backing_var(),
BlockReturn::Normal(kind, expr) => { BlockReturn::Normal(kind, expr) => {
if let Some(expr) = expr {
if kind == ReturnKind::Soft { if kind == ReturnKind::Soft {
expr.backing_var() expr.backing_var()
} else { } else {
None None
} }
} else {
None
}
} }
} }
} }

View File

@ -328,7 +328,7 @@ impl FunctionDefinition {
pub struct Block { pub struct Block {
/// List of non-returning statements /// List of non-returning statements
pub statements: Vec<Statement>, pub statements: Vec<Statement>,
pub return_expression: Option<(ReturnKind, Box<Expression>)>, pub return_expression: Option<(ReturnKind, Option<Box<Expression>>)>,
pub meta: Metadata, pub meta: Metadata,
} }

View File

@ -462,11 +462,15 @@ impl Block {
ReturnKind::Hard => state.scope.return_type_hint.clone(), ReturnKind::Hard => state.scope.return_type_hint.clone(),
ReturnKind::Soft => hint_t.cloned(), ReturnKind::Soft => hint_t.cloned(),
}; };
if let Some(expr) = expr {
let res = expr.typecheck(&mut state, &typerefs, ret_hint_t.as_ref()); let res = expr.typecheck(&mut state, &typerefs, ret_hint_t.as_ref());
Ok(( Ok((
*return_kind, *return_kind,
state.or_else(res, TypeKind::Vague(Vague::Unknown), expr.1), state.or_else(res, TypeKind::Vague(Vague::Unknown), expr.1),
)) ))
} else {
Ok((*return_kind, TypeKind::Void))
}
} else { } else {
Ok((ReturnKind::Soft, TypeKind::Void)) Ok((ReturnKind::Soft, TypeKind::Void))
} }

View File

@ -266,8 +266,10 @@ impl Block {
// If there is a return expression, infer it's type // If there is a return expression, infer it's type
if let Some(ret_expr) = &mut self.return_expression { if let Some(ret_expr) = &mut self.return_expression {
let ret_res = ret_expr.1.infer_types(&mut state, &inner_refs); if let Some(expr) = &mut ret_expr.1 {
state.ok(ret_res, ret_expr.1 .1); let ret_res = expr.infer_types(&mut state, &inner_refs);
state.ok(ret_res, expr.1);
}
} }
// Fetch the declared return type // Fetch the declared return type

View File

@ -17,7 +17,7 @@ use util::assert_err;
mod util; mod util;
fn test(source: &str, name: &str, expected_exit_code: i32) { fn test(source: &str, name: &str, expected_exit_code: Option<i32>) {
assert_err(assert_err(std::panic::catch_unwind(|| { assert_err(assert_err(std::panic::catch_unwind(|| {
let mut map = Default::default(); let mut map = Default::default();
let (id, tokens) = assert_err(parse_module(source, name, &mut map)); 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(); let executed = Command::new(&out_path).output();
std::fs::remove_file(out_path).unwrap(); std::fs::remove_file(out_path).unwrap();
if let Some(expected_exit_code) = expected_exit_code {
assert_eq!(expected_exit_code, executed.unwrap().status.code().unwrap()); assert_eq!(expected_exit_code, executed.unwrap().status.code().unwrap());
}
Ok::<(), ()>(()) Ok::<(), ()>(())
}))) })))
@ -58,81 +60,125 @@ fn test(source: &str, name: &str, expected_exit_code: i32) {
#[test] #[test]
fn arithmetic_compiles_well() { fn arithmetic_compiles_well() {
test(include_str!("../../examples/arithmetic.reid"), "test", 48); test(
include_str!("../../examples/arithmetic.reid"),
"test",
Some(48),
);
} }
#[test] #[test]
fn array_structs_compiles_well() { 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] #[test]
fn array_compiles_well() { fn array_compiles_well() {
test(include_str!("../../examples/array.reid"), "test", 3); test(include_str!("../../examples/array.reid"), "test", Some(3));
} }
#[test] #[test]
fn borrow_compiles_well() { fn borrow_compiles_well() {
test(include_str!("../../examples/borrow.reid"), "test", 17); test(include_str!("../../examples/borrow.reid"), "test", Some(17));
} }
#[test] #[test]
fn borrow_hard_compiles_well() { 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] #[test]
fn cast_compiles_well() { fn cast_compiles_well() {
test(include_str!("../../examples/cast.reid"), "test", 6); test(include_str!("../../examples/cast.reid"), "test", Some(6));
} }
#[test] #[test]
fn char_compiles_well() { fn char_compiles_well() {
test(include_str!("../../examples/char.reid"), "test", 98); test(include_str!("../../examples/char.reid"), "test", Some(98));
} }
#[test] #[test]
fn div_mod_compiles_well() { 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] #[test]
fn fibonacci_compiles_well() { fn fibonacci_compiles_well() {
test(include_str!("../../examples/fibonacci.reid"), "test", 1); test(
include_str!("../../examples/fibonacci.reid"),
"test",
Some(1),
);
} }
#[test] #[test]
fn float_compiles_well() { fn float_compiles_well() {
test(include_str!("../../examples/float.reid"), "test", 1); test(include_str!("../../examples/float.reid"), "test", Some(1));
} }
#[test] #[test]
fn hello_world_compiles_well() { fn hello_world_compiles_well() {
test(include_str!("../../examples/hello_world.reid"), "test", 8); test(
include_str!("../../examples/hello_world.reid"),
"test",
None,
);
} }
#[test] #[test]
fn mutable_compiles_well() { fn mutable_compiles_well() {
test(include_str!("../../examples/mutable.reid"), "test", 21); test(
include_str!("../../examples/mutable.reid"),
"test",
Some(21),
);
} }
#[test] #[test]
fn ptr_compiles_well() { fn ptr_compiles_well() {
test(include_str!("../../examples/ptr.reid"), "test", 5); test(include_str!("../../examples/ptr.reid"), "test", Some(5));
} }
#[test] #[test]
fn std_test_compiles_well() { 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] #[test]
fn strings_compiles_well() { fn strings_compiles_well() {
test(include_str!("../../examples/strings.reid"), "test", 5); test(include_str!("../../examples/strings.reid"), "test", Some(5));
} }
#[test] #[test]
fn struct_compiles_well() { fn struct_compiles_well() {
test(include_str!("../../examples/struct.reid"), "test", 17); test(include_str!("../../examples/struct.reid"), "test", Some(17));
} }
#[test] #[test]
fn loops_compiles_well() { fn loops_compiles_well() {
test(include_str!("../../examples/loops.reid"), "test", 10); test(include_str!("../../examples/loops.reid"), "test", Some(10));
} }
#[test] #[test]
fn ptr_hard_compiles_well() { 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] #[test]
fn loop_hard_compiles_well() { 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] #[test]
fn custom_binop_compiles_well() { fn custom_binop_compiles_well() {
test(include_str!("../../examples/custom_binop.reid"), "test", 21); test(
include_str!("../../examples/custom_binop.reid"),
"test",
Some(21),
);
} }