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,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
|
||||
|
@ -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) => {
|
||||
(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
|
||||
};
|
||||
|
@ -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)
|
||||
|
@ -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,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
|
||||
}
|
||||
|
@ -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(),
|
||||
};
|
||||
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))
|
||||
}
|
||||
|
@ -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();
|
||||
|
||||
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),
|
||||
);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user