From 965450ce5047e7dc80d904b2d1aaa909cd430763 Mon Sep 17 00:00:00 2001 From: CohenArthur Date: Sat, 22 Jan 2022 01:50:29 +0100 Subject: [PATCH 01/11] ft: Add base test for generic type instantiation --- tests/ft/generics/generics.yml | 5 +++++ tests/ft/generics/valid_simple_type.jk | 5 +++++ 2 files changed, 10 insertions(+) create mode 100644 tests/ft/generics/valid_simple_type.jk diff --git a/tests/ft/generics/generics.yml b/tests/ft/generics/generics.yml index 83e02c8f..225b13d6 100644 --- a/tests/ft/generics/generics.yml +++ b/tests/ft/generics/generics.yml @@ -66,3 +66,8 @@ tests: args: - "tests/ft/generics/invalid_typechecking.jk" exit_code: 1 + - name: "Valid simple generic type" + binary: "target/debug/jinko" + args: + - "tests/ft/generics/valid_simple_type.jk" + exit_code: 0 diff --git a/tests/ft/generics/valid_simple_type.jk b/tests/ft/generics/valid_simple_type.jk new file mode 100644 index 00000000..891e4ac6 --- /dev/null +++ b/tests/ft/generics/valid_simple_type.jk @@ -0,0 +1,5 @@ +type Generic[T](inner: T); +type Generic2[T, U](inner1: T, inner2: U); + +a = Generic[int](inner: 15); +b = Generic2[Generic, string](inner1: a, inner2: "woooow embedded"); From a825c7683dea95e6ee1d853187f8e2badd787833 Mon Sep 17 00:00:00 2001 From: CohenArthur Date: Sat, 22 Jan 2022 01:52:19 +0100 Subject: [PATCH 02/11] ft: Fix test for generic instantiation --- tests/ft/generics/invalid_missing_type.jk | 6 ++++++ tests/ft/generics/valid_simple_type.jk | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 tests/ft/generics/invalid_missing_type.jk diff --git a/tests/ft/generics/invalid_missing_type.jk b/tests/ft/generics/invalid_missing_type.jk new file mode 100644 index 00000000..0629feb2 --- /dev/null +++ b/tests/ft/generics/invalid_missing_type.jk @@ -0,0 +1,6 @@ +type Invalid[T](inner: T); +type Invalid2[T, U](inner1: T, inner2: U); + +a = Invalid[int](inner: 15); +// missing type on `Invalid` +b = Invalid2[Invalid, string](inner1: a, inner2: "woooow embedded"); diff --git a/tests/ft/generics/valid_simple_type.jk b/tests/ft/generics/valid_simple_type.jk index 891e4ac6..f248c0c4 100644 --- a/tests/ft/generics/valid_simple_type.jk +++ b/tests/ft/generics/valid_simple_type.jk @@ -2,4 +2,4 @@ type Generic[T](inner: T); type Generic2[T, U](inner1: T, inner2: U); a = Generic[int](inner: 15); -b = Generic2[Generic, string](inner1: a, inner2: "woooow embedded"); +b = Generic2[Generic[int], string](inner1: a, inner2: "woooow embedded"); From 5b1908cea8e3e4f6091718f1a555cd97aa680b43 Mon Sep 17 00:00:00 2001 From: CohenArthur Date: Sat, 26 Feb 2022 21:56:05 +0100 Subject: [PATCH 03/11] stdlib: Add base for generic vector type --- stdlib/intrinsics.jk | 5 +++++ stdlib/lib.jk | 2 ++ stdlib/vec.jk | 39 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 46 insertions(+) create mode 100644 stdlib/intrinsics.jk create mode 100644 stdlib/vec.jk diff --git a/stdlib/intrinsics.jk b/stdlib/intrinsics.jk new file mode 100644 index 00000000..8424f442 --- /dev/null +++ b/stdlib/intrinsics.jk @@ -0,0 +1,5 @@ +/* built-in interpreter directives */ + +ext func size_of[T](value: T); +ext func type_of[T](value: T) -> string; +ext func deinitialize[T](value: T); diff --git a/stdlib/lib.jk b/stdlib/lib.jk index cfc0abdc..053c63a7 100644 --- a/stdlib/lib.jk +++ b/stdlib/lib.jk @@ -1,6 +1,7 @@ incl bool incl int incl string +incl vec incl pair incl maybe @@ -9,6 +10,7 @@ incl iter incl ffi incl args incl fmt +incl intrinsics ext func __builtin_exit(exit_code: int); diff --git a/stdlib/vec.jk b/stdlib/vec.jk new file mode 100644 index 00000000..ab61ea8a --- /dev/null +++ b/stdlib/vec.jk @@ -0,0 +1,39 @@ +ext func memcpy(dest: int, src: int, n: int); +ext func calloc(n: int, size: int); +ext func realloc(ptr: int, size: int); + +/* FIXME: Add default values for fields */ +type Vec[T](raw_pointer: int, len: int, size: int); +/* type Vec[T](raw_pointer: int = 0, len: int = 0, size: int = 0); */ + +func inner_init[T](value: T) -> Vec[T] { + Vec(inner: malloc(size_of(value)), + len: 1, + size: size_of(value)) +} + +func inner_grow[T](v: Vec[T], value: T) -> Vec[T] { + new_size = v.size + size_of(value); + new_pointer = v.raw_pointer.realloc(v.size + size_of(value)); + + Vec(raw_pointer: new_pointer, len: v.len + 1, size: new_size) +} + +func add[T](v: Vec[T], value: T) -> Vec[T] { + if v.size == 0 { + inner_init(value) + } else { + inner_grow(v, value) + } +} + +// FIXME: Add this once we have proper first parameter overloading +// func deinitialize[T](v: Vec[T]) { +// v.raw_pointer.free() +// } + +test vec_init() { + v = Vec[int](raw_pointer: 0, len: 0, size: 0).add(15).add(14); + + // v.deinitialize(); +} From fc994523e5d836f39b0c8ea50d0581d253c1f913 Mon Sep 17 00:00:00 2001 From: CohenArthur Date: Sun, 27 Feb 2022 10:21:26 +0100 Subject: [PATCH 04/11] stdlib: Rename Vec.add() -> Vec.push() --- stdlib/vec.jk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/vec.jk b/stdlib/vec.jk index ab61ea8a..1d2def9f 100644 --- a/stdlib/vec.jk +++ b/stdlib/vec.jk @@ -19,7 +19,7 @@ func inner_grow[T](v: Vec[T], value: T) -> Vec[T] { Vec(raw_pointer: new_pointer, len: v.len + 1, size: new_size) } -func add[T](v: Vec[T], value: T) -> Vec[T] { +func push[T](v: Vec[T], value: T) -> Vec[T] { if v.size == 0 { inner_init(value) } else { @@ -33,7 +33,7 @@ func add[T](v: Vec[T], value: T) -> Vec[T] { // } test vec_init() { - v = Vec[int](raw_pointer: 0, len: 0, size: 0).add(15).add(14); + v = Vec[int](raw_pointer: 0, len: 0, size: 0).push(15).push(14); // v.deinitialize(); } From d471c6b5a304a11b4d4c69bea5eb79e2af3db61a Mon Sep 17 00:00:00 2001 From: CohenArthur Date: Sun, 27 Feb 2022 10:21:43 +0100 Subject: [PATCH 05/11] parser: Parse nested generics properly --- src/parser/constructs.rs | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/parser/constructs.rs b/src/parser/constructs.rs index 09d345b2..ddab9145 100644 --- a/src/parser/constructs.rs +++ b/src/parser/constructs.rs @@ -196,8 +196,8 @@ fn method_or_field( /// | 'test' function_declaration block /// | 'mock' function_declaration block /// -/// | 'type' spaced_identifier '(' named_args -/// | '_ncl' spaced_identifier [ 'as' next IDENTIFIER ] +/// | 'type' type_id '(' named_args +/// | 'incl' spaced_identifier [ 'as' next IDENTIFIER ] /// | 'mut' spaced_identifier '=' expr (* mutable variable assigment *) /// | '@' spaced_identifier '(' args /// @@ -414,11 +414,12 @@ fn type_id(input: ParseInput) -> ParseResult { } } -/// spaced_identifier '(' type_inst_arg (',' type_inst_arg)* ')' +/// type_id '(' type_inst_arg (',' type_inst_arg)* ')' fn unit_type_decl( input: ParseInput, start_loc: Location, ) -> ParseResult> { + // FIXME: This needs to use TypeIds let (input, (name, _)) = spaced_identifier(input)?; let (input, generics) = maybe_generic_list(input)?; let (input, mut type_dec) = if let Ok((input, _)) = Token::left_parenthesis(input) { @@ -508,26 +509,20 @@ fn unit_block( // FIXME: This does not parse default generic types yet (`func f()`) fn generic_list(input: ParseInput) -> ParseResult> { - fn whitespace_plus_id(input: ParseInput) -> ParseResult { + fn whitespace_plus_type_id(input: ParseInput) -> ParseResult { let input = next(input); - let (input, id) = Token::identifier(input)?; + let (input, id) = type_id(input)?; let input = next(input); Ok((input, id)) } - let (input, first_type) = whitespace_plus_id(input)?; - let (input, mut generics) = many0(preceded(Token::comma, whitespace_plus_id))(input)?; + let (input, first_type) = whitespace_plus_type_id(input)?; + let (input, mut generics) = many0(preceded(Token::comma, whitespace_plus_type_id))(input)?; let (input, _) = Token::right_bracket(input)?; generics.insert(0, first_type); - Ok(( - input, - generics - .into_iter() - .map(|name| TypeId::new(Symbol::from(name))) - .collect(), - )) + Ok((input, generics)) } fn maybe_generic_list(input: ParseInput) -> ParseResult> { @@ -1637,4 +1632,9 @@ func void() { }"## fn complex_function_declaration() { assert!(expr(span!("func f_to_f[F1, F2](f1: func[F1](A) -> B, f2: func[F2](B) -> func(A) -> B, p: string) -> func[F1, F2](F1, F2, string) -> Pair[A, B] { /* todo :) */ }")).is_ok()); } + + #[test] + fn nested_generic_type() { + assert!(expr(span!("a = Pair[Pair[int], int](a: 15, b: 14)")).is_ok()); + } } From 1c6acfe074a772ad560767c7246fd19c2a8fbdb7 Mon Sep 17 00:00:00 2001 From: CohenArthur Date: Sun, 27 Feb 2022 13:15:26 +0100 Subject: [PATCH 06/11] generics: Handle generic builtins properly --- src/builtins.rs | 31 +++++++++++++++++++++++++++++-- src/context.rs | 4 +++- src/generics.rs | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+), 3 deletions(-) diff --git a/src/builtins.rs b/src/builtins.rs index 7e401cfc..528c70ab 100644 --- a/src/builtins.rs +++ b/src/builtins.rs @@ -6,7 +6,9 @@ use std::path::PathBuf; #[cfg(feature = "ffi")] use crate::ffi; use crate::instance::{FromObjectInstance, ToObjectInstance}; -use crate::{Context, Instruction, JkBool, JkChar, JkFloat, JkInt, JkString, ObjectInstance}; +use crate::{ + generics, log, Context, Instruction, JkBool, JkChar, JkFloat, JkInt, JkString, ObjectInstance, +}; type Args = Vec>; type BuiltinFn = fn(&mut Context, Args) -> Option; @@ -141,6 +143,23 @@ fn fmt_float(ctx: &mut Context, args: Args) -> Option { Some(JkString::from(value.to_string()).to_instance()) } +fn size_of(ctx: &mut Context, args: Args) -> Option { + let instance = args[0].execute(ctx).unwrap(); + + log!("called size_of"); + + Some(JkInt::from(instance.size() as i64).to_instance()) +} + +fn type_of(ctx: &mut Context, args: Args) -> Option { + let instance = args[0].execute(ctx).unwrap(); + let instance_ty = instance.ty().to_string(); + + log!("called type_of"); + + Some(JkString::from(instance_ty).to_instance()) +} + impl Builtins { fn add(&mut self, name: &'static str, builtin_fn: BuiltinFn) { self.functions.insert(String::from(name), builtin_fn); @@ -164,16 +183,24 @@ impl Builtins { builtins.add("__builtin_arg_get", arg_get); builtins.add("__builtin_arg_amount", arg_amount); builtins.add("__builtin_exit", exit); + builtins.add("size_of", size_of); + builtins.add("type_of", type_of); builtins } pub fn contains(&self, name: &str) -> bool { + // We can demangle builtins to dispatch to our single, non generic + // implementation. + let name = generics::original_name(name); + + log!("checking if builtin is present: {}", name); + self.functions.contains_key(name) } pub fn get(&self, builtin: &str) -> Option<&BuiltinFn> { - self.functions.get(builtin) + self.functions.get(generics::original_name(builtin)) } } diff --git a/src/context.rs b/src/context.rs index 2ba2bf5c..59720163 100644 --- a/src/context.rs +++ b/src/context.rs @@ -17,9 +17,9 @@ use std::rc::Rc; use crate::error::{ErrKind, Error, ErrorHandler}; use crate::instruction::{Block, FunctionDec, FunctionKind, Instruction, TypeDec, Var}; -use crate::parser; use crate::typechecker::{SpecializedNode, TypeCheck, TypeCtx, TypeId}; use crate::ObjectInstance; +use crate::{log, parser}; use crate::{Builtins, CheckedType}; /// Type the context uses for keys @@ -298,6 +298,8 @@ impl Context { fn inner_check(&mut self, ep: &mut Block) -> Result<(), Error> { self.scope_enter(); + log!("starting first pass of typechecking"); + ep.type_of(&mut self.typechecker); self.error_handler diff --git a/src/generics.rs b/src/generics.rs index eddb0941..c627b020 100644 --- a/src/generics.rs +++ b/src/generics.rs @@ -91,6 +91,31 @@ pub fn mangle(name: &str, types: &[TypeId]) -> String { mangled } +/// Performs the opposite conversion, turning a mangled name into a valid +/// jinko function name with generics. +pub fn demangle(_mangled_name: &str) -> String { + todo!() +} + +/// Fetch the original name contained in a mangled name. This is useful for +/// generic builtins, which only have one implementation despite being able +/// to handle multiple types. +/// +/// ```rust +/// use jinko::generics::original_name; +/// +/// let mangled = "type_of+int"; +/// let original_builtin_name = original_name(mangled); +/// +/// assert_eq(original_builtin_name, "type_of"); +/// ``` +pub fn original_name(mangled_name: &str) -> &str { + match mangled_name.find('+') { + None => mangled_name, + Some(first_separator) => &mangled_name[..first_separator], + } +} + /// Since most of the instructions cannot do generic expansion, we can implement /// default methods which do nothing. This avoid more boilerplate code for instructions /// such as constants or variables which cannot be generic. @@ -138,6 +163,14 @@ mod tests { ); } + #[test] + fn get_original_name_back() { + assert_eq!( + original_name(&mangle("og_fn", &[ty!("float"), ty!("ComplexType")])), + "og_fn" + ); + } + #[test] fn create_map_different_size() { let mut ctx = TypeCtx::new(); From be9a180b27d910a1c21464a3a53fcfe3da6ff652 Mon Sep 17 00:00:00 2001 From: CohenArthur Date: Sun, 27 Feb 2022 13:15:42 +0100 Subject: [PATCH 07/11] log: Improve formatting of header --- src/log.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/log.rs b/src/log.rs index 3ac80a6e..c816f1b8 100644 --- a/src/log.rs +++ b/src/log.rs @@ -23,7 +23,7 @@ macro_rules! log { if $crate::log::is_enabled() { use colored::Colorize; - eprintln!("<{}> {}", "LOG".on_purple(), format_args!($($token)*)); + eprintln!("<{}> {}", "LOG".black().on_purple(), format_args!($($token)*)); } ); } From c627f00ab916193ea650398669f7154a1360a30e Mon Sep 17 00:00:00 2001 From: CohenArthur Date: Sun, 27 Feb 2022 13:15:53 +0100 Subject: [PATCH 08/11] stdlib: Add intrinsics builtin library --- stdlib/intrinsics.jk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/intrinsics.jk b/stdlib/intrinsics.jk index 8424f442..103cda44 100644 --- a/stdlib/intrinsics.jk +++ b/stdlib/intrinsics.jk @@ -1,5 +1,5 @@ /* built-in interpreter directives */ -ext func size_of[T](value: T); +ext func size_of[T](value: T) -> int; ext func type_of[T](value: T) -> string; ext func deinitialize[T](value: T); From c8abeec130109ed8bc009c86b452b49e4c723e2f Mon Sep 17 00:00:00 2001 From: CohenArthur Date: Sun, 27 Feb 2022 14:00:04 +0100 Subject: [PATCH 09/11] parser: Allow whitespace between generics and args --- src/instruction/function_call.rs | 7 +++++ src/instruction/type_instantiation.rs | 2 +- src/instruction/var.rs | 4 +++ src/instruction/var_assignment.rs | 10 +++++++ src/parser/constructs.rs | 7 +++++ src/typechecker.rs | 38 ++++++++++++++++++--------- 6 files changed, 54 insertions(+), 14 deletions(-) diff --git a/src/instruction/function_call.rs b/src/instruction/function_call.rs index 864aa1ed..8f357252 100644 --- a/src/instruction/function_call.rs +++ b/src/instruction/function_call.rs @@ -274,6 +274,10 @@ impl Instruction for FunctionCall { } impl TypeCheck for FunctionCall { + fn type_log(&self) -> String { + self.fn_name.to_string() + } + fn resolve_type(&mut self, ctx: &mut TypeCtx) -> CheckedType { log!("typechecking call to {}", self.fn_name); @@ -455,6 +459,9 @@ impl Generic for FunctionCall { }) .collect(); + // FIXME: Do we need this? + // self.args.iter_mut().for_each(|arg| arg.resolve_self(ctx)); + self.fn_name = generic_name; self.generics = vec![]; match dec.ty() { diff --git a/src/instruction/type_instantiation.rs b/src/instruction/type_instantiation.rs index 00423117..439a0ff6 100644 --- a/src/instruction/type_instantiation.rs +++ b/src/instruction/type_instantiation.rs @@ -197,7 +197,7 @@ impl Generic for TypeInstantiation {} #[cfg(test)] mod test { use super::*; - use crate::{jinko_fail, span, symbol::Symbol}; + use crate::{jinko_fail, symbol::Symbol}; #[test] fn t_fields_number() { diff --git a/src/instruction/var.rs b/src/instruction/var.rs index 8975fcc9..98fb6e36 100644 --- a/src/instruction/var.rs +++ b/src/instruction/var.rs @@ -104,6 +104,10 @@ impl Instruction for Var { } impl TypeCheck for Var { + fn type_log(&self) -> String { + self.name.to_string() + } + fn resolve_type(&mut self, ctx: &mut TypeCtx) -> CheckedType { match ctx.get_var(self.name()) { Some(var_ty) => var_ty.clone(), diff --git a/src/instruction/var_assignment.rs b/src/instruction/var_assignment.rs index 19b8c383..8aab753c 100644 --- a/src/instruction/var_assignment.rs +++ b/src/instruction/var_assignment.rs @@ -202,6 +202,7 @@ impl Generic for VarAssign { } fn resolve_self(&mut self, ctx: &mut TypeCtx) { + log!("resolving value of var assign"); self.value.resolve_self(ctx) } } @@ -301,4 +302,13 @@ mod tests { JkInt::from(0).to_instance() ); } + + #[test] + fn generic_builtin_for_var_assign() { + jinko! { + a = 156; + + int_size = size_of[int](a); + }; + } } diff --git a/src/parser/constructs.rs b/src/parser/constructs.rs index ddab9145..a43d035a 100644 --- a/src/parser/constructs.rs +++ b/src/parser/constructs.rs @@ -665,6 +665,7 @@ fn generic_func_or_type_inst_args( ) -> ParseResult> { // FIXME: Assign generics to FunctionCall and TypeInstantiation let (input, generics) = generic_list(input)?; + let input = next(input); let (input, _) = Token::left_parenthesis(input)?; func_or_type_inst_args(next(input), id, generics, start_loc) @@ -1637,4 +1638,10 @@ func void() { }"## fn nested_generic_type() { assert!(expr(span!("a = Pair[Pair[int], int](a: 15, b: 14)")).is_ok()); } + + #[test] + fn assign_call_to_generic_fn() { + assert!(expr(span!("int_size = size_of[int](15)")).is_ok()); + assert!(expr(span!("int_size = size_of [int] (15)")).is_ok()); + } } diff --git a/src/typechecker.rs b/src/typechecker.rs index 99cae9d4..925566a2 100644 --- a/src/typechecker.rs +++ b/src/typechecker.rs @@ -8,7 +8,7 @@ pub use type_id::{TypeId, PRIMITIVE_TYPES}; use crate::{ error::ErrorHandler, instruction::{FunctionDec, TypeDec}, - Error, ScopeMap, + log, Error, ScopeMap, }; use colored::Colorize; use std::{ @@ -254,25 +254,37 @@ pub trait TypeCheck { /// Access the cached type of an instruction fn cached_type(&self) -> Option<&CheckedType>; + fn type_log(&self) -> String { + String::new() + } + /// Access the cached type of an instruction or perform the type resolution process. /// This avoid typechecking an entire instruction a second time and allows the /// context to just access it. This is useful for passes such as generic expansion. fn type_of(&mut self, ctx: &mut TypeCtx) -> CheckedType { + log!("type_of value {}", self.type_log()); + // FIXME: Remove clones match self.cached_type() { - None => match self.resolve_type(ctx) { - CheckedType::Resolved(new_ty) => { - self.set_cached_type(CheckedType::Resolved(new_ty.clone())); - CheckedType::Resolved(new_ty) + None => { + log!("no cached type"); + match self.resolve_type(ctx) { + CheckedType::Resolved(new_ty) => { + self.set_cached_type(CheckedType::Resolved(new_ty.clone())); + CheckedType::Resolved(new_ty) + } + CheckedType::Void => { + self.set_cached_type(CheckedType::Void); + CheckedType::Void + } + CheckedType::Error => CheckedType::Error, + CheckedType::Later => CheckedType::Later, } - CheckedType::Void => { - self.set_cached_type(CheckedType::Void); - CheckedType::Void - } - CheckedType::Error => CheckedType::Error, - CheckedType::Later => CheckedType::Later, - }, - Some(ty) => ty.clone(), + } + Some(ty) => { + log!("cached type!: {}", ty); + ty.clone() + } } } } From 185ac43faa3e578f22a66deada80f7a1287cbdbf Mon Sep 17 00:00:00 2001 From: Arthur Cohen Date: Sat, 19 Mar 2022 22:43:35 +0100 Subject: [PATCH 10/11] generics: Add proper generic type specialization --- src/generics.rs | 2 +- src/instruction/field_access.rs | 9 ++-- src/instruction/type_declaration.rs | 61 ++++++++++++++++++------- src/instruction/type_instantiation.rs | 64 +++++++++++++++++++++++---- src/typechecker.rs | 29 +++--------- stdlib/vec.jk | 11 +++-- 6 files changed, 117 insertions(+), 59 deletions(-) diff --git a/src/generics.rs b/src/generics.rs index c627b020..4fcd882e 100644 --- a/src/generics.rs +++ b/src/generics.rs @@ -107,7 +107,7 @@ pub fn demangle(_mangled_name: &str) -> String { /// let mangled = "type_of+int"; /// let original_builtin_name = original_name(mangled); /// -/// assert_eq(original_builtin_name, "type_of"); +/// assert_eq!(original_builtin_name, "type_of"); /// ``` pub fn original_name(mangled_name: &str) -> &str { match mangled_name.find('+') { diff --git a/src/instruction/field_access.rs b/src/instruction/field_access.rs index 8206a2f4..4ed6ffa6 100644 --- a/src/instruction/field_access.rs +++ b/src/instruction/field_access.rs @@ -105,13 +105,14 @@ impl TypeCheck for FieldAccess { // We can unwrap here since the type that was resolved from the instance HAS // to exist. If it does not, this is an interpreter error - let (_, fields_ty) = ctx.get_custom_type(instance_ty_name).unwrap(); + let dec = ctx.get_custom_type(instance_ty_name).unwrap(); - match fields_ty + match dec + .fields() .iter() - .find(|(field_name, _)| *field_name == self.field_name) + .find(|dec_arg| dec_arg.name() == self.field_name) { - Some((_, field_ty)) => field_ty.clone(), + Some(dec_arg) => CheckedType::Resolved(dec_arg.get_type().clone()), None => { ctx.error( Error::new(ErrKind::TypeChecker) diff --git a/src/instruction/type_declaration.rs b/src/instruction/type_declaration.rs index 2837799b..fac8ca72 100644 --- a/src/instruction/type_declaration.rs +++ b/src/instruction/type_declaration.rs @@ -1,9 +1,10 @@ use super::{DecArg, InstrKind, Instruction}; use crate::{ + generics::GenericMap, log, typechecker::{CheckedType, TypeCtx, TypeId}, - Context, Generic, ObjectInstance, SpanTuple, TypeCheck, + Context, ErrKind, Error, Generic, ObjectInstance, SpanTuple, TypeCheck, }; #[derive(Clone, Debug, PartialEq)] @@ -27,6 +28,42 @@ impl TypeDec { } } + /// Generate a new instance of [`TypeDec`] from a given generic type map + pub fn from_type_map( + &self, + mangled_name: String, + type_map: &GenericMap, + ctx: &mut TypeCtx, + ) -> Result { + let mut new_type = self.clone(); + new_type.name = mangled_name; + new_type.generics = vec![]; + + let mut is_err = false; + + new_type + .fields + .iter_mut() + .zip(self.fields().iter()) + .filter(|(_, generic)| self.generics().contains(generic.get_type())) + .for_each(|(new_arg, old_generic)| { + let new_type = match type_map.get_match(old_generic.get_type()) { + Ok(t) => t, + Err(e) => { + ctx.error(e); + is_err = true; + TypeId::void() + } + }; + new_arg.set_type(new_type); + }); + + match is_err { + true => Err(Error::new(ErrKind::Generics)), + false => Ok(new_type), + } + } + /// Get a reference to the name of the type pub fn name(&self) -> &str { &self.name @@ -37,6 +74,11 @@ impl TypeDec { &self.fields } + /// Get a reference to the type's generics + pub fn generics(&self) -> &Vec { + &self.generics + } + pub fn set_location(&mut self, location: SpanTuple) { self.location = Some(location) } @@ -101,22 +143,7 @@ impl Instruction for TypeDec { impl TypeCheck for TypeDec { fn resolve_type(&mut self, ctx: &mut TypeCtx) -> CheckedType { - // TODO: FunctionDecs and TypeDec are very similar. Should we factor them together? - let fields_ty = self - .fields - .iter() - .map(|dec_arg| { - ( - dec_arg.name().to_string(), - CheckedType::Resolved(dec_arg.get_type().clone()), - ) - }) - .collect(); - if let Err(e) = ctx.declare_custom_type( - self.name.clone(), - CheckedType::Resolved(TypeId::from(self.name())), - fields_ty, - ) { + if let Err(e) = ctx.declare_custom_type(self.name.clone(), self.clone()) { ctx.error(e); } diff --git a/src/instruction/type_instantiation.rs b/src/instruction/type_instantiation.rs index 439a0ff6..064b9abe 100644 --- a/src/instruction/type_instantiation.rs +++ b/src/instruction/type_instantiation.rs @@ -2,10 +2,12 @@ //! type on execution. use super::{Context, ErrKind, Error, InstrKind, Instruction, ObjectInstance, TypeDec, VarAssign}; +use crate::generics::{self, GenericMap}; use crate::instance::Name; -use crate::typechecker::TypeCtx; +use crate::symbol::Symbol; +use crate::typechecker::{SpecializedNode, TypeCtx}; +use crate::{log, Generic, SpanTuple}; use crate::{typechecker::CheckedType, TypeCheck, TypeId}; -use crate::{Generic, SpanTuple}; use std::rc::Rc; @@ -84,6 +86,47 @@ impl TypeInstantiation { pub fn set_location(&mut self, location: SpanTuple) { self.location = Some(location) } + + pub fn resolve_generic_instantiation( + &mut self, + dec: TypeDec, + ctx: &mut TypeCtx, + ) -> CheckedType { + log!( + "creating specialized type. type generics: {}, instantiation generics {}", + dec.generics().len(), + self.generics.len() + ); + let type_map = match GenericMap::create(dec.generics(), &self.generics, ctx) { + Ok(map) => map, + Err(e) => { + ctx.error(e.with_loc(self.location.clone())); + return CheckedType::Error; + } + }; + + let specialized_name = generics::mangle(dec.name(), &self.generics); + log!("specialized name {}", specialized_name); + if ctx.get_custom_type(&specialized_name).is_none() { + // FIXME: Remove this clone once we have proper symbols + let specialized_ty = match dec.from_type_map(specialized_name.clone(), &type_map, ctx) { + Ok(f) => f, + Err(e) => { + ctx.error(e.with_loc(self.location.clone())); + return CheckedType::Error; + } + }; + + ctx.add_specialized_node(SpecializedNode::Type(specialized_ty)); + } + + self.type_name = TypeId::new(Symbol::from(specialized_name)); + self.generics = vec![]; + + // Recursively resolve the type of self now that we changed the + // function to call + self.type_of(ctx) + } } impl Instruction for TypeInstantiation { @@ -143,8 +186,8 @@ impl Instruction for TypeInstantiation { impl TypeCheck for TypeInstantiation { fn resolve_type(&mut self, ctx: &mut TypeCtx) -> CheckedType { - let (_, fields_ty) = match ctx.get_custom_type(self.type_name.id()) { - Some(ty) => ty, + let dec = match ctx.get_custom_type(self.type_name.id()) { + Some(ty) => ty.clone(), None => { ctx.error( Error::new(ErrKind::TypeChecker) @@ -159,17 +202,22 @@ impl TypeCheck for TypeInstantiation { } }; - let fields_ty = fields_ty.clone(); + if !dec.generics().is_empty() || !self.generics.is_empty() { + log!("resolving generic type instantiation"); + return self.resolve_generic_instantiation(dec, ctx); + } let mut errors = vec![]; - for ((_, field_ty), var_assign) in fields_ty.iter().zip(self.fields.iter_mut()) { + for (field_dec, var_assign) in dec.fields().iter().zip(self.fields.iter_mut()) { + let expected_ty = CheckedType::Resolved(field_dec.get_type().clone()); let value_ty = var_assign.value_mut().type_of(ctx); - if field_ty != &value_ty { + if expected_ty != value_ty { errors.push( Error::new(ErrKind::TypeChecker) .with_msg(format!( "trying to assign value of type `{}` to field of type `{}`", - value_ty, field_ty + value_ty, + field_dec.get_type() )) .with_loc(var_assign.location().cloned()), ); diff --git a/src/typechecker.rs b/src/typechecker.rs index 925566a2..c9266c90 100644 --- a/src/typechecker.rs +++ b/src/typechecker.rs @@ -47,11 +47,6 @@ impl Display for CheckedType { } } -struct CustomTypeType { - self_ty: CheckedType, - fields_ty: Vec<(String, CheckedType)>, -} - /// Possible generic generated nodes. Since we can only expand generic functions or /// generic types, there is no need to store any other instruction type. pub enum SpecializedNode { @@ -73,7 +68,7 @@ pub struct TypeCtx { /// map: Variables, Functions and Types. /// For functions, we keep a vector of argument types as well as the return type. /// Custom types need to keep a type for themselves, as well as types for all their fields - types: ScopeMap, + types: ScopeMap, /// When typechecking, monomorphization is performed, meaning that generic functions /// and types get expanded into a new [`Instruction`]. We need to store them /// as we go and then use them in the calling context @@ -100,8 +95,7 @@ impl TypeCtx { ($ty_name:ident) => { ctx.declare_custom_type( String::from(stringify!($ty_name)), - CheckedType::Resolved(TypeId::from(stringify!($ty_name))), - vec![], + TypeDec::new(stringify!($ty_name).to_string(), vec![], vec![]), ) .unwrap(); }; @@ -185,14 +179,8 @@ impl TypeCtx { } /// Declare a newly-created custom type - pub fn declare_custom_type( - &mut self, - name: String, - self_ty: CheckedType, - fields_ty: Vec<(String, CheckedType)>, - ) -> Result<(), Error> { - self.types - .add_type(name, CustomTypeType { self_ty, fields_ty }) + pub fn declare_custom_type(&mut self, name: String, dec: TypeDec) -> Result<(), Error> { + self.types.add_type(name, dec) // FIXME: Add hint here too } @@ -207,13 +195,8 @@ impl TypeCtx { } /// Access a previously declared custom type - pub fn get_custom_type( - &mut self, - name: &str, - ) -> Option<(&CheckedType, &Vec<(String, CheckedType)>)> { - self.types - .get_type(name) - .map(|custom_type| (&custom_type.self_ty, &custom_type.fields_ty)) + pub fn get_custom_type(&mut self, name: &str) -> Option<&TypeDec> { + self.types.get_type(name) } /// Create a new error to propagate to the original context diff --git a/stdlib/vec.jk b/stdlib/vec.jk index 1d2def9f..4516d268 100644 --- a/stdlib/vec.jk +++ b/stdlib/vec.jk @@ -1,6 +1,7 @@ ext func memcpy(dest: int, src: int, n: int); ext func calloc(n: int, size: int); ext func realloc(ptr: int, size: int); +ext func free(ptr: int); /* FIXME: Add default values for fields */ type Vec[T](raw_pointer: int, len: int, size: int); @@ -27,13 +28,11 @@ func push[T](v: Vec[T], value: T) -> Vec[T] { } } +func release(v: Vec[T]) { + v.raw_pointer.free() +} + // FIXME: Add this once we have proper first parameter overloading // func deinitialize[T](v: Vec[T]) { // v.raw_pointer.free() // } - -test vec_init() { - v = Vec[int](raw_pointer: 0, len: 0, size: 0).push(15).push(14); - - // v.deinitialize(); -} From aa183daf84324f8ad9a574399a9711e8462a20b9 Mon Sep 17 00:00:00 2001 From: Arthur Cohen Date: Sat, 19 Mar 2022 23:12:36 +0100 Subject: [PATCH 11/11] generics: Fix generic functional tests --- tests/ft/generics/generics.yml | 8 +++++++- tests/ft/generics/valid_nested_type.jk | 7 +++++++ tests/ft/generics/valid_simple_type.jk | 4 ++-- 3 files changed, 16 insertions(+), 3 deletions(-) create mode 100644 tests/ft/generics/valid_nested_type.jk diff --git a/tests/ft/generics/generics.yml b/tests/ft/generics/generics.yml index 225b13d6..96461c3a 100644 --- a/tests/ft/generics/generics.yml +++ b/tests/ft/generics/generics.yml @@ -70,4 +70,10 @@ tests: binary: "target/debug/jinko" args: - "tests/ft/generics/valid_simple_type.jk" - exit_code: 0 + exit_code: 15 + # FIXME: Don't ignore once #408 is fixed + # - name: "Valid nested simple generic type" + # binary: "target/debug/jinko" + # args: + # - "tests/ft/generics/valid_nested_type.jk" + # exit_code: 15 diff --git a/tests/ft/generics/valid_nested_type.jk b/tests/ft/generics/valid_nested_type.jk new file mode 100644 index 00000000..a236ae5d --- /dev/null +++ b/tests/ft/generics/valid_nested_type.jk @@ -0,0 +1,7 @@ +type Generic[T](inner: T); +type Generic2[T, U](inner1: T, inner2: U); + +a = Generic[int](inner: 15); +b = Generic2[Generic[int], string](inner1: a, inner2: "woooow embedded"); + +b.inner1.inner diff --git a/tests/ft/generics/valid_simple_type.jk b/tests/ft/generics/valid_simple_type.jk index f248c0c4..6115833f 100644 --- a/tests/ft/generics/valid_simple_type.jk +++ b/tests/ft/generics/valid_simple_type.jk @@ -1,5 +1,5 @@ type Generic[T](inner: T); -type Generic2[T, U](inner1: T, inner2: U); a = Generic[int](inner: 15); -b = Generic2[Generic[int], string](inner1: a, inner2: "woooow embedded"); + +a.inner