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::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;
}

View File

@ -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

View File

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

View File

@ -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))
}
}
}

View File

@ -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
};

View File

@ -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)

View File

@ -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")?;

View File

@ -367,7 +367,7 @@ impl StructType {
enum BlockReturn<'b> {
Early(&'b Statement),
Normal(ReturnKind, &'b Expression),
Normal(ReturnKind, &'b Option<Box<Expression>>),
}
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
}

View File

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

View File

@ -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))
}

View File

@ -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

View File

@ -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<i32>) {
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),
);
}