Implement void returns
This commit is contained in:
		
							parent
							
								
									5b59d0689b
								
							
						
					
					
						commit
						be3c415a57
					
				| @ -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; | ||||
| } | ||||
|  | ||||
| @ -89,8 +89,10 @@ impl mir::Block { | ||||
|         } | ||||
| 
 | ||||
|         if let Some((_, ret_expr)) = &self.return_expression { | ||||
|             if let Some(ret_expr) = ret_expr { | ||||
|                 allocated.extend(ret_expr.allocate(scope)); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         allocated | ||||
|     } | ||||
|  | ||||
| @ -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), | ||||
| } | ||||
|  | ||||
| @ -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)) | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|  | ||||
| @ -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) => { | ||||
|                     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 | ||||
|         }; | ||||
|  | ||||
| @ -632,14 +632,14 @@ impl FunctionDefinitionKind { | ||||
|                 } | ||||
| 
 | ||||
|                 if let Some(debug) = &scope.debug { | ||||
|                     let location = &block | ||||
|                         .return_meta() | ||||
|                         .into_debug(scope.tokens, debug.scope) | ||||
|                         .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(_) => {} | ||||
|             mir::FunctionDefinitionKind::Intrinsic(_) => {} | ||||
|         }; | ||||
| @ -664,6 +664,7 @@ impl mir::Block { | ||||
|         } | ||||
| 
 | ||||
|         if let Some((kind, expr)) = &self.return_expression { | ||||
|             if let Some(expr) = expr { | ||||
|                 let ret = expr.codegen(&mut scope, &mut state.load(true))?; | ||||
|                 match kind { | ||||
|                     mir::ReturnKind::Hard => { | ||||
| @ -676,6 +677,13 @@ impl mir::Block { | ||||
|                     } | ||||
|                     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) | ||||
|         } | ||||
|  | ||||
| @ -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")?; | ||||
|  | ||||
| @ -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,18 +420,30 @@ 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 let Some(expr) = expr { | ||||
|                     if kind == ReturnKind::Soft { | ||||
|                         expr.backing_var() | ||||
|                     } else { | ||||
|                         None | ||||
|                     } | ||||
|                 } else { | ||||
|                     None | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @ -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, | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -462,11 +462,15 @@ impl Block { | ||||
|                 ReturnKind::Hard => state.scope.return_type_hint.clone(), | ||||
|                 ReturnKind::Soft => hint_t.cloned(), | ||||
|             }; | ||||
|             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)) | ||||
|         } | ||||
|  | ||||
| @ -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
 | ||||
|  | ||||
| @ -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(); | ||||
| 
 | ||||
|         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), | ||||
|     ); | ||||
| } | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user