allow returning a second value via rdx

This commit is contained in:
2026-05-28 09:41:11 +02:00
parent fa5ff0aaa5
commit eb14607f2a
4 changed files with 73 additions and 22 deletions

View File

@@ -330,7 +330,7 @@ _builtin_environ:
Stmt::Function {
name,
params,
return_type: _,
return_types: _,
body,
exported,
} => {
@@ -387,8 +387,17 @@ _builtin_environ:
emit!(&mut self.output, " ret");
}
}
Stmt::Return { expr, keyword: _ } => {
self.compile_expr(env, expr)?;
Stmt::Return { keyword: _, exprs } => {
if exprs.len() == 2 {
self.compile_expr(env, &exprs[1])?;
emit!(&mut self.output, " push rax");
self.compile_expr(env, &exprs[0])?;
emit!(&mut self.output, " pop rdx");
} else if exprs.len() == 1 {
self.compile_expr(env, &exprs[0])?;
} else {
todo!();
}
emit!(&mut self.output, " mov rsp, rbp");
emit!(&mut self.output, " pop rbp");
emit!(&mut self.output, " ret");

View File

@@ -52,13 +52,13 @@ pub enum Stmt {
Function {
name: Token,
params: Params,
return_type: Token,
return_types: Vec<Token>,
body: Box<Stmt>,
exported: bool,
},
Return {
expr: Expr,
keyword: Token,
exprs: Vec<Expr>,
},
Break,
Continue,
@@ -213,7 +213,14 @@ impl Parser {
self.consume(TokenType::RightBracket, "expected ']' after arguments")?;
self.consume(TokenType::Colon, "expected ':' after '['")?;
let return_type = self.consume(TokenType::Identifier, "expected return type")?;
let mut return_types = vec![];
loop {
return_types.push(self.consume(TokenType::Identifier, "expected return type")?);
if !self.match_token(&[TokenType::Comma]) {
break;
}
}
self.is_inside_function = true;
let body = Box::new(self.block()?);
@@ -226,7 +233,7 @@ impl Parser {
} else {
Params::Normal(params)
},
return_type,
return_types,
body,
exported,
})
@@ -303,10 +310,14 @@ impl Parser {
self.for_statement()
} else if self.match_token(&[TokenType::KeywordReturn]) {
let keyword = self.previous().clone();
Ok(Stmt::Return {
expr: self.expression()?,
keyword,
})
let mut exprs = vec![];
loop {
exprs.push(self.expression()?);
if !self.match_token(&[TokenType::Comma]) {
break;
}
}
Ok(Stmt::Return { keyword, exprs })
} else if self.match_token(&[TokenType::KeywordBreak]) {
Ok(Stmt::Break)
} else if self.match_token(&[TokenType::KeywordContinue]) {

View File

@@ -93,18 +93,23 @@ impl SymbolTable {
Stmt::Function {
name,
params,
return_type,
return_types,
body: _,
exported: _,
} => {
if self.is_name_defined(&name.lexeme) {
return error!(name.loc, format!("tried to redefine '{}'", name.lexeme));
}
let return_type = return_types
.iter()
.map(|t| t.lexeme.clone())
.collect::<Vec<_>>()
.join(",");
match params {
Params::Normal(params) => self.functions.insert(
name.lexeme.clone(),
FnType {
return_type: return_type.lexeme.clone(),
return_type,
params: Some(
params.iter().map(|x| x.var_type.lexeme.clone()).collect(),
),
@@ -113,7 +118,7 @@ impl SymbolTable {
Params::Variadic => self.functions.insert(
name.lexeme.clone(),
FnType {
return_type: return_type.lexeme.clone(),
return_type,
params: None,
},
),

View File

@@ -95,6 +95,12 @@ impl<'a> TypeChecker<'a> {
initializer,
} => {
let mut actual_type = self.typecheck_expr(env, initializer)?;
if actual_type.contains(',') {
return error!(
&name.loc,
"cannot assign multi-return call to a single variable"
);
}
if let Some(var_type) = var_type {
if !self.is_valid_type_name(&var_type.lexeme) {
return error!(
@@ -113,6 +119,12 @@ impl<'a> TypeChecker<'a> {
}
Stmt::Assign { left, op, value } => {
let value_type = self.typecheck_expr(env, value)?;
if value_type.contains(',') {
return error!(
&op.loc,
"cannot assign multi-return call to a single variable"
);
}
match &left.kind {
ExprKind::Variable(name) => {
@@ -217,12 +229,18 @@ impl<'a> TypeChecker<'a> {
Stmt::Function {
name,
params,
return_type,
return_types,
body,
exported: _,
} => {
let return_type = return_types
.iter()
.map(|t| t.lexeme.clone())
.collect::<Vec<_>>()
.join(",");
if name.lexeme == "main" {
if return_type.lexeme != "i64" {
if return_type != "i64" {
return error!(&name.loc, "main function must return i64");
}
@@ -256,14 +274,14 @@ impl<'a> TypeChecker<'a> {
}
}
if !self.is_valid_type_name(&return_type.lexeme) {
if !self.is_valid_type_name(&return_type) {
return error!(
&return_type.loc,
"unrecognized type: ".to_owned() + &return_type.lexeme
&return_types[0].loc,
"unrecognized type: ".to_owned() + &return_type
);
}
self.current_function_return_type = return_type.lexeme.clone();
self.current_function_return_type = return_type.clone();
env.push_scope();
@@ -290,13 +308,18 @@ impl<'a> TypeChecker<'a> {
env.pop_scope();
}
Stmt::Return { expr, keyword } => {
Stmt::Return { keyword, exprs } => {
let expected = if self.current_function_return_type == "void" {
"i64".into()
} else {
self.current_function_return_type.clone()
};
expect_type!(self.typecheck_expr(env, expr)?, expected, keyword.loc);
let joined_type = exprs
.iter()
.map(|e| self.typecheck_expr(env, e))
.collect::<Result<Vec<String>, _>>()?
.join(",");
expect_type!(joined_type, expected, keyword.loc);
}
Stmt::Break => {}
Stmt::Continue => {}
@@ -528,6 +551,9 @@ impl<'a> TypeChecker<'a> {
}
fn is_valid_type_name(&self, name: &str) -> bool {
if name.contains(',') {
return name.split(',').all(|part| self.is_valid_type_name(part));
}
if BUILTIN_TYPES.contains(&name) {
return true;
}