Make early returns work even without an explicit return

This commit is contained in:
Sofia 2025-07-09 22:20:08 +03:00
parent 1aa9b3e76c
commit b19a32cd8a
5 changed files with 50 additions and 18 deletions

View File

@ -9,5 +9,5 @@ fn fibonacci(value: u16) -> u16 {
return 1; return 1;
} else { } else {
return fibonacci(value - 1) + fibonacci(value - 2); return fibonacci(value - 1) + fibonacci(value - 2);
} };
} }

View File

@ -93,7 +93,7 @@ impl mir::Module {
if !scope.block.delete_if_unused().unwrap() { if !scope.block.delete_if_unused().unwrap() {
// Add a void return just in case if the block // Add a void return just in case if the block
// wasn't unused but didn't have a terminator yet // wasn't unused but didn't have a terminator yet
scope.block.terminate(Term::RetVoid).unwrap(); scope.block.terminate(Term::RetVoid).ok();
} }
} }
} }
@ -225,11 +225,13 @@ impl mir::Expression {
lhs_exp lhs_exp
.return_type() .return_type()
.expect("No ret type in lhs?") .expect("No ret type in lhs?")
.1
.is_known() .is_known()
.expect("lhs ret type is unknown"); .expect("lhs ret type is unknown");
rhs_exp rhs_exp
.return_type() .return_type()
.expect("No ret type in rhs?") .expect("No ret type in rhs?")
.1
.is_known() .is_known()
.expect("rhs ret type is unknown"); .expect("rhs ret type is unknown");

View File

@ -177,7 +177,7 @@ pub enum CmpOperator {
NE, NE,
} }
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum ReturnKind { pub enum ReturnKind {
Hard, Hard,
Soft, Soft,

View File

@ -6,7 +6,10 @@ use crate::{mir::*, util::try_all};
use TypeKind::*; use TypeKind::*;
use VagueType::*; use VagueType::*;
use super::pass::{Pass, PassState, ScopeFunction}; use super::{
pass::{Pass, PassState, ScopeFunction},
types::ReturnType,
};
#[derive(thiserror::Error, Debug, Clone)] #[derive(thiserror::Error, Debug, Clone)]
pub enum ErrorKind { pub enum ErrorKind {
@ -86,8 +89,10 @@ impl Block {
) -> Result<TypeKind, ErrorKind> { ) -> Result<TypeKind, ErrorKind> {
let mut state = state.inner(); let mut state = state.inner();
let mut early_return = None;
for statement in &mut self.statements { for statement in &mut self.statements {
match &mut statement.0 { let ret = match &mut statement.0 {
StmtKind::Let(variable_reference, expression) => { StmtKind::Let(variable_reference, expression) => {
let res = expression.typecheck(&mut state, Some(variable_reference.0)); let res = expression.typecheck(&mut state, Some(variable_reference.0));
@ -118,15 +123,31 @@ impl Block {
variable_reference.1.clone(), variable_reference.1.clone(),
))); )));
state.ok(res, variable_reference.2); state.ok(res, variable_reference.2);
None
} }
StmtKind::Import(_) => todo!(), StmtKind::Import(_) => todo!(),
StmtKind::Expression(expression) => { StmtKind::Expression(expression) => {
let res = expression.typecheck(&mut state, None); let res = expression.typecheck(&mut state, None);
state.ok(res, expression.1); let res_t = state.or_else(res, Void, expression.1);
if let Ok((kind, _)) = expression.return_type() {
Some((kind, expression))
} else {
None
}
} }
};
if let Some((ReturnKind::Hard, _)) = ret {
early_return = early_return.or(ret);
} }
} }
if let Some((ReturnKind::Hard, expr)) = early_return {
let hint = state.scope.return_type_hint;
let res = expr.typecheck(&mut state, hint);
return Ok(state.or_else(res, Vague(Unknown), expr.1));
}
if let Some((return_kind, expr)) = &mut self.return_expression { if let Some((return_kind, expr)) = &mut self.return_expression {
// Use function return type as hint if return is hard. // Use function return type as hint if return is hard.
let ret_hint_t = match return_kind { let ret_hint_t = match return_kind {

View File

@ -9,20 +9,20 @@ pub enum ReturnTypeOther {
} }
pub trait ReturnType { pub trait ReturnType {
fn return_type(&self) -> Result<TypeKind, ReturnTypeOther>; fn return_type(&self) -> Result<(ReturnKind, TypeKind), ReturnTypeOther>;
} }
impl ReturnType for Block { impl ReturnType for Block {
fn return_type(&self) -> Result<TypeKind, ReturnTypeOther> { fn return_type(&self) -> Result<(ReturnKind, TypeKind), ReturnTypeOther> {
self.return_expression self.return_expression
.as_ref() .as_ref()
.ok_or(ReturnTypeOther::NoBlockReturn(self.meta.range)) .ok_or(ReturnTypeOther::NoBlockReturn(self.meta.range))
.and_then(|(_, stmt)| stmt.return_type()) .and_then(|(kind, stmt)| Ok((*kind, stmt.return_type()?.1)))
} }
} }
impl ReturnType for Statement { impl ReturnType for Statement {
fn return_type(&self) -> Result<TypeKind, ReturnTypeOther> { fn return_type(&self) -> Result<(ReturnKind, TypeKind), ReturnTypeOther> {
use StmtKind::*; use StmtKind::*;
match &self.0 { match &self.0 {
Expression(e) => e.return_type(), Expression(e) => e.return_type(),
@ -33,12 +33,21 @@ impl ReturnType for Statement {
} }
impl ReturnType for Expression { impl ReturnType for Expression {
fn return_type(&self) -> Result<TypeKind, ReturnTypeOther> { fn return_type(&self) -> Result<(ReturnKind, TypeKind), ReturnTypeOther> {
use ExprKind::*; use ExprKind::*;
match &self.0 { match &self.0 {
Literal(lit) => Ok(lit.as_type()), Literal(lit) => Ok((ReturnKind::Soft, lit.as_type())),
Variable(var) => var.return_type(), Variable(var) => var.return_type(),
BinOp(_, expr, _) => expr.return_type(), BinOp(_, then_e, else_e) => {
let then_r = then_e.return_type()?;
let else_e = else_e.return_type()?;
let kind = if then_r.0 == ReturnKind::Hard && else_e.0 == ReturnKind::Hard {
ReturnKind::Hard
} else {
ReturnKind::Hard
};
Ok((kind, then_r.1))
}
Block(block) => block.return_type(), Block(block) => block.return_type(),
FunctionCall(fcall) => fcall.return_type(), FunctionCall(fcall) => fcall.return_type(),
If(expr) => expr.return_type(), If(expr) => expr.return_type(),
@ -47,19 +56,19 @@ impl ReturnType for Expression {
} }
impl ReturnType for IfExpression { impl ReturnType for IfExpression {
fn return_type(&self) -> Result<TypeKind, ReturnTypeOther> { fn return_type(&self) -> Result<(ReturnKind, TypeKind), ReturnTypeOther> {
self.1.return_type() self.1.return_type()
} }
} }
impl ReturnType for VariableReference { impl ReturnType for VariableReference {
fn return_type(&self) -> Result<TypeKind, ReturnTypeOther> { fn return_type(&self) -> Result<(ReturnKind, TypeKind), ReturnTypeOther> {
Ok(self.0.clone()) Ok((ReturnKind::Soft, self.0.clone()))
} }
} }
impl ReturnType for FunctionCall { impl ReturnType for FunctionCall {
fn return_type(&self) -> Result<TypeKind, ReturnTypeOther> { fn return_type(&self) -> Result<(ReturnKind, TypeKind), ReturnTypeOther> {
Ok(self.return_type.clone()) Ok((ReturnKind::Soft, self.return_type.clone()))
} }
} }