From 01073f95dbe0e567ffe87885ca22d270e774038e Mon Sep 17 00:00:00 2001 From: sezna Date: Tue, 23 Jul 2024 11:01:50 -0400 Subject: [PATCH 01/10] wip: add @size_of intrinsic --- justfile | 5 ++- petr-ast/src/ast.rs | 2 + petr-ir/src/error.rs | 2 + petr-ir/src/lib.rs | 53 +++++++++++++++++----- petr-parse/src/parse_to_ast.rs | 1 + petr-resolve/src/resolver.rs | 4 +- petr-typecheck/src/lib.rs | 80 ++++++++++++++++++++++++--------- petr-utils/src/sources.rs | 26 +++++++++++ petr-vm/src/lib.rs | Bin 15449 -> 15937 bytes stdlib/src/mem.pt | 4 ++ 10 files changed, 140 insertions(+), 37 deletions(-) diff --git a/justfile b/justfile index 67f4dfb..61d53a9 100644 --- a/justfile +++ b/justfile @@ -2,7 +2,10 @@ alias t := test test: - CARGO_TERM_COLOR=always NO_COLOR=1 cargo test + CARGO_TERM_COLOR=always NO_COLOR=1 cargo test + +dtest: + CARGO_TERM_COLOR=always NO_COLOR=1 cargo test --features=debug update-expects: CARGO_TERM_COLOR=always NO_COLOR=1 UPDATE_EXPECT=1 cargo test diff --git a/petr-ast/src/ast.rs b/petr-ast/src/ast.rs index 2c74e6d..1c90bb1 100644 --- a/petr-ast/src/ast.rs +++ b/petr-ast/src/ast.rs @@ -167,6 +167,7 @@ impl std::fmt::Display for Intrinsic { Intrinsic::Multiply => write!(f, "multiply"), Intrinsic::Divide => write!(f, "divide"), Intrinsic::Malloc => write!(f, "malloc"), + Intrinsic::SizeOf => write!(f, "size_of"), } } } @@ -180,6 +181,7 @@ pub enum Intrinsic { Multiply, Divide, Malloc, + SizeOf, } #[derive(Clone)] diff --git a/petr-ir/src/error.rs b/petr-ir/src/error.rs index 31ff180..01661b3 100644 --- a/petr-ir/src/error.rs +++ b/petr-ir/src/error.rs @@ -5,4 +5,6 @@ use thiserror::Error; pub enum LoweringError { #[error("Internal compiler error: {0}")] Internal(String), + #[error("Unable to infer type")] + UnableToInferType, } diff --git a/petr-ir/src/lib.rs b/petr-ir/src/lib.rs index 0612ceb..119828f 100644 --- a/petr-ir/src/lib.rs +++ b/petr-ir/src/lib.rs @@ -48,6 +48,7 @@ pub struct Lowerer { type_checker: TypeChecker, variables_in_scope: Vec>, monomorphized_functions: IndexMap, + errors: Vec>, } #[derive(Debug, Clone)] @@ -72,6 +73,7 @@ impl Lowerer { type_checker, variables_in_scope: Default::default(), monomorphized_functions: Default::default(), + errors: Default::default(), }; let monomorphized_entry_point_id = match entry_point { @@ -81,6 +83,7 @@ impl Lowerer { Some(monomorphized_entry_point_id) }, }; + println!("my errors are {:?}", lowerer.errors); lowerer.entry_point = monomorphized_entry_point_id; Ok(lowerer) @@ -304,33 +307,37 @@ impl Lowerer { } fn to_ir_type( - &self, + &mut self, param_ty: TypeVariable, ) -> IrTy { use petr_typecheck::PetrType::*; - let ty = self.type_checker.look_up_variable(param_ty); + let ty = self.type_checker.look_up_variable(param_ty).clone(); match ty { Unit => IrTy::Unit, Integer => IrTy::Int64, Boolean => IrTy::Boolean, String => IrTy::String, - Ref(ty) => self.to_ir_type(*ty), + Ref(ty) => self.to_ir_type(ty), UserDefined { name: _, variants } => { // get the user type - - IrTy::UserDefinedType { - variants: variants - .iter() - .map(|variant| IrUserDefinedTypeVariant { - fields: variant.fields.iter().map(|field| self.to_ir_type(*field)).collect(), - }) - .collect(), + let mut variants_buf = Vec::with_capacity(variants.len()); + + for variant in variants { + let mut fields_buf = Vec::with_capacity(variant.fields.len()); + for field in &variant.fields { + fields_buf.push(self.to_ir_type(*field)); + } + variants_buf.push(IrUserDefinedTypeVariant { fields: fields_buf }); } + IrTy::UserDefinedType { variants: variants_buf } }, Arrow(_) => todo!(), ErrorRecovery => todo!(), List(_) => todo!(), - Infer(_) => todo!("err for var {param_ty}: inference should be resolved by now"), + Infer(_, span) => { + self.errors.push(span.with_item(LoweringError::UnableToInferType)); + IrTy::Unit + }, } } @@ -373,6 +380,16 @@ impl Lowerer { } Ok(buf) }, + SizeOf(expr) => { + let ty = self.type_checker.expr_ty(expr); + let size = self.to_ir_type(ty).size(); + match return_destination { + ReturnDestination::Reg(reg) => { + buf.push(IrOpcode::LoadImmediate(reg, size.num_bytes() as u64)); + }, + } + Ok(buf) + }, } } @@ -452,8 +469,20 @@ impl Lowerer { pc += 1; } } + println!("in pretty print, my errors are {:?}", self.errors); + + if !self.errors.is_empty() { + result.push_str("\n____ERRORS____"); + for err in &self.errors { + result.push_str(&format!("{:?}\n", err)); + } + } result } + + pub fn errors(&self) -> &[SpannedItem] { + &self.errors + } } struct MonomorphizedFunction { diff --git a/petr-parse/src/parse_to_ast.rs b/petr-parse/src/parse_to_ast.rs index b6f6b55..a65ed0f 100644 --- a/petr-parse/src/parse_to_ast.rs +++ b/petr-parse/src/parse_to_ast.rs @@ -343,6 +343,7 @@ impl Parse for IntrinsicCall { "multiply" => Intrinsic::Multiply, "divide" => Intrinsic::Divide, "malloc" => Intrinsic::Malloc, + "size_of" => Intrinsic::SizeOf, a => todo!("unrecognized intrinsic error: {a:?}"), }; p.token(Token::Intrinsic)?; diff --git a/petr-resolve/src/resolver.rs b/petr-resolve/src/resolver.rs index 7abb264..8c759b8 100644 --- a/petr-resolve/src/resolver.rs +++ b/petr-resolve/src/resolver.rs @@ -51,7 +51,7 @@ pub enum Type { Unit, String, // like `Unit`, but doesn't throw additional type errors to prevent cascading errors. - ErrorRecovery, + ErrorRecovery(Span), Named(TypeId), Generic(Identifier), } @@ -738,7 +738,7 @@ mod tests { Type::Bool => "bool".to_string(), Type::Unit => "()".to_string(), Type::String => "string".to_string(), - Type::ErrorRecovery => "".to_string(), + Type::ErrorRecovery(..) => "".to_string(), Type::Named(id) => { format!("named type {}", resolver.interner.get(resolver.get_type(*id).name.id)) }, diff --git a/petr-typecheck/src/lib.rs b/petr-typecheck/src/lib.rs index b1e60d9..ee06a35 100644 --- a/petr-typecheck/src/lib.rs +++ b/petr-typecheck/src/lib.rs @@ -137,10 +137,13 @@ impl TypeContext { self.constraints.push(TypeConstraint::satisfies(ty1, ty2, span)); } - fn new_variable(&mut self) -> TypeVariable { + fn new_variable( + &mut self, + span: Span, + ) -> TypeVariable { // infer is special -- it knows its own id, mostly for printing let infer_id = self.types.len(); - self.types.insert(PetrType::Infer(infer_id)) + self.types.insert(PetrType::Infer(infer_id, span)) } /// Update a type variable with a new PetrType @@ -180,7 +183,9 @@ pub enum PetrType { ErrorRecovery, List(TypeVariable), /// the usize is just an identifier for use in rendering the type - Infer(usize), + /// the span is the location of the inference, for error reporting if the inference is never + /// resolved + Infer(usize, Span), } #[derive(Clone, PartialEq, Debug, Eq, PartialOrd, Ord)] @@ -230,7 +235,7 @@ impl TypeChecker { return *ty; } } - let fresh_ty = self.fresh_ty_var(); + let fresh_ty = self.fresh_ty_var(id.span); match self.variable_scope.last_mut() { Some(entry) => { entry.insert(*id, fresh_ty); @@ -259,7 +264,7 @@ impl TypeChecker { fn fully_type_check(&mut self) { for (id, decl) in self.resolved.types() { - let ty = self.fresh_ty_var(); + let ty = self.fresh_ty_var(decl.name.span); let variants = decl .variants .iter() @@ -322,16 +327,16 @@ impl TypeChecker { (ErrorRecovery, _) | (_, ErrorRecovery) => (), (Ref(a), _) => self.apply_unify_constraint(a, t2, span), (_, Ref(b)) => self.apply_unify_constraint(t1, b, span), - (Infer(id), Infer(id2)) if id != id2 => { + (Infer(id, _), Infer(id2, _)) if id != id2 => { // if two different inferred types are unified, replace the second with a reference // to the first self.ctx.update_type(t2, Ref(t1)); }, // instantiate the infer type with the known type - (Infer(_), known) => { + (Infer(_, _), known) => { self.ctx.update_type(t1, known); }, - (known, Infer(_)) => { + (known, Infer(_, _)) => { self.ctx.update_type(t2, known); }, // lastly, if no unification rule exists for these two types, it is a mismatch @@ -358,7 +363,7 @@ impl TypeChecker { (Ref(a), _) => self.apply_satisfies_constraint(*a, t2, span), (_, Ref(b)) => self.apply_satisfies_constraint(t1, *b, span), // if t1 is a fully instantiated type, then t2 can be updated to be a reference to t1 - (_known, Infer(_)) => { + (_known, Infer(_, _)) => { self.ctx.update_type(t2, Ref(t1)); }, // (Infer(_), _) | (_, Infer(_)) => Ok(()), @@ -394,8 +399,11 @@ impl TypeChecker { .insert(id, ty); } - pub fn fresh_ty_var(&mut self) -> TypeVariable { - self.ctx.new_variable() + pub fn fresh_ty_var( + &mut self, + span: Span, + ) -> TypeVariable { + self.ctx.new_variable(span) } fn arrow_type( @@ -421,9 +429,9 @@ impl TypeChecker { petr_resolve::Type::Bool => PetrType::Boolean, petr_resolve::Type::Unit => PetrType::Unit, petr_resolve::Type::String => PetrType::String, - petr_resolve::Type::ErrorRecovery => { + petr_resolve::Type::ErrorRecovery(span) => { // unifies to anything, fresh var - return self.fresh_ty_var(); + return self.fresh_ty_var(*span); }, petr_resolve::Type::Named(ty_id) => PetrType::Ref(*self.type_map.get(&ty_id.into()).expect("type did not exist in type map")), petr_resolve::Type::Generic(generic_name) => { @@ -570,6 +578,7 @@ pub enum Intrinsic { Divide(Box, Box), Subtract(Box, Box), Malloc(Box), + SizeOf(Box), } impl std::fmt::Debug for Intrinsic { @@ -584,6 +593,7 @@ impl std::fmt::Debug for Intrinsic { Intrinsic::Divide(lhs, rhs) => write!(f, "@divide({:?}, {:?})", lhs, rhs), Intrinsic::Subtract(lhs, rhs) => write!(f, "@subtract({:?}, {:?})", lhs, rhs), Intrinsic::Malloc(size) => write!(f, "@malloc({:?})", size), + Intrinsic::SizeOf(expr) => write!(f, "@sizeof({:?})", expr), } } } @@ -805,7 +815,6 @@ impl TypeCheck for SpannedItem { ctx: &mut TypeChecker, ) -> Self::Output { use petr_resolve::IntrinsicName::*; - let string_ty = ctx.string(); let kind = match self.item().intrinsic { Puts => { if self.item().args.len() != 1 { @@ -813,7 +822,7 @@ impl TypeCheck for SpannedItem { } // puts takes a single string and returns unit let arg = self.item().args[0].type_check(ctx); - ctx.unify_expr_return(string_ty, &arg); + ctx.unify_expr_return(ctx.string(), &arg); TypedExprKind::Intrinsic { intrinsic: Intrinsic::Puts(Box::new(arg)), ty: ctx.unit(), @@ -834,6 +843,7 @@ impl TypeCheck for SpannedItem { todo!("sub arg len check"); } let (lhs, rhs) = unify_basic_math_op(&self.item().args[0], &self.item().args[1], ctx); + TypedExprKind::Intrinsic { intrinsic: Intrinsic::Subtract(Box::new(lhs), Box::new(rhs)), ty: ctx.int(), @@ -880,6 +890,19 @@ impl TypeCheck for SpannedItem { ty: int_ty, } }, + SizeOf => { + if self.item().args.len() != 1 { + todo!("size_of arg len check"); + } + + let arg = self.item().args[0].type_check(ctx); + + dbg!(&arg); + TypedExprKind::Intrinsic { + intrinsic: Intrinsic::SizeOf(Box::new(arg)), + ty: ctx.int(), + } + }, }; TypedExpr { kind, span: self.span() } @@ -928,8 +951,6 @@ impl TypeCheck for petr_resolve::Function { body, } }) - // in a scope that contains the above names to type variables, check the body - // TODO: introduce scopes here, like in the binder, except with type variables } } @@ -940,18 +961,33 @@ impl TypeCheck for petr_resolve::FunctionCall { &self, ctx: &mut TypeChecker, ) -> Self::Output { - let func_type = *ctx.get_type(self.function); + //let func_type = *ctx.get_type(self.function); + let func_decl = ctx.get_function(&self.function).clone(); + let args = self.args.iter().map(|arg| arg.type_check(ctx)).collect::>(); let mut arg_types = Vec::with_capacity(args.len()); for arg in args.iter() { - arg_types.push(ctx.expr_ty(arg)); + arg_types.push((ctx.expr_ty(arg), arg.span())); } - let arg_type = ctx.arrow_type(arg_types); + let params = func_decl.params.iter().map(|(_, ty)| *ty).collect::>(); + + if arg_types.len() != params.len() { + // TODO: support partial application + ctx.push_error(self.span().with_item(TypeConstraintError::ArgumentCountMismatch { + expected: params.len(), + got: arg_types.len(), + function: ctx.get_symbol(func_decl.name.id).to_string(), + })); + } - ctx.unify(func_type, arg_type, self.span()); + println!("unifying"); + + for ((arg, arg_span), param) in arg_types.iter().zip(params.iter()) { + ctx.unify(*arg, *param, *arg_span); + } } } @@ -1064,7 +1100,7 @@ mod tests { }, PetrType::ErrorRecovery => "error recovery".to_string(), PetrType::List(ty) => format!("[{}]", pretty_print_ty(ty, type_checker)), - PetrType::Infer(id) => format!("t{id}"), + PetrType::Infer(id, _) => format!("t{id}"), } } diff --git a/petr-utils/src/sources.rs b/petr-utils/src/sources.rs index cec989d..b3bd77d 100644 --- a/petr-utils/src/sources.rs +++ b/petr-utils/src/sources.rs @@ -134,6 +134,32 @@ pub struct Span { span: miette::SourceSpan, } +impl PartialOrd for Span { + fn partial_cmp( + &self, + other: &Self, + ) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for Span { + fn cmp( + &self, + other: &Self, + ) -> std::cmp::Ordering { + let self_offset = self.span.offset(); + let self_len = self.span.len(); + let self_source = self.source.0; + + let other_offset = other.span.offset(); + let other_len = other.span.len(); + let other_source = other.source.0; + + (self_offset, self_len, self_source).cmp(&(other_offset, other_len, other_source)) + } +} + impl Span { pub fn new( source: SourceId, diff --git a/petr-vm/src/lib.rs b/petr-vm/src/lib.rs index 3f30fbb01f3a0ac648aeb005d86a9a7867b4ce0c..14bf9678d01e71851433288b2ad42db7ec04ef5f 100644 GIT binary patch delta 241 zcmcavaj<5CJNslE4so5}lA_GKbSta8)N&0?Yc4JYD9B7xP|V3MPc2F<(n~EW$}cL` z(41_`Dy0M!ge!z;fUDw~TrQ_ES%$5O6|8jf0ygQ%!R$fIWx4T_m$FY#E=n!dD=00_ zh%YWF(ojpwFUn0UQPfbX2Fe4atn6!*G$)5}9N;d_tV)f~Pm9mYE19gosXlqGfPgHJ x4Ys(hxFkg{H#Juesz}4cNRw-_gP=4|v{G1NPHC!!g{D&M Date: Tue, 23 Jul 2024 16:22:10 -0400 Subject: [PATCH 02/10] wip --- petr-ir/src/lib.rs | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/petr-ir/src/lib.rs b/petr-ir/src/lib.rs index 119828f..7a61b25 100644 --- a/petr-ir/src/lib.rs +++ b/petr-ir/src/lib.rs @@ -83,7 +83,6 @@ impl Lowerer { Some(monomorphized_entry_point_id) }, }; - println!("my errors are {:?}", lowerer.errors); lowerer.entry_point = monomorphized_entry_point_id; Ok(lowerer) @@ -188,18 +187,6 @@ impl Lowerer { }, FunctionCall { func, args, ty: _ty } => { let mut buf = Vec::with_capacity(args.len()); - - /* - - self.monomorphized_functions.insert(FunctionSignature { - label: *func, - concrete_types: args - .iter() - .map(|(_name, expr)| self.to_ir_type(self.type_checker.expr_ty(expr))) - .collect(), - }); - */ - // push all args onto the stack in order for (_arg_name, arg_expr) in args { let reg = self.fresh_reg(); @@ -270,7 +257,9 @@ impl Lowerer { let mut buf = vec![]; // the memory model for types is currently not finalized, // but for now, it is just sequential memory that is word-aligned + println!("BEFORE1"); let ir_ty = self.to_ir_type(*ty); + println!("AFTER1"); let size_of_aggregate_type = ir_ty.size(); let ReturnDestination::Reg(return_destination) = return_destination; buf.push(IrOpcode::MallocImmediate(return_destination, size_of_aggregate_type)); @@ -286,7 +275,9 @@ impl Lowerer { let arg_ty = self.type_checker.expr_ty(arg); + println!("BEFORE2"); current_size_offset += self.to_ir_type(arg_ty).size().num_bytes() as u64; + println!("AFTER2"); } Ok(buf) }, @@ -485,6 +476,7 @@ impl Lowerer { } } +#[derive(Debug)] struct MonomorphizedFunction { id: FunctionId, params: Vec<(Identifier, IrTy)>, From 5ae2137065dce04f869c3c4920f4da7883d79fcd Mon Sep 17 00:00:00 2001 From: sezna Date: Wed, 24 Jul 2024 21:20:57 -0400 Subject: [PATCH 03/10] wip: move monomorphization to type checker --- petr-ir/src/lib.rs | 90 ++++++++++++++++++-------------------- petr-typecheck/src/lib.rs | 50 ++++++++++++++++++--- petr-vm/src/lib.rs | Bin 15942 -> 16416 bytes 3 files changed, 86 insertions(+), 54 deletions(-) diff --git a/petr-ir/src/lib.rs b/petr-ir/src/lib.rs index 7a61b25..8e30973 100644 --- a/petr-ir/src/lib.rs +++ b/petr-ir/src/lib.rs @@ -9,7 +9,7 @@ use std::{collections::BTreeMap, rc::Rc}; -use petr_typecheck::{FunctionId, TypeChecker, TypeVariable, TypedExpr, TypedExprKind}; +use petr_typecheck::{FunctionSignature, PetrType, TypeChecker, TypeVariable, TypedExpr, TypedExprKind}; use petr_utils::{idx_map_key, Identifier, IndexMap, SpannedItem, SymbolId}; mod error; @@ -29,12 +29,6 @@ pub struct Function { body: Vec, } -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] -pub struct FunctionSignature { - label: FunctionId, - concrete_types: Vec, -} - idx_map_key!(MonomorphizedFunctionId); pub type Result = std::result::Result>; @@ -62,9 +56,7 @@ impl Lowerer { pub fn new(type_checker: TypeChecker) -> Result { // if there is an entry point, set that // set entry point to func named main - let entry_point = type_checker - .functions() - .find(|(_func_id, func)| &*type_checker.get_symbol(func.name.id) == "main"); + let entry_point = type_checker.get_main_function(); let mut lowerer = Self { data_section: IndexMap::default(), @@ -79,7 +71,7 @@ impl Lowerer { let monomorphized_entry_point_id = match entry_point { None => None, Some((id, _func)) => { - let monomorphized_entry_point_id = lowerer.monomorphize_function(MonomorphizedFunction { id, params: vec![] })?; + let monomorphized_entry_point_id = lowerer.monomorphize_function((id, vec![].into_boxed_slice()))?; Some(monomorphized_entry_point_id) }, }; @@ -111,13 +103,17 @@ impl Lowerer { /// this lowers a function declaration. fn monomorphize_function( &mut self, - func: MonomorphizedFunction, + func: FunctionSignature, ) -> Result { - if let Some(previously_monomorphized_definition) = self.monomorphized_functions.iter().find(|(_id, (sig, _))| *sig == func.signature()) { + if let Some(previously_monomorphized_definition) = self.monomorphized_functions.iter().find(|(_id, (sig, _))| *sig == func) { return Ok(previously_monomorphized_definition.0); } - let function_definition_body = self.type_checker.get_function(&func.id).body.clone(); + println!("monomorphizing function {:?}", func); + println!("func name is {}", self.type_checker.get_function(&func.0).name.id); + let func_def = self.type_checker.get_monomorphized_function(&func).clone(); + + let function_definition_body = self.type_checker.get_function(&func.0).body.clone(); let mut buf = vec![]; self.with_variable_context(|ctx| -> Result<_> { @@ -126,7 +122,8 @@ impl Lowerer { // When we lower a function call, we push them onto the stack from first to last. Since // the stack is FILO, we reverse that order here. - for (param_name, param_ty) in func.params.iter().rev() { + for (param_ty, (param_name, _)) in func.1.iter().zip(func_def.params).rev() { + let param_ty = ctx.petr_type_to_ir_type(param_ty.clone()); // in order, assign parameters to registers if param_ty.fits_in_reg() { // load from stack into register @@ -138,7 +135,7 @@ impl Lowerer { buf.push(IrOpcode::StackPop(ty_reg)); // insert param into mapping - ctx.insert_var(param_name, param_reg); + ctx.insert_var(¶m_name, param_reg); } else { todo!("make reg a ptr to the value") } @@ -146,7 +143,12 @@ impl Lowerer { let return_reg = ctx.fresh_reg(); let return_dest = ReturnDestination::Reg(return_reg); - let mut expr_body = ctx.lower_expr(&function_definition_body, return_dest)?; + println!("I have {} errors", ctx.errors.len()); + let mut expr_body = ctx.lower_expr(&function_definition_body, return_dest).map_err(|e| { + println!("error is {:?}", e); + e + })?; + println!("Now I have {} errors", ctx.errors.len()); buf.append(&mut expr_body); // load return value into func return register @@ -155,13 +157,7 @@ impl Lowerer { // jump back to caller buf.push(IrOpcode::Return()); - Ok(ctx.monomorphized_functions.insert(( - FunctionSignature { - label: func.id, - concrete_types: func.params.iter().map(|(_name, ty)| ty.clone()).collect(), - }, - Function { body: buf }, - ))) + Ok(ctx.monomorphized_functions.insert((func, Function { body: buf }))) }) } @@ -186,32 +182,27 @@ impl Lowerer { }) }, FunctionCall { func, args, ty: _ty } => { - let mut buf = Vec::with_capacity(args.len()); + let mut buf = Vec::new(); // push all args onto the stack in order - for (_arg_name, arg_expr) in args { + + let mut arg_types = Vec::with_capacity(args.len()); + for (arg_name, arg_expr) in args { let reg = self.fresh_reg(); let mut expr = self.lower_expr(arg_expr, ReturnDestination::Reg(reg))?; let arg_ty = self.type_checker.expr_ty(arg_expr); - expr.push(IrOpcode::StackPush(TypedReg { - ty: self.to_ir_type(arg_ty), - reg, - })); + let petr_ty = self.type_checker.look_up_variable(arg_ty); + arg_types.push((*arg_name, petr_ty.clone())); + let ir_ty = self.petr_type_to_ir_type(petr_ty.clone()); + expr.push(IrOpcode::StackPush(TypedReg { ty: ir_ty, reg })); buf.append(&mut expr); } // push current PC onto the stack buf.push(IrOpcode::PushPc()); - let params_as_ir_types = args - .iter() - .map(|(name, expr)| (*name, self.to_ir_type(self.type_checker.expr_ty(expr)))) - .collect::>(); + let arg_petr_types = arg_types.iter().map(|(_name, ty)| ty.clone()).collect::>(); - // TODO deduplicate monomorphized functions - let monomorphized_func_id = self.monomorphize_function(MonomorphizedFunction { - id: *func, - params: params_as_ir_types, - })?; + 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)); @@ -297,12 +288,11 @@ impl Lowerer { }) } - fn to_ir_type( + fn petr_type_to_ir_type( &mut self, - param_ty: TypeVariable, + ty: PetrType, ) -> IrTy { use petr_typecheck::PetrType::*; - let ty = self.type_checker.look_up_variable(param_ty).clone(); match ty { Unit => IrTy::Unit, Integer => IrTy::Int64, @@ -332,6 +322,14 @@ impl Lowerer { } } + fn to_ir_type( + &mut self, + param_ty: TypeVariable, + ) -> IrTy { + let ty = self.type_checker.look_up_variable(param_ty).clone(); + self.petr_type_to_ir_type(ty) + } + fn lower_intrinsic( &mut self, intrinsic: &petr_typecheck::Intrinsic, @@ -476,12 +474,7 @@ impl Lowerer { } } -#[derive(Debug)] -struct MonomorphizedFunction { - id: FunctionId, - params: Vec<(Identifier, IrTy)>, -} - +/* impl MonomorphizedFunction { fn signature(&self) -> FunctionSignature { FunctionSignature { @@ -490,6 +483,7 @@ impl MonomorphizedFunction { } } } +*/ enum ReturnDestination { Reg(Reg), diff --git a/petr-typecheck/src/lib.rs b/petr-typecheck/src/lib.rs index adc71e2..dd4bb71 100644 --- a/petr-typecheck/src/lib.rs +++ b/petr-typecheck/src/lib.rs @@ -156,9 +156,12 @@ impl TypeContext { } } +pub type FunctionSignature = (FunctionId, Box<[PetrType]>); + pub struct TypeChecker { ctx: TypeContext, type_map: BTreeMap, + monomorphized_functions: BTreeMap, typed_functions: BTreeMap, errors: Vec, resolved: QueryableResolvedItems, @@ -288,11 +291,26 @@ impl TypeChecker { self.type_map.insert(id.into(), ty); self.typed_functions.insert(id, typed_function); } + // type check the main func with no params + let main_func = self.get_main_function(); + // construct a function call for the main function, if one exists + if let Some((id, func)) = main_func { + let call = petr_resolve::FunctionCall { + function: dbg!(id), + args: vec![], + span: func.name.span, + }; + call.type_check(self); + } // we have now collected our constraints and can solve for them self.apply_constraints(); } + pub fn get_main_function(&self) -> Option<(FunctionId, Function)> { + self.functions().find(|(_, func)| &*self.get_symbol(func.name.id) == "main") + } + /// iterate through each constraint and transform the underlying types to satisfy them /// - unification tries to collapse two types into one /// - satisfaction tries to make one type satisfy the constraints of another, although type @@ -366,7 +384,6 @@ impl TypeChecker { (_known, Infer(_, _)) => { self.ctx.update_type(t2, Ref(t1)); }, - // (Infer(_), _) | (_, Infer(_)) => Ok(()), (a, b) => { self.push_error(span.with_item(TypeConstraintError::FailedToSatisfy(a.clone(), b.clone()))); }, @@ -382,6 +399,7 @@ impl TypeChecker { typed_functions: Default::default(), resolved, variable_scope: Default::default(), + monomorphized_functions: Default::default(), }; type_checker.fully_type_check(); @@ -503,10 +521,26 @@ impl TypeChecker { } pub fn get_function( - &self, + &mut self, id: &FunctionId, + ) -> Function { + println!("looking for function {:?}", id); + if let Some(func) = self.typed_functions.get(id) { + return func.clone(); + } + + // if the function hasn't been type checked yet, type check it + let func = self.get_untyped_function(*id).clone(); + let type_checked = func.type_check(self); + self.typed_functions.insert(*id, type_checked.clone()); + type_checked + } + + pub fn get_monomorphized_function( + &self, + id: &(FunctionId, Box<[PetrType]>), ) -> &Function { - self.typed_functions.get(id).expect("invariant: should exist") + self.monomorphized_functions.get(id).expect("invariant: should exist") } // TODO unideal clone @@ -739,6 +773,7 @@ impl TypeCheck for Expr { arg_types.push(arg_ty); args.push((*param_name, arg_expr)); } + (*call).type_check(ctx); TypedExprKind::FunctionCall { func: call.function, args, @@ -961,7 +996,6 @@ impl TypeCheck for petr_resolve::FunctionCall { &self, ctx: &mut TypeChecker, ) -> Self::Output { - //let func_type = *ctx.get_type(self.function); let func_decl = ctx.get_function(&self.function).clone(); let args = self.args.iter().map(|arg| arg.type_check(ctx)).collect::>(); @@ -983,11 +1017,15 @@ impl TypeCheck for petr_resolve::FunctionCall { })); } - println!("unifying"); - + // unify all of the arg types with the param types for ((arg, arg_span), param) in arg_types.iter().zip(params.iter()) { ctx.unify(*arg, *param, *arg_span); } + + let concrete_arg_types: Vec = arg_types.iter().map(|(arg, _span)| ctx.look_up_variable(*arg).clone()).collect(); + + ctx.monomorphized_functions + .insert((self.function, concrete_arg_types.into_boxed_slice()), func_decl); } } diff --git a/petr-vm/src/lib.rs b/petr-vm/src/lib.rs index 27264937dfd5af2b53d254c69233270b580f1fff..34387742b937fda3c40d210e3d1aee1ae9a11cd6 100644 GIT binary patch delta 250 zcmX?Bv!H=-!&|P&Y&^nZ#hF#9@%d@-iRtM@sp*L&sqrP11*wxa@~fzU7z%Fr`3klm z(kZb>AyGj+Gp|G;2|^`vj! z;E|eqfs38dc=88svB~QML}UsQi;GJ#itg(MQ&4x*R47U Date: Wed, 24 Jul 2024 21:58:29 -0400 Subject: [PATCH 04/10] progress -- need to debug failed to satisfy calls --- petr-typecheck/src/lib.rs | 87 ++++++++++++-------------------------- petr-vm/src/lib.rs | Bin 16416 -> 16436 bytes 2 files changed, 26 insertions(+), 61 deletions(-) diff --git a/petr-typecheck/src/lib.rs b/petr-typecheck/src/lib.rs index dd4bb71..48b9d08 100644 --- a/petr-typecheck/src/lib.rs +++ b/petr-typecheck/src/lib.rs @@ -296,7 +296,7 @@ impl TypeChecker { // construct a function call for the main function, if one exists if let Some((id, func)) = main_func { let call = petr_resolve::FunctionCall { - function: dbg!(id), + function: id, args: vec![], span: func.name.span, }; @@ -511,15 +511,6 @@ impl TypeChecker { self.resolved.get_function(function) } - /// Given a symbol ID, look it up in the interner and realize it as a - /// string. - fn realize_symbol( - &self, - id: petr_utils::SymbolId, - ) -> Rc { - self.resolved.interner.get(id) - } - pub fn get_function( &mut self, id: &FunctionId, @@ -747,39 +738,7 @@ impl TypeCheck for Expr { } } }, - ExprKind::FunctionCall(call) => { - // unify args with params - // return the func return type - let func_decl = ctx.get_untyped_function(call.function).clone(); - if call.args.len() != func_decl.params.len() { - ctx.push_error(call.span().with_item(TypeConstraintError::ArgumentCountMismatch { - expected: func_decl.params.len(), - got: call.args.len(), - function: ctx.realize_symbol(func_decl.name.id).to_string(), - })); - return TypedExpr { - kind: TypedExprKind::ErrorRecovery(self.span), - span: self.span, - }; - } - let mut args = Vec::with_capacity(call.args.len()); - let mut arg_types = Vec::with_capacity(call.args.len()); - - for (arg, (param_name, param)) in call.args.iter().zip(func_decl.params.iter()) { - let arg_expr = arg.type_check(ctx); - let param_ty = ctx.to_type_var(param); - let arg_ty = ctx.expr_ty(&arg_expr); - ctx.satisfies(arg_ty, param_ty, arg_expr.span()); - arg_types.push(arg_ty); - args.push((*param_name, arg_expr)); - } - (*call).type_check(ctx); - TypedExprKind::FunctionCall { - func: call.function, - args, - ty: ctx.to_type_var(&func_decl.return_type), - } - }, + ExprKind::FunctionCall(call) => (*call).type_check(ctx), ExprKind::Unit => TypedExprKind::Unit, ExprKind::ErrorRecovery => TypedExprKind::ErrorRecovery(self.span), ExprKind::Variable { name, ty } => { @@ -932,7 +891,6 @@ impl TypeCheck for SpannedItem { let arg = self.item().args[0].type_check(ctx); - dbg!(&arg); TypedExprKind::Intrinsic { intrinsic: Intrinsic::SizeOf(Box::new(arg)), ty: ctx.int(), @@ -990,7 +948,7 @@ impl TypeCheck for petr_resolve::Function { } impl TypeCheck for petr_resolve::FunctionCall { - type Output = (); + type Output = TypedExprKind; fn type_check( &self, @@ -998,34 +956,41 @@ impl TypeCheck for petr_resolve::FunctionCall { ) -> Self::Output { let func_decl = ctx.get_function(&self.function).clone(); - let args = self.args.iter().map(|arg| arg.type_check(ctx)).collect::>(); - - let mut arg_types = Vec::with_capacity(args.len()); - - for arg in args.iter() { - arg_types.push((ctx.expr_ty(arg), arg.span())); - } - - let params = func_decl.params.iter().map(|(_, ty)| *ty).collect::>(); - - if arg_types.len() != params.len() { + if self.args.len() != func_decl.params.len() { // TODO: support partial application ctx.push_error(self.span().with_item(TypeConstraintError::ArgumentCountMismatch { - expected: params.len(), - got: arg_types.len(), + expected: func_decl.params.len(), + got: self.args.len(), function: ctx.get_symbol(func_decl.name.id).to_string(), })); + return TypedExprKind::ErrorRecovery(self.span()); } + let mut args: Vec<(Identifier, TypedExpr, TypeVariable)> = Vec::with_capacity(self.args.len()); + // unify all of the arg types with the param types - for ((arg, arg_span), param) in arg_types.iter().zip(params.iter()) { - ctx.unify(*arg, *param, *arg_span); + for (arg, (name, param_ty)) in self.args.iter().zip(func_decl.params.iter()) { + let arg = arg.type_check(ctx); + let arg_ty = ctx.expr_ty(&arg); + ctx.satisfies(*param_ty, arg_ty, arg.span()); + args.push((*name, arg, arg_ty)); } - let concrete_arg_types: Vec = arg_types.iter().map(|(arg, _span)| ctx.look_up_variable(*arg).clone()).collect(); + // unify declared return type with body return type + let declared_return_type = func_decl.return_ty; + + ctx.unify_expr_return(declared_return_type, &func_decl.body); + + let concrete_arg_types: Vec = args.iter().map(|(_, _, ty)| ctx.look_up_variable(*ty).clone()).collect(); ctx.monomorphized_functions .insert((self.function, concrete_arg_types.into_boxed_slice()), func_decl); + + TypedExprKind::FunctionCall { + func: self.function, + args: args.into_iter().map(|(name, expr, _)| (name, expr)).collect(), + ty: declared_return_type, + } } } diff --git a/petr-vm/src/lib.rs b/petr-vm/src/lib.rs index 34387742b937fda3c40d210e3d1aee1ae9a11cd6..630b4e614589c44f9d7ebca307ba3c7ba9124829 100644 GIT binary patch delta 29 kcmZ3`z__J>al Date: Wed, 24 Jul 2024 22:15:37 -0400 Subject: [PATCH 05/10] lol back to original error --- petr-typecheck/src/lib.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/petr-typecheck/src/lib.rs b/petr-typecheck/src/lib.rs index 48b9d08..0516d3e 100644 --- a/petr-typecheck/src/lib.rs +++ b/petr-typecheck/src/lib.rs @@ -381,9 +381,11 @@ impl TypeChecker { (Ref(a), _) => self.apply_satisfies_constraint(*a, t2, span), (_, Ref(b)) => self.apply_satisfies_constraint(t1, *b, span), // if t1 is a fully instantiated type, then t2 can be updated to be a reference to t1 - (_known, Infer(_, _)) => { + (Unit | Integer | Boolean | UserDefined { .. } | String | Arrow(..) | List(..), Infer(_, _)) => { self.ctx.update_type(t2, Ref(t1)); }, + // if we are trying to satisfy an inferred type with no bounds, this is ok + (Infer(..), _) => (), (a, b) => { self.push_error(span.with_item(TypeConstraintError::FailedToSatisfy(a.clone(), b.clone()))); }, From 624d7bea98e5c71aeaba447841a0357d04337403 Mon Sep 17 00:00:00 2001 From: sezna Date: Thu, 25 Jul 2024 09:35:49 -0400 Subject: [PATCH 06/10] start unit testing monomorphized functions --- petr-ir/src/lib.rs | 15 +------ petr-typecheck/src/lib.rs | 93 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 93 insertions(+), 15 deletions(-) diff --git a/petr-ir/src/lib.rs b/petr-ir/src/lib.rs index 8e30973..a165d5f 100644 --- a/petr-ir/src/lib.rs +++ b/petr-ir/src/lib.rs @@ -109,8 +109,6 @@ impl Lowerer { return Ok(previously_monomorphized_definition.0); } - println!("monomorphizing function {:?}", func); - println!("func name is {}", self.type_checker.get_function(&func.0).name.id); let func_def = self.type_checker.get_monomorphized_function(&func).clone(); let function_definition_body = self.type_checker.get_function(&func.0).body.clone(); @@ -143,12 +141,7 @@ impl Lowerer { let return_reg = ctx.fresh_reg(); let return_dest = ReturnDestination::Reg(return_reg); - println!("I have {} errors", ctx.errors.len()); - let mut expr_body = ctx.lower_expr(&function_definition_body, return_dest).map_err(|e| { - println!("error is {:?}", e); - e - })?; - println!("Now I have {} errors", ctx.errors.len()); + let mut expr_body = ctx.lower_expr(&function_definition_body, return_dest).map_err(|e| e)?; buf.append(&mut expr_body); // load return value into func return register @@ -248,9 +241,7 @@ impl Lowerer { let mut buf = vec![]; // the memory model for types is currently not finalized, // but for now, it is just sequential memory that is word-aligned - println!("BEFORE1"); let ir_ty = self.to_ir_type(*ty); - println!("AFTER1"); let size_of_aggregate_type = ir_ty.size(); let ReturnDestination::Reg(return_destination) = return_destination; buf.push(IrOpcode::MallocImmediate(return_destination, size_of_aggregate_type)); @@ -266,9 +257,7 @@ impl Lowerer { let arg_ty = self.type_checker.expr_ty(arg); - println!("BEFORE2"); current_size_offset += self.to_ir_type(arg_ty).size().num_bytes() as u64; - println!("AFTER2"); } Ok(buf) }, @@ -316,6 +305,7 @@ impl Lowerer { ErrorRecovery => todo!(), List(_) => todo!(), Infer(_, span) => { + println!("Unable to infer ty: {ty:?}"); self.errors.push(span.with_item(LoweringError::UnableToInferType)); IrTy::Unit }, @@ -458,7 +448,6 @@ impl Lowerer { pc += 1; } } - println!("in pretty print, my errors are {:?}", self.errors); if !self.errors.is_empty() { result.push_str("\n____ERRORS____"); diff --git a/petr-typecheck/src/lib.rs b/petr-typecheck/src/lib.rs index 0516d3e..6dd7dbd 100644 --- a/petr-typecheck/src/lib.rs +++ b/petr-typecheck/src/lib.rs @@ -635,6 +635,51 @@ impl TypedExpr { pub fn span(&self) -> Span { self.span } + + /// for each type variable in the mapping, replace all references to it with the new petrtype + fn apply_new_type_mapping( + &mut self, + ctx: &mut TypeChecker, + type_mapping: &BTreeMap, + ) { + use TypedExprKind::*; + + match self.kind { + FunctionCall { ref mut args, .. } => { + for (_, ref mut arg) in args { + arg.apply_new_type_mapping(ctx, type_mapping); + } + }, + Literal { .. } => (), + List { ref mut elements, .. } => { + for ref mut elem in elements { + elem.apply_new_type_mapping(ctx, type_mapping); + } + }, + Unit => (), + Variable { ref mut ty, .. } => { + if let Some(new_ty) = type_mapping.get(&ty) { + *ty = *new_ty; + } + }, + Intrinsic { .. } => (), + ErrorRecovery(..) => (), + ExprWithBindings { + ref mut bindings, + ref mut expression, + } => { + for (_, ref mut expr) in bindings { + expr.apply_new_type_mapping(ctx, type_mapping); + } + expression.apply_new_type_mapping(ctx, type_mapping); + }, + TypeConstructor { ref mut args, .. } => { + for ref mut arg in args { + arg.apply_new_type_mapping(ctx, type_mapping); + } + }, + } + } } #[derive(Clone, Debug)] @@ -956,7 +1001,7 @@ impl TypeCheck for petr_resolve::FunctionCall { &self, ctx: &mut TypeChecker, ) -> Self::Output { - let func_decl = ctx.get_function(&self.function).clone(); + let mut func_decl = ctx.get_function(&self.function).clone(); if self.args.len() != func_decl.params.len() { // TODO: support partial application @@ -977,6 +1022,16 @@ impl TypeCheck for petr_resolve::FunctionCall { ctx.satisfies(*param_ty, arg_ty, arg.span()); args.push((*name, arg, arg_ty)); } + // build a mapping to replace all parameter types with their monomorphized concrete types + let type_mapping: BTreeMap = func_decl + .params + .iter() + .map(|(_, ty)| *ty) + .zip(args.iter().map(|(_, _, ty)| *ty)) + .collect(); + + // update all arg types in the decl with the concrete types + func_decl.apply_new_type_mapping(ctx, &type_mapping); // unify declared return type with body return type let declared_return_type = func_decl.return_ty; @@ -996,6 +1051,26 @@ impl TypeCheck for petr_resolve::FunctionCall { } } +impl Function { + fn apply_new_type_mapping( + &mut self, + ctx: &mut TypeChecker, + type_mapping: &BTreeMap, + ) { + for (_, ref mut param_ty) in self.params.iter_mut() { + let mut petr_ty = ctx.look_up_variable(*param_ty); + while let PetrType::Ref(ty) = petr_ty { + if let Some(new_ty) = type_mapping.get(ty) { + println!("replacing arg type"); + *param_ty = *new_ty + } + petr_ty = ctx.look_up_variable(*ty); + } + } + self.body.apply_new_type_mapping(ctx, type_mapping); + } +} + #[cfg(test)] mod tests { use expect_test::{expect, Expect}; @@ -1054,10 +1129,24 @@ mod tests { s.push('\n'); }, } - s.push('\n'); } + if !type_checker.monomorphized_functions.is_empty() { + s.push_str("\n__MONOMORPHIZED FUNCTIONS__"); + } + + for func in type_checker.monomorphized_functions.values() { + let func_name = type_checker.resolved.interner.get(func.name.id); + let arg_types = func.params.iter().map(|(_, ty)| pretty_print_ty(ty, &type_checker)).collect::>(); + s.push_str(&format!( + "\nfn {}({:?}) -> {}", + func_name, + arg_types, + pretty_print_ty(&func.return_ty, &type_checker) + )); + } + if !type_checker.errors.is_empty() { s.push_str("\nErrors:\n"); for error in type_checker.errors { From e910703d933ec37c4ffb9c6e3277526e221acbcd Mon Sep 17 00:00:00 2001 From: sezna Date: Thu, 25 Jul 2024 09:49:24 -0400 Subject: [PATCH 07/10] track monomorphized definitions in typechecker tests --- petr-typecheck/src/lib.rs | 126 +++++++++++++------------------------- 1 file changed, 44 insertions(+), 82 deletions(-) diff --git a/petr-typecheck/src/lib.rs b/petr-typecheck/src/lib.rs index 6dd7dbd..71137bc 100644 --- a/petr-typecheck/src/lib.rs +++ b/petr-typecheck/src/lib.rs @@ -635,51 +635,6 @@ impl TypedExpr { pub fn span(&self) -> Span { self.span } - - /// for each type variable in the mapping, replace all references to it with the new petrtype - fn apply_new_type_mapping( - &mut self, - ctx: &mut TypeChecker, - type_mapping: &BTreeMap, - ) { - use TypedExprKind::*; - - match self.kind { - FunctionCall { ref mut args, .. } => { - for (_, ref mut arg) in args { - arg.apply_new_type_mapping(ctx, type_mapping); - } - }, - Literal { .. } => (), - List { ref mut elements, .. } => { - for ref mut elem in elements { - elem.apply_new_type_mapping(ctx, type_mapping); - } - }, - Unit => (), - Variable { ref mut ty, .. } => { - if let Some(new_ty) = type_mapping.get(&ty) { - *ty = *new_ty; - } - }, - Intrinsic { .. } => (), - ErrorRecovery(..) => (), - ExprWithBindings { - ref mut bindings, - ref mut expression, - } => { - for (_, ref mut expr) in bindings { - expr.apply_new_type_mapping(ctx, type_mapping); - } - expression.apply_new_type_mapping(ctx, type_mapping); - }, - TypeConstructor { ref mut args, .. } => { - for ref mut arg in args { - arg.apply_new_type_mapping(ctx, type_mapping); - } - }, - } - } } #[derive(Clone, Debug)] @@ -1001,7 +956,7 @@ impl TypeCheck for petr_resolve::FunctionCall { &self, ctx: &mut TypeChecker, ) -> Self::Output { - let mut func_decl = ctx.get_function(&self.function).clone(); + let func_decl = ctx.get_function(&self.function).clone(); if self.args.len() != func_decl.params.len() { // TODO: support partial application @@ -1022,26 +977,41 @@ impl TypeCheck for petr_resolve::FunctionCall { ctx.satisfies(*param_ty, arg_ty, arg.span()); args.push((*name, arg, arg_ty)); } - // build a mapping to replace all parameter types with their monomorphized concrete types - let type_mapping: BTreeMap = func_decl - .params - .iter() - .map(|(_, ty)| *ty) - .zip(args.iter().map(|(_, _, ty)| *ty)) - .collect(); - // update all arg types in the decl with the concrete types - func_decl.apply_new_type_mapping(ctx, &type_mapping); + let concrete_arg_types: Vec = args.iter().map(|(_, _, ty)| ctx.look_up_variable(*ty).clone()).collect(); + + let signature = (self.function, concrete_arg_types.clone().into_boxed_slice()); + // now that we know the argument types, check if this signature has been monomorphized + // already + if ctx.monomorphized_functions.contains_key(&signature) { + return TypedExprKind::FunctionCall { + func: self.function, + args: args.into_iter().map(|(name, expr, _)| (name, expr)).collect(), + ty: func_decl.return_ty, + }; + } // unify declared return type with body return type let declared_return_type = func_decl.return_ty; ctx.unify_expr_return(declared_return_type, &func_decl.body); - let concrete_arg_types: Vec = args.iter().map(|(_, _, ty)| ctx.look_up_variable(*ty).clone()).collect(); + // to create a monomorphized func decl, we don't actually have to update all of the types + // throughout the entire definition. We only need to update the parameter types. + let mut monomorphized_func_decl = Function { + name: func_decl.name, + params: func_decl.params.clone(), + return_ty: declared_return_type, + body: func_decl.body.clone(), + }; + + // update the parameter types to be the concrete types + for (param, concrete_ty) in monomorphized_func_decl.params.iter_mut().zip(concrete_arg_types.iter()) { + let param_ty = ctx.insert_type(concrete_ty.clone()); + param.1 = param_ty; + } - ctx.monomorphized_functions - .insert((self.function, concrete_arg_types.into_boxed_slice()), func_decl); + ctx.monomorphized_functions.insert(signature, monomorphized_func_decl); TypedExprKind::FunctionCall { func: self.function, @@ -1051,26 +1021,6 @@ impl TypeCheck for petr_resolve::FunctionCall { } } -impl Function { - fn apply_new_type_mapping( - &mut self, - ctx: &mut TypeChecker, - type_mapping: &BTreeMap, - ) { - for (_, ref mut param_ty) in self.params.iter_mut() { - let mut petr_ty = ctx.look_up_variable(*param_ty); - while let PetrType::Ref(ty) = petr_ty { - if let Some(new_ty) = type_mapping.get(ty) { - println!("replacing arg type"); - *param_ty = *new_ty - } - petr_ty = ctx.look_up_variable(*ty); - } - } - self.body.apply_new_type_mapping(ctx, type_mapping); - } -} - #[cfg(test)] mod tests { use expect_test::{expect, Expect}; @@ -1316,7 +1266,9 @@ mod tests { fn foo: (MyType → MyComposedType) function call to functionid2 with args: someField: MyType, returns MyComposedType - "#]], + + __MONOMORPHIZED FUNCTIONS__ + fn firstVariant(["MyType"]) -> MyComposedType"#]], ); } @@ -1372,7 +1324,9 @@ mod tests { fn my_func: unit intrinsic: @puts(function call to functionid0 with args: ) - "#]], + + __MONOMORPHIZED FUNCTIONS__ + fn string_literal([]) -> string"#]], ); } @@ -1438,6 +1392,8 @@ mod tests { intrinsic: @puts(function call to functionid0 with args: ) + __MONOMORPHIZED FUNCTIONS__ + fn bool_literal([]) -> bool Errors: SpannedItem UnificationFailure(String, Boolean) [Span { source: SourceId(0), span: SourceSpan { offset: SourceOffset(110), length: 14 } }] "#]], @@ -1468,7 +1424,10 @@ 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"#]], ); } #[test] @@ -1534,7 +1493,10 @@ 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"#]], ) } } From 2fdbd702d415f2e853f1a25a6c81858b04d440db Mon Sep 17 00:00:00 2001 From: sezna Date: Thu, 25 Jul 2024 10:37:31 -0400 Subject: [PATCH 08/10] figured it out, it was using the wrong function definition --- petr-ir/src/lib.rs | 4 +--- petr-typecheck/src/lib.rs | 45 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/petr-ir/src/lib.rs b/petr-ir/src/lib.rs index a165d5f..33b4f95 100644 --- a/petr-ir/src/lib.rs +++ b/petr-ir/src/lib.rs @@ -111,8 +111,6 @@ impl Lowerer { let func_def = self.type_checker.get_monomorphized_function(&func).clone(); - let function_definition_body = self.type_checker.get_function(&func.0).body.clone(); - let mut buf = vec![]; self.with_variable_context(|ctx| -> Result<_> { // Pop parameters off the stack in reverse order -- the last parameter for the function @@ -141,7 +139,7 @@ impl Lowerer { let return_reg = ctx.fresh_reg(); let return_dest = ReturnDestination::Reg(return_reg); - let mut expr_body = ctx.lower_expr(&function_definition_body, return_dest).map_err(|e| e)?; + let mut expr_body = ctx.lower_expr(&func_def.body, return_dest).map_err(|e| e)?; buf.append(&mut expr_body); // load return value into func return register diff --git a/petr-typecheck/src/lib.rs b/petr-typecheck/src/lib.rs index 71137bc..1d4b8c1 100644 --- a/petr-typecheck/src/lib.rs +++ b/petr-typecheck/src/lib.rs @@ -517,7 +517,6 @@ impl TypeChecker { &mut self, id: &FunctionId, ) -> Function { - println!("looking for function {:?}", id); if let Some(func) = self.typed_functions.get(id) { return func.clone(); } @@ -1011,6 +1010,14 @@ impl TypeCheck for petr_resolve::FunctionCall { param.1 = param_ty; } + // if there are any variable exprs in the body, update those ref types + let mut num_replacements = 0; + replace_var_reference_types( + &mut monomorphized_func_decl.body.kind, + &monomorphized_func_decl.params, + &mut num_replacements, + ); + ctx.monomorphized_functions.insert(signature, monomorphized_func_decl); TypedExprKind::FunctionCall { @@ -1021,6 +1028,42 @@ impl TypeCheck for petr_resolve::FunctionCall { } } +fn replace_var_reference_types( + expr: &mut TypedExprKind, + params: &Vec<(Identifier, TypeVariable)>, + num_replacements: &mut usize, +) { + match expr { + TypedExprKind::Variable { ref mut ty, name } => { + if let Some((_param_name, ty_var)) = params.iter().find(|(param_name, _)| param_name.id == name.id) { + *num_replacements += 1; + *ty = *ty_var; + } + }, + TypedExprKind::FunctionCall { args, .. } => { + for (_, arg) in args { + replace_var_reference_types(&mut arg.kind, params, num_replacements); + } + }, + TypedExprKind::Intrinsic { intrinsic, .. } => { + use Intrinsic::*; + match intrinsic { + // intrinsics which take one arg, grouped for convenience + Puts(a) | Malloc(a) | SizeOf(a) => { + 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) => { + replace_var_reference_types(&mut a.kind, params, num_replacements); + replace_var_reference_types(&mut b.kind, params, num_replacements); + }, + } + }, + // TODO other expr kinds like bindings + _ => (), + } +} + #[cfg(test)] mod tests { use expect_test::{expect, Expect}; From f97b67b332ed9959602c0cfdb8d4a586a3b60874 Mon Sep 17 00:00:00 2001 From: sezna Date: Thu, 25 Jul 2024 10:43:35 -0400 Subject: [PATCH 09/10] Clippy --- petr-ir/src/lib.rs | 3 ++- petr-vm/src/lib.rs | Bin 16436 -> 16552 bytes 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/petr-ir/src/lib.rs b/petr-ir/src/lib.rs index 33b4f95..95ca0a6 100644 --- a/petr-ir/src/lib.rs +++ b/petr-ir/src/lib.rs @@ -139,7 +139,7 @@ impl Lowerer { let return_reg = ctx.fresh_reg(); let return_dest = ReturnDestination::Reg(return_reg); - let mut expr_body = ctx.lower_expr(&func_def.body, return_dest).map_err(|e| e)?; + let mut expr_body = ctx.lower_expr(&func_def.body, return_dest)?; buf.append(&mut expr_body); // load return value into func return register @@ -310,6 +310,7 @@ impl Lowerer { } } + #[allow(clippy::wrong_self_convention)] fn to_ir_type( &mut self, param_ty: TypeVariable, diff --git a/petr-vm/src/lib.rs b/petr-vm/src/lib.rs index 630b4e614589c44f9d7ebca307ba3c7ba9124829..6455bc7f072f822bd8538f2f3e71be3169455686 100644 GIT binary patch delta 207 zcmdnez__B3al>1#$qj<69{O4erFqHuxw)x%B?{#ksd);C>FGtO>4_z&3MG{VsS3HN zx%owv3OR|D`K3S=`9;~qTnZ4N99@!HToMcBPX5WIHJOE1d~&4#&txlZbrz74$%)+3 zlPkEDH9(xY;*u0SplN!=nN_Lr`Dq%7nlOVDl$3QKrtIO8)-*QKREmZ0LHa-dp|TdP MM@xV63vM4102*pH;s5{u delta 59 zcmV-B0L1^OfdRCD0kG`~lV}bTlR*n3ldcRGlZOi@k`yD8*$Wqw^a~i1lNA?}&k6_y RGcqY6U6cM2BeQM{Iy8C~6b}FZ From bb4ba1a3866bd68917eaa073b899878cf6fb0c2e Mon Sep 17 00:00:00 2001 From: sezna Date: Thu, 25 Jul 2024 11:02:20 -0400 Subject: [PATCH 10/10] update playground regex --- petr-playground/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/petr-playground/index.js b/petr-playground/index.js index 2400587..5c5b206 100644 --- a/petr-playground/index.js +++ b/petr-playground/index.js @@ -14,8 +14,8 @@ monaco.languages.setMonarchTokensProvider("petr", { keywords: [ 'fn', 'returns', 'in', 'type', 'export' ], tokenizer: { root: [ - [/\~([a-zA-Z][a-zA-Z0-9]+)(\.[a-zA-Z]([a-zA-Z0-9])+)*/, "function-call"], - [/\@[a-zA-Z]+/, "intrinsic"], + [/\~([a-zA-Z_][a-zA-Z0-9_]+)(\.[a-zA-Z_]([a-zA-Z0-9_])+)*/, "function-call"], + [/\@[a-zA-Z_]+/, "intrinsic"], [/[0-9]+/, "integer-literal"], [/\".*\"/, "string-literal"], ],