From 2a82999c26865a01d6c278f93bb8c11991b25517 Mon Sep 17 00:00:00 2001 From: sezna Date: Thu, 25 Jul 2024 14:40:01 -0400 Subject: [PATCH 1/6] begin work on if expressions --- petr-ast/src/ast.rs | 8 ++++++ petr-ast/src/pretty_print.rs | 26 ++++++++++++++++++++ petr-fmt/src/lib.rs | 1 + petr-ir/src/lib.rs | 5 ++++ petr-parse/src/parser.rs | 1 + petr-resolve/src/resolver.rs | 41 +++++++++++++++++++++++++++++-- petr-typecheck/src/lib.rs | 47 +++++++++++++++++++++++++++++++++++- 7 files changed, 126 insertions(+), 3 deletions(-) diff --git a/petr-ast/src/ast.rs b/petr-ast/src/ast.rs index 1c90bb1..9e7554e 100644 --- a/petr-ast/src/ast.rs +++ b/petr-ast/src/ast.rs @@ -131,6 +131,7 @@ pub enum Expression { IntrinsicCall(IntrinsicCall), Binding(ExpressionWithBindings), TypeConstructor(petr_utils::TypeId, Box<[SpannedItem]>), + If(If), } #[derive(Clone)] @@ -149,6 +150,13 @@ pub struct Binding { pub val: SpannedItem, } +#[derive(Clone)] +pub struct If { + pub condition: Box>, + pub then_branch: Box>, + pub else_branch: Option>>, +} + #[derive(Clone)] pub struct IntrinsicCall { pub intrinsic: Intrinsic, diff --git a/petr-ast/src/pretty_print.rs b/petr-ast/src/pretty_print.rs index 7565a9b..28058dc 100644 --- a/petr-ast/src/pretty_print.rs +++ b/petr-ast/src/pretty_print.rs @@ -164,10 +164,36 @@ impl PrettyPrint for Expression { Expression::Variable(v) => format!("var({})", interner.get(v.id)), Expression::IntrinsicCall(call) => call.pretty_print(interner, indentation), Expression::Binding(binding) => binding.pretty_print(interner, indentation + 1), + Expression::If(if_expr) => if_expr.pretty_print(interner, indentation), } } } +impl PrettyPrint for If { + fn pretty_print( + &self, + interner: &SymbolInterner, + indentation: usize, + ) -> String { + let If { + condition, + then_branch, + else_branch, + } = self; + format!( + "{}if {} then {}{}", + " ".repeat(indentation), + condition.pretty_print(interner, indentation), + then_branch.pretty_print(interner, indentation), + if let Some(else_branch) = else_branch { + format!(" else {}", else_branch.pretty_print(interner, indentation)) + } else { + Default::default() + } + ) + } +} + impl PrettyPrint for ExpressionWithBindings { fn pretty_print( &self, diff --git a/petr-fmt/src/lib.rs b/petr-fmt/src/lib.rs index cdfff2c..29644a0 100644 --- a/petr-fmt/src/lib.rs +++ b/petr-fmt/src/lib.rs @@ -207,6 +207,7 @@ impl Formattable for Expression { Expression::FunctionCall(f) => f.format(ctx), Expression::IntrinsicCall(i) => i.format(ctx), Expression::Binding(binding) => binding.format(ctx), + Expression::If(if_expr) => todo!(), } } } diff --git a/petr-ir/src/lib.rs b/petr-ir/src/lib.rs index 95ca0a6..b7ff5d0 100644 --- a/petr-ir/src/lib.rs +++ b/petr-ir/src/lib.rs @@ -259,6 +259,11 @@ impl Lowerer { } Ok(buf) }, + If { + condition, + then_branch, + else_branch, + } => todo!(), } } diff --git a/petr-parse/src/parser.rs b/petr-parse/src/parser.rs index 56e3278..85084a1 100644 --- a/petr-parse/src/parser.rs +++ b/petr-parse/src/parser.rs @@ -218,6 +218,7 @@ impl Parser { } } + // TODO: document [ExprId] system pub fn new_expr_id(&mut self) -> ExprId { let id = self.expr_id_assigner; self.expr_id_assigner += 1; diff --git a/petr-resolve/src/resolver.rs b/petr-resolve/src/resolver.rs index 8c759b8..db7c04b 100644 --- a/petr-resolve/src/resolver.rs +++ b/petr-resolve/src/resolver.rs @@ -135,13 +135,24 @@ pub enum ExprKind { Literal(petr_ast::Literal), List(Box<[Expr]>), FunctionCall(FunctionCall), - Variable { name: Identifier, ty: Type }, + Variable { + name: Identifier, + ty: Type, + }, Intrinsic(Intrinsic), Unit, // the `id` is the id of the type declaration that defined this constructor TypeConstructor(TypeId, Box<[Expr]>), ErrorRecovery, - ExpressionWithBindings { bindings: Vec, expression: Box }, + ExpressionWithBindings { + bindings: Vec, + expression: Box, + }, + If { + condition: Box, + then_branch: Box, + else_branch: Box, + }, } #[derive(Clone, Debug)] @@ -510,6 +521,27 @@ impl Resolve for SpannedItem { self.span(), ) }, + Expression::If(petr_ast::If { + condition, + then_branch, + else_branch, + }) => { + let condition = (**condition).resolve(resolver, binder, scope_id)?; + let then_branch = (**then_branch).resolve(resolver, binder, scope_id)?; + let else_branch = else_branch + .as_ref() + .map(|x| (*x).resolve(resolver, binder, scope_id))? + .unwrap_or_else(|| Expr::new(ExprKind::Unit, self.span())); + + Expr::new( + ExprKind::If { + condition: Box::new(condition), + then_branch: Box::new(then_branch), + else_branch: Box::new(else_branch), + }, + self.span(), + ) + }, }) } } @@ -768,6 +800,11 @@ mod tests { ), ExprKind::TypeConstructor(..) => "Type constructor".into(), ExprKind::ExpressionWithBindings { .. } => todo!(), + ExprKind::If { + condition, + then_branch, + else_branch, + } => todo!(), } } } diff --git a/petr-typecheck/src/lib.rs b/petr-typecheck/src/lib.rs index 1d4b8c1..43a900c 100644 --- a/petr-typecheck/src/lib.rs +++ b/petr-typecheck/src/lib.rs @@ -95,6 +95,7 @@ pub struct TypeContext { unit_ty: TypeVariable, string_ty: TypeVariable, int_ty: TypeVariable, + bool_ty: TypeVariable, error_recovery: TypeVariable, } @@ -104,12 +105,14 @@ impl Default for TypeContext { // instantiate basic primitive types let unit_ty = types.insert(PetrType::Unit); let string_ty = types.insert(PetrType::String); - let int_ty = types.insert(PetrType::Integer); + let bool_ty = types.insert(PetrType::Integer); + let int_ty = types.insert(PetrType::Boolean); let error_recovery = types.insert(PetrType::ErrorRecovery); // insert primitive types TypeContext { types, constraints: Default::default(), + bool_ty, unit_ty, string_ty, int_ty, @@ -555,6 +558,11 @@ impl TypeChecker { ErrorRecovery(..) => self.ctx.error_recovery, ExprWithBindings { expression, .. } => self.expr_ty(expression), TypeConstructor { ty, .. } => *ty, + If { + condition, + then_branch, + else_branch, + } => todo!(), } } @@ -580,6 +588,10 @@ impl TypeChecker { self.ctx.int_ty } + pub fn bool(&self) -> TypeVariable { + self.ctx.bool_ty + } + /// To reference an error recovery type, you must provide an error. /// This holds the invariant that error recovery types are only generated when /// an error occurs. @@ -669,6 +681,11 @@ pub enum TypedExprKind { ty: TypeVariable, args: Box<[TypedExpr]>, }, + If { + condition: Box, + then_branch: Box, + else_branch: Box, + }, } impl std::fmt::Debug for TypedExpr { @@ -705,6 +722,11 @@ impl std::fmt::Debug for TypedExpr { write!(f, "expression: {:?}", expression) }, TypeConstructor { ty, .. } => write!(f, "type constructor: {:?}", ty), + If { + condition, + then_branch, + else_branch, + } => todo!(), } } } @@ -781,6 +803,29 @@ impl TypeCheck for Expr { } }) }, + ExprKind::If { + condition, + then_branch, + else_branch, + } => { + let condition = condition.type_check(ctx); + let condition_ty = ctx.expr_ty(&condition); + ctx.unify(condition_ty, ctx.bool(), condition.span()); + + let then_branch = then_branch.type_check(ctx); + let then_ty = ctx.expr_ty(&then_branch); + + let else_branch = else_branch.type_check(ctx); + let else_ty = ctx.expr_ty(&else_branch); + + ctx.unify(then_ty, else_ty, self.span); + + TypedExprKind::If { + condition: Box::new(condition), + then_branch: Box::new(then_branch), + else_branch: Box::new(else_branch), + } + }, }; TypedExpr { kind, span: self.span } From b41a98bc8468927fc304cf5da5f9cce1074d6208 Mon Sep 17 00:00:00 2001 From: sezna Date: Fri, 26 Jul 2024 07:07:43 -0400 Subject: [PATCH 2/6] parse if exp --- petr-parse/src/parse_to_ast.rs | 19 +++++++++++++++++++ petr-parse/src/parser/lexer.rs | 9 +++++++++ petr-parse/src/parser/tests.rs | 18 ++++++++++++++++++ petr-typecheck/src/lib.rs | 5 +++-- 4 files changed, 49 insertions(+), 2 deletions(-) diff --git a/petr-parse/src/parse_to_ast.rs b/petr-parse/src/parse_to_ast.rs index a65ed0f..bc0d574 100644 --- a/petr-parse/src/parse_to_ast.rs +++ b/petr-parse/src/parse_to_ast.rs @@ -278,6 +278,7 @@ impl Parse for Expression { Some(Expression::Operator(Box::new(OperatorExpression { lhs, rhs, op }))) }, Token::Identifier => Some(Expression::Variable(p.parse()?)), + Token::If => Some(Expression::If(p.parse()?)), Token::OpenBracket => Some(Expression::List(p.parse()?)), Token::Tilde => Some(Expression::FunctionCall(p.parse()?)), Token::True | Token::False | Token::String | Token::Integer => Some(Expression::Literal(p.parse()?)), @@ -305,6 +306,24 @@ impl Parse for Expression { } } +impl Parse for If { + fn parse(p: &mut Parser) -> Option { + p.with_help("if expression", |p| -> Option { + p.token(Token::If)?; + let condition = p.parse()?; + p.token(Token::Then)?; + let then_branch = p.parse()?; + let else_tok = p.try_token(Token::Else); + let else_branch = if else_tok.is_some() { Some(Box::new(p.parse()?)) } else { None }; + Some(If { + condition: Box::new(condition), + then_branch: Box::new(then_branch), + else_branch, + }) + }) + } +} + impl Parse for ExpressionWithBindings { /// parse an expression that is prefaced with symbol bindings fn parse(p: &mut Parser) -> Option { diff --git a/petr-parse/src/parser/lexer.rs b/petr-parse/src/parser/lexer.rs index 59e5492..2ac9970 100644 --- a/petr-parse/src/parser/lexer.rs +++ b/petr-parse/src/parser/lexer.rs @@ -78,6 +78,12 @@ pub enum Token { Intrinsic, #[token(";")] Semicolon, + #[token("if")] + If, + #[token("then")] + Then, + #[token("else")] + Else, NewFile(SourceId), Eof, } @@ -132,6 +138,9 @@ impl std::fmt::Display for Token { Dot => write!(f, "."), As => write!(f, "as"), Semicolon => write!(f, ";"), + If => write!(f, "if"), + Then => write!(f, "then"), + Else => write!(f, "else"), } } } diff --git a/petr-parse/src/parser/tests.rs b/petr-parse/src/parser/tests.rs index c31a621..1d36f16 100644 --- a/petr-parse/src/parser/tests.rs +++ b/petr-parse/src/parser/tests.rs @@ -256,3 +256,21 @@ fn imports_and_exports() { "#]], ) } + +#[test] +fn if_exp_basic() { + check( + vec![ + "fn if_exp() returns 'int + if true then 1 else 0 + ", + ], + expect![[r#" + AST + ____ + module test = + Func if_exp() -> 'int if true then 1 else 0 + + "#]], + ) +} diff --git a/petr-typecheck/src/lib.rs b/petr-typecheck/src/lib.rs index 43a900c..90c7d0d 100644 --- a/petr-typecheck/src/lib.rs +++ b/petr-typecheck/src/lib.rs @@ -105,8 +105,8 @@ impl Default for TypeContext { // instantiate basic primitive types let unit_ty = types.insert(PetrType::Unit); let string_ty = types.insert(PetrType::String); - let bool_ty = types.insert(PetrType::Integer); - let int_ty = types.insert(PetrType::Boolean); + let bool_ty = types.insert(PetrType::Boolean); + let int_ty = types.insert(PetrType::Integer); let error_recovery = types.insert(PetrType::ErrorRecovery); // insert primitive types TypeContext { @@ -145,6 +145,7 @@ impl TypeContext { span: Span, ) -> TypeVariable { // infer is special -- it knows its own id, mostly for printing + // and disambiguating let infer_id = self.types.len(); self.types.insert(PetrType::Infer(infer_id, span)) } From 7ac96f444faac5a2fc9d1847e0147385dd1bf39a Mon Sep 17 00:00:00 2001 From: sezna Date: Fri, 26 Jul 2024 07:44:05 -0400 Subject: [PATCH 3/6] WIP: if expressions --- petr-ast/src/ast.rs | 4 +++ petr-ast/src/pretty_print.rs | 1 + petr-ir/src/lib.rs | 52 +++++++++++++++++++++++---------- petr-ir/src/opcodes.rs | 7 +++-- petr-parse/src/parse_to_ast.rs | 2 ++ petr-parse/src/parser/lexer.rs | 2 +- petr-parse/src/parser/tests.rs | 18 ++++++++++++ petr-resolve/src/resolver.rs | 1 + petr-stdlib/src/ops.pt | 2 ++ petr-typecheck/src/lib.rs | 27 ++++++++++++----- petr-vm/src/lib.rs | Bin 16552 -> 12656 bytes 11 files changed, 91 insertions(+), 25 deletions(-) diff --git a/petr-ast/src/ast.rs b/petr-ast/src/ast.rs index 9e7554e..be4e602 100644 --- a/petr-ast/src/ast.rs +++ b/petr-ast/src/ast.rs @@ -176,6 +176,7 @@ impl std::fmt::Display for Intrinsic { Intrinsic::Divide => write!(f, "divide"), Intrinsic::Malloc => write!(f, "malloc"), Intrinsic::SizeOf => write!(f, "size_of"), + Intrinsic::Equals => write!(f, "eq"), } } } @@ -190,6 +191,7 @@ pub enum Intrinsic { Divide, Malloc, SizeOf, + Equals, } #[derive(Clone)] @@ -257,6 +259,7 @@ pub enum Operator { Minus, Star, Slash, + Eq, } impl Operator { @@ -266,6 +269,7 @@ impl Operator { Operator::Minus => "-", Operator::Star => "*", Operator::Slash => "/", + Operator::Eq => "=", } } } diff --git a/petr-ast/src/pretty_print.rs b/petr-ast/src/pretty_print.rs index 28058dc..cafd28b 100644 --- a/petr-ast/src/pretty_print.rs +++ b/petr-ast/src/pretty_print.rs @@ -294,6 +294,7 @@ impl PrettyPrint for OperatorExpression { Operator::Minus => "-", Operator::Star => "*", Operator::Slash => "/", + Operator::Eq => "=", }; format!("{}({} {})", op, lhs, rhs) } diff --git a/petr-ir/src/lib.rs b/petr-ir/src/lib.rs index b7ff5d0..648dc0e 100644 --- a/petr-ir/src/lib.rs +++ b/petr-ir/src/lib.rs @@ -17,7 +17,7 @@ mod opcodes; pub use error::LoweringError; use opcodes::*; -pub use opcodes::{DataLabel, Intrinsic, IrOpcode, Reg, ReservedRegister}; +pub use opcodes::{DataLabel, Intrinsic, IrOpcode, LabelId, Reg, ReservedRegister}; pub fn lower(checker: TypeChecker) -> Result<(DataSection, Vec)> { let lowerer = Lowerer::new(checker)?; @@ -43,6 +43,7 @@ pub struct Lowerer { variables_in_scope: Vec>, monomorphized_functions: IndexMap, errors: Vec>, + label_assigner: usize, } #[derive(Debug, Clone)] @@ -65,6 +66,7 @@ impl Lowerer { type_checker, variables_in_scope: Default::default(), monomorphized_functions: Default::default(), + label_assigner: 0, errors: Default::default(), }; @@ -85,7 +87,7 @@ impl Lowerer { // insert jump to entry point as first instr if let Some(entry_point) = self.entry_point { - program_section.push(IrOpcode::JumpImmediate(entry_point)); + program_section.push(IrOpcode::JumpImmediateFunction(entry_point)); } else { // TODO use diagnostics here eprintln!("Warning: Generating IR for program with no entry point"); @@ -196,7 +198,7 @@ impl Lowerer { let monomorphized_func_id = self.monomorphize_function((*func, arg_petr_types.into_boxed_slice()))?; // jump to the function - buf.push(IrOpcode::JumpImmediate(monomorphized_func_id)); + buf.push(IrOpcode::JumpImmediateFunction(monomorphized_func_id)); // after returning to this function, return the register match return_destination { @@ -263,10 +265,29 @@ impl Lowerer { condition, then_branch, else_branch, - } => todo!(), + } => { + let mut buf = vec![]; + let condition_reg = self.fresh_reg(); + buf.append(&mut self.lower_expr(condition, ReturnDestination::Reg(condition_reg))?); + let else_label = self.new_label(); + let end_label = self.new_label(); + buf.push(IrOpcode::JumpIfFalseImmediate(condition_reg, else_label)); + buf.append(&mut self.lower_expr(then_branch, return_destination.clone())?); + buf.push(IrOpcode::JumpImmediate(end_label)); + buf.push(IrOpcode::Label(else_label)); + buf.append(&mut self.lower_expr(else_branch, return_destination)?); + buf.push(IrOpcode::Label(end_label)); + Ok(buf) + }, } } + fn new_label(&mut self) -> LabelId { + let label: LabelId = self.label_assigner.into(); + self.label_assigner += 1; + label + } + fn insert_literal_data( &mut self, lit: &petr_typecheck::Literal, @@ -373,6 +394,17 @@ impl Lowerer { } Ok(buf) }, + Equals(lhs, rhs) => { + let lhs_reg = self.fresh_reg(); + let rhs_reg = self.fresh_reg(); + buf.append(&mut self.lower_expr(lhs, ReturnDestination::Reg(lhs_reg))?); + buf.append(&mut self.lower_expr(rhs, ReturnDestination::Reg(rhs_reg))?); + let return_reg = match return_destination { + ReturnDestination::Reg(reg) => reg, + }; + buf.push(IrOpcode::Equal(return_reg, lhs_reg, rhs_reg)); + Ok(buf) + }, } } @@ -467,17 +499,7 @@ impl Lowerer { } } -/* -impl MonomorphizedFunction { - fn signature(&self) -> FunctionSignature { - FunctionSignature { - label: self.id, - concrete_types: self.params.iter().map(|(_name, ty)| ty.clone()).collect(), - } - } -} -*/ - +#[derive(Clone)] enum ReturnDestination { Reg(Reg), } diff --git a/petr-ir/src/opcodes.rs b/petr-ir/src/opcodes.rs index d7ce647..08596af 100644 --- a/petr-ir/src/opcodes.rs +++ b/petr-ir/src/opcodes.rs @@ -41,7 +41,7 @@ macro_rules! ir_ops { } ir_ops! { - JumpImmediate "jumpi" MonomorphizedFunctionId: imm; + JumpImmediateFunction "fjumpi" MonomorphizedFunctionId: imm; Jump "jump" Reg: dest; Add "add" Reg: dest, Reg: lhs, Reg: rhs; Multiply "mult" Reg: dest, Reg: lhs, Reg: rhs; @@ -63,7 +63,10 @@ ir_ops! { MallocImmediate "malloci" Reg: ptr_dest, Size: imm; /// Register `src` will itself have its value written to the memory pointed to by `dest_ptr` WriteRegisterToMemory "sri" Reg: src, Reg: dest_ptr; - Comment "comment" String: comment + Comment "comment" String: comment; + JumpIfFalseImmediate "cjump" Reg: cond, LabelId: dest; + JumpImmediate "jumpi" LabelId: dest; + Equal "eq" Reg: dest, Reg: lhs, Reg: rhs } idx_map_key!(LabelId); diff --git a/petr-parse/src/parse_to_ast.rs b/petr-parse/src/parse_to_ast.rs index bc0d574..11cc1eb 100644 --- a/petr-parse/src/parse_to_ast.rs +++ b/petr-parse/src/parse_to_ast.rs @@ -219,6 +219,7 @@ impl Parse for Operator { Token::Minus => Some(Operator::Minus), Token::Star => Some(Operator::Star), Token::Slash => Some(Operator::Slash), + Token::Equals => Some(Operator::Eq), _ => { p.push_error(p.span().with_item(ParseErrorKind::ExpectedOneOf( vec![Token::Plus, Token::Minus, Token::Star, Token::Slash], @@ -363,6 +364,7 @@ impl Parse for IntrinsicCall { "divide" => Intrinsic::Divide, "malloc" => Intrinsic::Malloc, "size_of" => Intrinsic::SizeOf, + "equals" => Intrinsic::Equals, a => todo!("unrecognized intrinsic error: {a:?}"), }; p.token(Token::Intrinsic)?; diff --git a/petr-parse/src/parser/lexer.rs b/petr-parse/src/parser/lexer.rs index 2ac9970..cc2ad64 100644 --- a/petr-parse/src/parser/lexer.rs +++ b/petr-parse/src/parser/lexer.rs @@ -91,7 +91,7 @@ pub enum Token { impl Token { pub(crate) fn is_operator(&self) -> bool { use Token::*; - matches!(self, Plus | Minus | Slash | Star) + matches!(self, Plus | Minus | Slash | Star | Equals) } } diff --git a/petr-parse/src/parser/tests.rs b/petr-parse/src/parser/tests.rs index 1d36f16..924d9f2 100644 --- a/petr-parse/src/parser/tests.rs +++ b/petr-parse/src/parser/tests.rs @@ -274,3 +274,21 @@ fn if_exp_basic() { "#]], ) } + +#[test] +fn nested_if_exp() { + check( + vec![ + "fn if_exp() returns 'int + if true then if false then 1 else 0 else 0 + ", + ], + expect![[r#" + AST + ____ + module test = + Func if_exp() -> 'int if true then if false then 1 else 0 else 0 + + "#]], + ) +} diff --git a/petr-resolve/src/resolver.rs b/petr-resolve/src/resolver.rs index db7c04b..0d5718c 100644 --- a/petr-resolve/src/resolver.rs +++ b/petr-resolve/src/resolver.rs @@ -412,6 +412,7 @@ impl Resolve for SpannedItem { Minus => "sub", Star => "mul", Slash => "div", + Eq => "eq", }; let path = ["std", "ops", func]; diff --git a/petr-stdlib/src/ops.pt b/petr-stdlib/src/ops.pt index 31a6429..5fe65db 100644 --- a/petr-stdlib/src/ops.pt +++ b/petr-stdlib/src/ops.pt @@ -5,3 +5,5 @@ export fn sub(lhs in 'int, rhs in 'int) returns 'int @subtract lhs, rhs export fn mult(lhs in 'int, rhs in 'int) returns 'int @multiply lhs, rhs export fn div(lhs in 'int, rhs in 'int) returns 'int @divide lhs, rhs + +export fn eq(lhs in 'A, rhs in 'A) returns 'bool @equals lhs, rhs diff --git a/petr-typecheck/src/lib.rs b/petr-typecheck/src/lib.rs index 90c7d0d..bb7f51c 100644 --- a/petr-typecheck/src/lib.rs +++ b/petr-typecheck/src/lib.rs @@ -559,11 +559,7 @@ impl TypeChecker { ErrorRecovery(..) => self.ctx.error_recovery, ExprWithBindings { expression, .. } => self.expr_ty(expression), TypeConstructor { ty, .. } => *ty, - If { - condition, - then_branch, - else_branch, - } => todo!(), + If { then_branch, .. } => self.expr_ty(then_branch), } } @@ -618,6 +614,7 @@ pub enum Intrinsic { Subtract(Box, Box), Malloc(Box), SizeOf(Box), + Equals(Box, Box), } impl std::fmt::Debug for Intrinsic { @@ -633,6 +630,7 @@ impl std::fmt::Debug for Intrinsic { Intrinsic::Subtract(lhs, rhs) => write!(f, "@subtract({:?}, {:?})", lhs, rhs), Intrinsic::Malloc(size) => write!(f, "@malloc({:?})", size), Intrinsic::SizeOf(expr) => write!(f, "@sizeof({:?})", expr), + Intrinsic::Equals(lhs, rhs) => write!(f, "@equal({:?}, {:?})", lhs, rhs), } } } @@ -727,7 +725,9 @@ impl std::fmt::Debug for TypedExpr { condition, then_branch, else_branch, - } => todo!(), + } => { + write!(f, "if {:?} then {:?} else {:?}", condition, then_branch, else_branch) + }, } } } @@ -943,6 +943,19 @@ impl TypeCheck for SpannedItem { ty: ctx.int(), } }, + Equals => { + if self.item().args.len() != 2 { + todo!("equal arg len check"); + } + + let lhs = self.item().args[0].type_check(ctx); + let rhs = self.item().args[1].type_check(ctx); + ctx.unify(ctx.expr_ty(&lhs), ctx.expr_ty(&rhs), self.span()); + TypedExprKind::Intrinsic { + intrinsic: Intrinsic::Equals(Box::new(lhs), Box::new(rhs)), + ty: ctx.bool(), + } + }, }; TypedExpr { kind, span: self.span() } @@ -1099,7 +1112,7 @@ fn replace_var_reference_types( replace_var_reference_types(&mut a.kind, params, num_replacements); }, // intrinsics which take two args, grouped for convenience - Add(a, b) | Subtract(a, b) | Multiply(a, b) | Divide(a, b) => { + Add(a, b) | Subtract(a, b) | Multiply(a, b) | Divide(a, b) | Equals(a, b) => { replace_var_reference_types(&mut a.kind, params, num_replacements); replace_var_reference_types(&mut b.kind, params, num_replacements); }, diff --git a/petr-vm/src/lib.rs b/petr-vm/src/lib.rs index 6455bc7f072f822bd8538f2f3e71be3169455686..c6fa96558a508efd8afaf8d82521871462704575 100644 GIT binary patch delta 496 zcmZ3{$oL^?!%{|epTwlp9M6==ix~?TtvAbvHM8<50D*FJYEe;sk%p4bWCuwDQLrk% z{1UhP(!3N6&m#YVSs$~H!h%d>H&jEQx17w`0Jr~Fu$e?zzzJ~MU zI3+$7sC{6)BG~j!*3)odbuBDS%$b~~wn`~8O(7?v7-DG=m{F}@s8Fkr3iOadwSs{{ z?c|O67L%ha1$a?Ro2+dms|I$Hl>#)Jb->QjRM53k2udw3%_*_b(6m#i=9=7~q%(Po zlGNne>>`stDd|tvR5qWSp)5T4je>|V*3eJ2vI2{1fGyC})U<~CxYn9$@(U!jK0NC`Py8r+H literal 16552 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 Bs%HQI From 56a55fbf063d2ee7d4bbcc670b9cdaf3ea06660b Mon Sep 17 00:00:00 2001 From: sezna Date: Fri, 26 Jul 2024 07:46:59 -0400 Subject: [PATCH 4/6] pull vm tests out into their own file --- petr-vm/src/tests.rs | Bin 0 -> 5859 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 petr-vm/src/tests.rs diff --git a/petr-vm/src/tests.rs b/petr-vm/src/tests.rs new file mode 100644 index 0000000000000000000000000000000000000000..3aefbd21b152d46433877bf1b45103233f21d519 GIT binary patch literal 5859 zcmdT|Yj4{&6z%8y3Pu4^sc~G}d9+pc=!SL=pg;?B=su)@Kuff(MixDiO6sM_Z{NA3 zBuk0x&FeN`25d{@z2v#C!z&e9F!5<2ViOsmO&DIgyB>RGM{l0*6cAYmqoP!W;cIy% zlu(UPCA7>gMHrSBkzUTnGSdwav&0575nqHMT}Jl0F<+Q8)3iXI2o(vXBsQ@7>S7_V z>!as8lbl&Ke~00d=8M9FES)bh_H%CJSMQ8U^XWG|JL^mcOA~rK0lT*2nJ`Y1eeM4H zDRlkdc|AKX4AZhuvCxpQR%?@F={OACehu_}7q-Ie3eTSA%ScE}k zDy_ZXc^X4;T%=jTCOpkV60%q(f+cB^KQs=tSDJcLJKcA-mid#AeRp!nxn$*xmtw+U za~LM>!mn^_wmun8cl~|Zt;Db%wgrH^lz9hUo5T+EDxZZS;H!JTwg3lQ&$^(VlNE{0!dt zhGnimY3!9!6nHyarH)-3bzEo9H6$nx1E&Wzs)10c!o>jMYTqG1U~6ZWQ2SUzDvf0C zEQwOeY7s8_)GF;!S2OLgFDszG86k_gBRGD-4F_x3RIJROq&d&h-;oC^p1YjGZ|~>M zXDR4~6dUvY<(zn>$0BWbe9>c>oNCH%7xc?{Uln7&|Lr3XobtY$R34 z)&v>*TY3cq9rQ}KIBmpy?*oKJN$jH)m;a)CAd!}_dm)s z_o3reC3NHR%N;R&uEFjm62JNTPzD@K@14vxm^Iux@ee^ygyUFPHLE_dF|v|1|K<-DE&`9zSA59?SV$ zGBVVd;utd8w8x<4cK}x{~Tb(-xH-gtB2vtsGIc*JhE_9n)xt%(Hy* zgOo@g{C>|BCAc|DkbR9t37hZ8-Et)iEmUPf*pXA%8b=gK_kyK&&a`SvmU9ejqIz|Q@1v?w60EN;lgNE(s8sXt6=Gn$z zIWa~Bn99CwjCQ&p6?tkXAh%Y;_hur?WRL17OLjfVsxFjgln>;g=Ev3Vz-3}{Vntc^ zbXwazc<{j6J8xEh!)~4=DRnfP;@#X7Y4Tsk{)q62s!Tl{ynP#NU{iR9Yr|V2$8`Uyl}aeKVo23Yr&BEwQ- zyk2Bfip1+hj!KcFL&Pici(Fpio=f1$u5jAcf$slXd7dfMQ45P`gX9yoN3HtmSretGlqO~`PZH6(2F3w2sM z Date: Fri, 26 Jul 2024 08:21:38 -0400 Subject: [PATCH 5/6] TDD: work through some testS --- petr-fmt/src/lib.rs | 2 +- petr-ir/src/lib.rs | 34 +++++++++++++++++++---- petr-ir/src/opcodes.rs | 6 ++++ petr-resolve/src/resolver.rs | 6 +--- petr-typecheck/src/lib.rs | 53 +++++++++++++++++++++++++++++++++++- 5 files changed, 89 insertions(+), 12 deletions(-) diff --git a/petr-fmt/src/lib.rs b/petr-fmt/src/lib.rs index 29644a0..0ff5d50 100644 --- a/petr-fmt/src/lib.rs +++ b/petr-fmt/src/lib.rs @@ -207,7 +207,7 @@ impl Formattable for Expression { Expression::FunctionCall(f) => f.format(ctx), Expression::IntrinsicCall(i) => i.format(ctx), Expression::Binding(binding) => binding.format(ctx), - Expression::If(if_expr) => todo!(), + Expression::If(_) => todo!(), } } } diff --git a/petr-ir/src/lib.rs b/petr-ir/src/lib.rs index 648dc0e..90ede76 100644 --- a/petr-ir/src/lib.rs +++ b/petr-ir/src/lib.rs @@ -213,7 +213,33 @@ impl Lowerer { // Ok(buf) }, - List { .. } => todo!(), + List { elements, .. } => { + let size_of_each_elements = elements + .iter() + .map(|el| self.to_ir_type(self.type_checker.expr_ty(el)).size().num_bytes() as u64) + .sum::(); + let size_of_list = size_of_each_elements * elements.len() as u64; + let size_of_list_reg = self.fresh_reg(); + + let mut buf = vec![]; + buf.push(IrOpcode::LoadImmediate(size_of_list_reg, size_of_list)); + let ReturnDestination::Reg(return_reg) = return_destination; + buf.push(IrOpcode::Malloc(return_reg, size_of_list_reg)); + + let mut current_offset = 0; + let current_offset_reg = self.fresh_reg(); + for el in elements { + // currently this only works for types that fit in a single register, + // will need work for larger types + let reg = self.fresh_reg(); + buf.append(&mut self.lower_expr(el, ReturnDestination::Reg(reg))?); + buf.push(IrOpcode::LoadImmediate(current_offset_reg, current_offset)); + buf.push(IrOpcode::Add(current_offset_reg, current_offset_reg, return_reg)); + buf.push(IrOpcode::WriteRegisterToMemory(reg, current_offset_reg)); + current_offset += self.to_ir_type(self.type_checker.expr_ty(el)).size().num_bytes() as u64; + } + Ok(buf) + }, Unit => todo!(), Variable { name, ty: _ } => { let var_reg = self @@ -327,7 +353,7 @@ impl Lowerer { }, Arrow(_) => todo!(), ErrorRecovery => todo!(), - List(_) => todo!(), + List(ty) => IrTy::List(Box::new(self.to_ir_type(ty))), Infer(_, span) => { println!("Unable to infer ty: {ty:?}"); self.errors.push(span.with_item(LoweringError::UnableToInferType)); @@ -399,9 +425,7 @@ impl Lowerer { let rhs_reg = self.fresh_reg(); buf.append(&mut self.lower_expr(lhs, ReturnDestination::Reg(lhs_reg))?); buf.append(&mut self.lower_expr(rhs, ReturnDestination::Reg(rhs_reg))?); - let return_reg = match return_destination { - ReturnDestination::Reg(reg) => reg, - }; + let ReturnDestination::Reg(return_reg) = return_destination; buf.push(IrOpcode::Equal(return_reg, lhs_reg, rhs_reg)); Ok(buf) }, diff --git a/petr-ir/src/opcodes.rs b/petr-ir/src/opcodes.rs index 08596af..6386c32 100644 --- a/petr-ir/src/opcodes.rs +++ b/petr-ir/src/opcodes.rs @@ -106,6 +106,7 @@ pub enum IrTy { String, Boolean, UserDefinedType { variants: Vec }, + List(Box), } #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] @@ -137,6 +138,11 @@ impl IrTy { .max() .expect("user defined type should have at least one variant") }, + IrTy::List(ty) => { + // the size of a list is the size of a pointer + // to the first element + return IrTy::Ptr(ty.clone()).size(); + }, } .into() } diff --git a/petr-resolve/src/resolver.rs b/petr-resolve/src/resolver.rs index 0d5718c..7519094 100644 --- a/petr-resolve/src/resolver.rs +++ b/petr-resolve/src/resolver.rs @@ -801,11 +801,7 @@ mod tests { ), ExprKind::TypeConstructor(..) => "Type constructor".into(), ExprKind::ExpressionWithBindings { .. } => todo!(), - ExprKind::If { - condition, - then_branch, - else_branch, - } => todo!(), + ExprKind::If { .. } => todo!(), } } } diff --git a/petr-typecheck/src/lib.rs b/petr-typecheck/src/lib.rs index bb7f51c..c3cf6e4 100644 --- a/petr-typecheck/src/lib.rs +++ b/petr-typecheck/src/lib.rs @@ -1200,7 +1200,7 @@ mod tests { } if !type_checker.errors.is_empty() { - s.push_str("\nErrors:\n"); + s.push_str("\n__ERRORS__\n"); for error in type_checker.errors { s.push_str(&format!("{:?}\n", error)); } @@ -1601,4 +1601,55 @@ fn main() returns 'int ~hi(1, 2)"#, fn main([]) -> int"#]], ) } + + #[test] + fn if_rejects_non_bool_condition() { + check( + r#" + fn hi(x in 'int) returns 'int + if x then 1 else 2 + fn main() returns 'int ~hi(1)"#, + expect![[r#" + fn hi: (int → int) + if int then 1 else 2 + + fn main: int + function call to functionid0 with args: x: int, returns int + "#]], + ) + } + + #[test] + fn if_rejects_non_unit_missing_else() { + check( + r#" + fn hi(x in 'int) returns 'int + if x then 1 + fn main() returns 'int ~hi(1)"#, + expect![[r#" + fn hi: (int → int) + if int then 1 + + fn main: int + function call to functionid0 with args: x: int, returns int + "#]], + ) + } + + #[test] + fn if_allows_unit_missing_else() { + check( + r#" + fn hi(x in 'int) returns 'unit + if = x 1 then @puts "hi" + fn main() returns 'int ~hi(1)"#, + expect![[r#" + fn hi: (int → int) + if int then 1 else unit + + fn main: int + function call to functionid0 with args: x: int, returns int + "#]], + ) + } } From b2bbd80653f3ef09b80b9c13444d1bd0eb6a0486 Mon Sep 17 00:00:00 2001 From: sezna Date: Fri, 26 Jul 2024 10:50:27 -0400 Subject: [PATCH 6/6] fix missing else type unification error --- petr-ir/src/lib.rs | 26 +++++----- petr-parse/src/parser/lexer.rs | 12 ++--- petr-parse/src/parser/tests.rs | 21 ++++++++ petr-resolve/src/resolver.rs | 48 ++++++++++++++---- petr-typecheck/src/lib.rs | 88 +++++++++++++++++++-------------- petr-vm/src/tests.rs | Bin 5859 -> 5797 bytes 6 files changed, 130 insertions(+), 65 deletions(-) diff --git a/petr-ir/src/lib.rs b/petr-ir/src/lib.rs index 90ede76..a5685fd 100644 --- a/petr-ir/src/lib.rs +++ b/petr-ir/src/lib.rs @@ -649,7 +649,7 @@ mod tests { 4 ld v1 datalabel0 5 push v1 6 ppc - 7 jumpi monomorphizedfunctionid0 + 7 fjumpi monomorphizedfunctionid0 8 cp v0 rr(func return value) 9 cp rr(func return value) v0 10 ret @@ -686,7 +686,7 @@ mod tests { 11 cp v7 v3 12 push v7 13 ppc - 14 jumpi monomorphizedfunctionid0 + 14 fjumpi monomorphizedfunctionid0 15 cp v5 rr(func return value) 16 cp rr(func return value) v5 17 ret @@ -696,7 +696,7 @@ mod tests { 20 ld v2 datalabel1 21 push v2 22 ppc - 23 jumpi monomorphizedfunctionid1 + 23 fjumpi monomorphizedfunctionid1 24 cp v0 rr(func return value) 25 cp rr(func return value) v0 26 ret @@ -730,7 +730,7 @@ mod tests { 7 ld v2 datalabel1 8 push v2 9 ppc - 10 jumpi monomorphizedfunctionid0 + 10 fjumpi monomorphizedfunctionid0 11 cp v0 rr(func return value) 12 cp rr(func return value) v0 13 ret @@ -779,15 +779,15 @@ mod tests { 17 cp v13 v3 18 push v13 19 ppc - 20 jumpi monomorphizedfunctionid0 + 20 fjumpi monomorphizedfunctionid0 21 cp v11 rr(func return value) 22 push v11 23 ppc - 24 jumpi monomorphizedfunctionid0 + 24 fjumpi monomorphizedfunctionid0 25 cp v9 rr(func return value) 26 push v9 27 ppc - 28 jumpi monomorphizedfunctionid0 + 28 fjumpi monomorphizedfunctionid0 29 cp v5 rr(func return value) 30 cp rr(func return value) v5 31 ret @@ -797,7 +797,7 @@ mod tests { 34 ld v2 datalabel1 35 push v2 36 ppc - 37 jumpi monomorphizedfunctionid1 + 37 fjumpi monomorphizedfunctionid1 38 cp v0 rr(func return value) 39 cp rr(func return value) v0 40 ret @@ -854,19 +854,19 @@ mod tests { 22 cp v18 v10 23 push v18 24 ppc - 25 jumpi monomorphizedfunctionid0 + 25 fjumpi monomorphizedfunctionid0 26 cp v16 rr(func return value) 27 push v16 28 ppc - 29 jumpi monomorphizedfunctionid0 + 29 fjumpi monomorphizedfunctionid0 30 cp v14 rr(func return value) 31 push v14 32 ppc - 33 jumpi monomorphizedfunctionid0 + 33 fjumpi monomorphizedfunctionid0 34 cp v12 rr(func return value) 35 push v12 36 ppc - 37 jumpi monomorphizedfunctionid0 + 37 fjumpi monomorphizedfunctionid0 38 cp v5 rr(func return value) 39 cp rr(func return value) v5 40 ret @@ -876,7 +876,7 @@ mod tests { 43 ld v2 datalabel1 44 push v2 45 ppc - 46 jumpi monomorphizedfunctionid1 + 46 fjumpi monomorphizedfunctionid1 47 cp v0 rr(func return value) 48 cp rr(func return value) v0 49 ret diff --git a/petr-parse/src/parser/lexer.rs b/petr-parse/src/parser/lexer.rs index cc2ad64..ca5b894 100644 --- a/petr-parse/src/parser/lexer.rs +++ b/petr-parse/src/parser/lexer.rs @@ -28,6 +28,12 @@ pub enum Token { Star, #[regex("[0-9]+")] Integer, + #[token("if")] + If, + #[token("then")] + Then, + #[token("else")] + Else, #[regex("[_a-zA-Z][_a-zA-Z0-9]{0,30}")] Identifier, #[regex(r#""([^"\\]|\\["\\bnfrt]|u[a-fA-F0-9]{4})*""#)] @@ -78,12 +84,6 @@ pub enum Token { Intrinsic, #[token(";")] Semicolon, - #[token("if")] - If, - #[token("then")] - Then, - #[token("else")] - Else, NewFile(SourceId), Eof, } diff --git a/petr-parse/src/parser/tests.rs b/petr-parse/src/parser/tests.rs index 924d9f2..bb02af7 100644 --- a/petr-parse/src/parser/tests.rs +++ b/petr-parse/src/parser/tests.rs @@ -292,3 +292,24 @@ fn nested_if_exp() { "#]], ) } + +#[test] +fn if_without_else() { + check( + vec![ + " + fn hi(x in 'int) returns 'int + if x then 1 + ", + ], + expect![[r#" + AST + ____ + module test = + Func hi( + x ∈ 'int + ) -> 'int if var(x) then 1 + + "#]], + ) +} diff --git a/petr-resolve/src/resolver.rs b/petr-resolve/src/resolver.rs index 7519094..00cf7e0 100644 --- a/petr-resolve/src/resolver.rs +++ b/petr-resolve/src/resolver.rs @@ -368,7 +368,7 @@ impl Resolve for SpannedItem { // resolved function map. // If we were to return `None` and not resolve the function, then calls to this // function would hold a reference `FunctionId(x)` which would not exist anymore. - None => Expr::error_recovery(self.span()), + None => dbg!(Expr::error_recovery(self.span())), }; Some(Function { @@ -529,16 +529,18 @@ impl Resolve for SpannedItem { }) => { let condition = (**condition).resolve(resolver, binder, scope_id)?; let then_branch = (**then_branch).resolve(resolver, binder, scope_id)?; - let else_branch = else_branch - .as_ref() - .map(|x| (*x).resolve(resolver, binder, scope_id))? - .unwrap_or_else(|| Expr::new(ExprKind::Unit, self.span())); - + let else_branch = match else_branch { + Some(branch) => { + let branch = (**branch).resolve(resolver, binder, scope_id)?; + Box::new(branch) + }, + None => Box::new(Expr::new(ExprKind::Unit, self.span())), + }; Expr::new( ExprKind::If { - condition: Box::new(condition), + condition: Box::new(condition), then_branch: Box::new(then_branch), - else_branch: Box::new(else_branch), + else_branch, }, self.span(), ) @@ -561,7 +563,7 @@ impl Resolve for petr_ast::IntrinsicCall { .iter() .map(|x| match x.resolve(resolver, binder, scope_id) { Some(x) => x, - None => Expr::error_recovery(x.span()), + None => dbg!(Expr::error_recovery(x.span())), }) .collect(); Some(Intrinsic { @@ -801,7 +803,18 @@ mod tests { ), ExprKind::TypeConstructor(..) => "Type constructor".into(), ExprKind::ExpressionWithBindings { .. } => todo!(), - ExprKind::If { .. } => todo!(), + ExprKind::If { + condition, + then_branch, + else_branch, + } => { + format!( + "if {} then {} else {}", + (*condition).to_string(resolver), + (*then_branch).to_string(resolver), + (*else_branch).to_string(resolver) + ) + }, } } } @@ -1026,4 +1039,19 @@ mod tests { "#]], ) } + + #[test] + fn if_without_else() { + check( + " + fn hi(x in 'int) returns 'int + if x then 1 + ", + expect![[r#" + _____FUNCTIONS_____ + #0 hi( x: int, ) -> int "if x: int then Literal(Integer(1)) else Unit" + _____TYPES_____ + "#]], + ) + } } diff --git a/petr-typecheck/src/lib.rs b/petr-typecheck/src/lib.rs index c3cf6e4..70cdef9 100644 --- a/petr-typecheck/src/lib.rs +++ b/petr-typecheck/src/lib.rs @@ -712,7 +712,9 @@ impl std::fmt::Debug for TypedExpr { Unit => write!(f, "unit"), Variable { name, .. } => write!(f, "variable: {}", name.id), Intrinsic { intrinsic, .. } => write!(f, "intrinsic: {:?}", intrinsic), - ErrorRecovery(..) => write!(f, "error recovery"), + ErrorRecovery(span) => { + write!(f, "error recovery {span:?}") + }, ExprWithBindings { bindings, expression } => { write!(f, "bindings: ")?; for (name, expr) in bindings { @@ -819,7 +821,7 @@ impl TypeCheck for Expr { let else_branch = else_branch.type_check(ctx); let else_ty = ctx.expr_ty(&else_branch); - ctx.unify(then_ty, else_ty, self.span); + ctx.unify(then_ty, else_ty, else_branch.span()); TypedExprKind::If { condition: Box::new(condition), @@ -1142,7 +1144,10 @@ mod tests { panic!("test failed: code didn't parse"); } let (errs, resolved) = resolve_symbols(ast, interner, Default::default()); - assert!(errs.is_empty(), "can't typecheck: unresolved symbols"); + if !errs.is_empty() { + errs.into_iter().for_each(|err| eprintln!("{:?}", render_error(&source_map, err))); + panic!("unresolved symbols in test"); + } let type_checker = TypeChecker::new(resolved); let res = pretty_print_type_checker(type_checker); @@ -1185,7 +1190,7 @@ mod tests { } if !type_checker.monomorphized_functions.is_empty() { - s.push_str("\n__MONOMORPHIZED FUNCTIONS__"); + s.push_str("__MONOMORPHIZED FUNCTIONS__"); } for func in type_checker.monomorphized_functions.values() { @@ -1310,8 +1315,8 @@ mod tests { fn foo(x in 'A) returns 'A x "#, expect![[r#" - fn foo: (t4 → t4) - variable x: t4 + fn foo: (t5 → t5) + variable x: t5 "#]], ); @@ -1362,13 +1367,12 @@ mod tests { fn firstVariant: (MyType → MyComposedType) type constructor: MyComposedType - fn secondVariant: (int → MyType → t18 → MyComposedType) + fn secondVariant: (int → MyType → t19 → MyComposedType) type constructor: MyComposedType fn foo: (MyType → MyComposedType) function call to functionid2 with args: someField: MyType, returns MyComposedType - __MONOMORPHIZED FUNCTIONS__ fn firstVariant(["MyType"]) -> MyComposedType"#]], ); @@ -1426,7 +1430,6 @@ mod tests { fn my_func: unit intrinsic: @puts(function call to functionid0 with args: ) - __MONOMORPHIZED FUNCTIONS__ fn string_literal([]) -> string"#]], ); @@ -1457,7 +1460,7 @@ mod tests { intrinsic: @puts(literal: true) - Errors: + __ERRORS__ SpannedItem UnificationFailure(String, Boolean) [Span { source: SourceId(0), span: SourceSpan { offset: SourceOffset(52), length: 4 } }] "#]], ); @@ -1493,10 +1496,9 @@ mod tests { fn my_func: unit intrinsic: @puts(function call to functionid0 with args: ) - __MONOMORPHIZED FUNCTIONS__ fn bool_literal([]) -> bool - Errors: + __ERRORS__ SpannedItem UnificationFailure(String, Boolean) [Span { source: SourceId(0), span: SourceSpan { offset: SourceOffset(110), length: 14 } }] "#]], ); @@ -1517,7 +1519,7 @@ mod tests { ~bool_literal(true, false) "#, expect![[r#" - fn bool_literal: (t4 → t5 → bool) + fn bool_literal: (t5 → t6 → bool) literal: true fn my_func: bool @@ -1526,7 +1528,6 @@ mod tests { fn my_second_func: bool function call to functionid0 with args: a: bool, b: bool, returns bool - __MONOMORPHIZED FUNCTIONS__ fn bool_literal(["int", "int"]) -> bool fn bool_literal(["bool", "bool"]) -> bool"#]], @@ -1539,11 +1540,11 @@ mod tests { fn my_list() returns 'list [ 1, true ] "#, expect![[r#" - fn my_list: t7 + fn my_list: t8 list: [literal: 1, literal: true, ] - Errors: + __ERRORS__ SpannedItem UnificationFailure(Integer, Boolean) [Span { source: SourceId(0), span: SourceSpan { offset: SourceOffset(48), length: 5 } }] "#]], ); @@ -1562,10 +1563,10 @@ mod tests { variable a: int fn add_five: (int → int) - error recovery + error recovery Span { source: SourceId(0), span: SourceSpan { offset: SourceOffset(113), length: 8 } } - Errors: + __ERRORS__ SpannedItem ArgumentCountMismatch { function: "add", expected: 2, got: 1 } [Span { source: SourceId(0), span: SourceSpan { offset: SourceOffset(113), length: 8 } }] "#]], ); @@ -1595,7 +1596,6 @@ fn main() returns 'int ~hi(1, 2)"#, fn main: int function call to functionid0 with args: x: int, y: int, returns int - __MONOMORPHIZED FUNCTIONS__ fn hi(["int", "int"]) -> int fn main([]) -> int"#]], @@ -1611,11 +1611,17 @@ fn main() returns 'int ~hi(1, 2)"#, fn main() returns 'int ~hi(1)"#, expect![[r#" fn hi: (int → int) - if int then 1 else 2 + if variable: symbolid2 then literal: 1 else literal: 2 fn main: int function call to functionid0 with args: x: int, returns int - "#]], + + __MONOMORPHIZED FUNCTIONS__ + fn hi(["int"]) -> int + fn main([]) -> int + __ERRORS__ + SpannedItem UnificationFailure(Integer, Boolean) [Span { source: SourceId(0), span: SourceSpan { offset: SourceOffset(61), length: 2 } }] + "#]], ) } @@ -1623,16 +1629,22 @@ fn main() returns 'int ~hi(1, 2)"#, fn if_rejects_non_unit_missing_else() { check( r#" - fn hi(x in 'int) returns 'int - if x then 1 - fn main() returns 'int ~hi(1)"#, + fn hi() returns 'int + if true then 1 + fn main() returns 'int ~hi()"#, expect![[r#" - fn hi: (int → int) - if int then 1 + fn hi: int + if literal: true then literal: 1 else unit fn main: int - function call to functionid0 with args: x: int, returns int - "#]], + function call to functionid0 with args: returns int + + __MONOMORPHIZED FUNCTIONS__ + fn hi([]) -> int + fn main([]) -> int + __ERRORS__ + SpannedItem UnificationFailure(Integer, Unit) [Span { source: SourceId(0), span: SourceSpan { offset: SourceOffset(33), length: 46 } }] + "#]], ) } @@ -1640,16 +1652,20 @@ fn main() returns 'int ~hi(1, 2)"#, fn if_allows_unit_missing_else() { check( r#" - fn hi(x in 'int) returns 'unit - if = x 1 then @puts "hi" - fn main() returns 'int ~hi(1)"#, + fn hi() returns 'unit + if true then @puts "hi" + + fn main() returns 'unit ~hi()"#, expect![[r#" - fn hi: (int → int) - if int then 1 else unit + fn hi: unit + if literal: true then intrinsic: @puts(literal: "hi") else unit - fn main: int - function call to functionid0 with args: x: int, returns int - "#]], + fn main: unit + function call to functionid0 with args: returns unit + + __MONOMORPHIZED FUNCTIONS__ + fn hi([]) -> unit + fn main([]) -> unit"#]], ) } } diff --git a/petr-vm/src/tests.rs b/petr-vm/src/tests.rs index 3aefbd21b152d46433877bf1b45103233f21d519..8a2ad1807bfcca4bdcbc1ba9afb2ac703893c669 100644 GIT binary patch delta 110 zcmaE?yHt0BwAkbu delta 93 wcmZ3g`&f5_wAkb>ZrRCw+>%gi;+n{%fCiE