Make early returns work even without an explicit return
This commit is contained in:
		
							parent
							
								
									1aa9b3e76c
								
							
						
					
					
						commit
						b19a32cd8a
					
				| @ -9,5 +9,5 @@ fn fibonacci(value: u16) -> u16 { | ||||
|         return 1; | ||||
|     } else { | ||||
|         return fibonacci(value - 1) + fibonacci(value - 2); | ||||
|     } | ||||
|     }; | ||||
| } | ||||
|  | ||||
| @ -93,7 +93,7 @@ impl mir::Module { | ||||
|                         if !scope.block.delete_if_unused().unwrap() { | ||||
|                             // Add a void return just in case if the block
 | ||||
|                             // 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 | ||||
|                     .return_type() | ||||
|                     .expect("No ret type in lhs?") | ||||
|                     .1 | ||||
|                     .is_known() | ||||
|                     .expect("lhs ret type is unknown"); | ||||
|                 rhs_exp | ||||
|                     .return_type() | ||||
|                     .expect("No ret type in rhs?") | ||||
|                     .1 | ||||
|                     .is_known() | ||||
|                     .expect("rhs ret type is unknown"); | ||||
| 
 | ||||
|  | ||||
| @ -177,7 +177,7 @@ pub enum CmpOperator { | ||||
|     NE, | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, Clone, Copy)] | ||||
| #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] | ||||
| pub enum ReturnKind { | ||||
|     Hard, | ||||
|     Soft, | ||||
|  | ||||
| @ -6,7 +6,10 @@ use crate::{mir::*, util::try_all}; | ||||
| use TypeKind::*; | ||||
| use VagueType::*; | ||||
| 
 | ||||
| use super::pass::{Pass, PassState, ScopeFunction}; | ||||
| use super::{ | ||||
|     pass::{Pass, PassState, ScopeFunction}, | ||||
|     types::ReturnType, | ||||
| }; | ||||
| 
 | ||||
| #[derive(thiserror::Error, Debug, Clone)] | ||||
| pub enum ErrorKind { | ||||
| @ -86,8 +89,10 @@ impl Block { | ||||
|     ) -> Result<TypeKind, ErrorKind> { | ||||
|         let mut state = state.inner(); | ||||
| 
 | ||||
|         let mut early_return = None; | ||||
| 
 | ||||
|         for statement in &mut self.statements { | ||||
|             match &mut statement.0 { | ||||
|             let ret = match &mut statement.0 { | ||||
|                 StmtKind::Let(variable_reference, expression) => { | ||||
|                     let res = expression.typecheck(&mut state, Some(variable_reference.0)); | ||||
| 
 | ||||
| @ -118,15 +123,31 @@ impl Block { | ||||
|                             variable_reference.1.clone(), | ||||
|                         ))); | ||||
|                     state.ok(res, variable_reference.2); | ||||
|                     None | ||||
|                 } | ||||
|                 StmtKind::Import(_) => todo!(), | ||||
|                 StmtKind::Expression(expression) => { | ||||
|                     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 { | ||||
|             // Use function return type as hint if return is hard.
 | ||||
|             let ret_hint_t = match return_kind { | ||||
|  | ||||
| @ -9,20 +9,20 @@ pub enum ReturnTypeOther { | ||||
| } | ||||
| 
 | ||||
| pub trait ReturnType { | ||||
|     fn return_type(&self) -> Result<TypeKind, ReturnTypeOther>; | ||||
|     fn return_type(&self) -> Result<(ReturnKind, TypeKind), ReturnTypeOther>; | ||||
| } | ||||
| 
 | ||||
| impl ReturnType for Block { | ||||
|     fn return_type(&self) -> Result<TypeKind, ReturnTypeOther> { | ||||
|     fn return_type(&self) -> Result<(ReturnKind, TypeKind), ReturnTypeOther> { | ||||
|         self.return_expression | ||||
|             .as_ref() | ||||
|             .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 { | ||||
|     fn return_type(&self) -> Result<TypeKind, ReturnTypeOther> { | ||||
|     fn return_type(&self) -> Result<(ReturnKind, TypeKind), ReturnTypeOther> { | ||||
|         use StmtKind::*; | ||||
|         match &self.0 { | ||||
|             Expression(e) => e.return_type(), | ||||
| @ -33,12 +33,21 @@ impl ReturnType for Statement { | ||||
| } | ||||
| 
 | ||||
| impl ReturnType for Expression { | ||||
|     fn return_type(&self) -> Result<TypeKind, ReturnTypeOther> { | ||||
|     fn return_type(&self) -> Result<(ReturnKind, TypeKind), ReturnTypeOther> { | ||||
|         use ExprKind::*; | ||||
|         match &self.0 { | ||||
|             Literal(lit) => Ok(lit.as_type()), | ||||
|             Literal(lit) => Ok((ReturnKind::Soft, lit.as_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(), | ||||
|             FunctionCall(fcall) => fcall.return_type(), | ||||
|             If(expr) => expr.return_type(), | ||||
| @ -47,19 +56,19 @@ impl ReturnType for Expression { | ||||
| } | ||||
| 
 | ||||
| impl ReturnType for IfExpression { | ||||
|     fn return_type(&self) -> Result<TypeKind, ReturnTypeOther> { | ||||
|     fn return_type(&self) -> Result<(ReturnKind, TypeKind), ReturnTypeOther> { | ||||
|         self.1.return_type() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl ReturnType for VariableReference { | ||||
|     fn return_type(&self) -> Result<TypeKind, ReturnTypeOther> { | ||||
|         Ok(self.0.clone()) | ||||
|     fn return_type(&self) -> Result<(ReturnKind, TypeKind), ReturnTypeOther> { | ||||
|         Ok((ReturnKind::Soft, self.0.clone())) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl ReturnType for FunctionCall { | ||||
|     fn return_type(&self) -> Result<TypeKind, ReturnTypeOther> { | ||||
|         Ok(self.return_type.clone()) | ||||
|     fn return_type(&self) -> Result<(ReturnKind, TypeKind), ReturnTypeOther> { | ||||
|         Ok((ReturnKind::Soft, self.return_type.clone())) | ||||
|     } | ||||
| } | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user