diff --git a/src/codegen_x86_64.rs b/src/codegen_x86_64.rs index 9fd690a..0e6d2ad 100644 --- a/src/codegen_x86_64.rs +++ b/src/codegen_x86_64.rs @@ -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"); diff --git a/src/parser.rs b/src/parser.rs index 1bb82fb..d6c09d1 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -52,13 +52,13 @@ pub enum Stmt { Function { name: Token, params: Params, - return_type: Token, + return_types: Vec, body: Box, exported: bool, }, Return { - expr: Expr, keyword: Token, + exprs: Vec, }, 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]) { diff --git a/src/symbol_table.rs b/src/symbol_table.rs index 2f1abd0..7f37fe0 100644 --- a/src/symbol_table.rs +++ b/src/symbol_table.rs @@ -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::>() + .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, }, ), diff --git a/src/typechecker.rs b/src/typechecker.rs index 3fa34a4..272c3b0 100644 --- a/src/typechecker.rs +++ b/src/typechecker.rs @@ -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::>() + .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::, _>>()? + .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; }