omg I can typecheck generics

let's gooooooooooo
M fml/src/ast.rs +1 -0
@@ 97,6 97,7 @@ pub enum Expr {
     },
     TypeCtor {
         name: String,
+        type_params: Vec<Type>,
         body: ExprNode,
     },
 }

          
M fml/src/lib.rs +11 -5
@@ 17,7 17,7 @@ pub enum Type {
 }
 
 impl Type {
-    fn get_primitive_type(s: &str) -> Option<Type> {
+    fn _get_primitive_type(s: &str) -> Option<Type> {
         match s {
             "I32" => Some(Type::Named("I32".to_string(), vec![])),
             "Bool" => Some(Type::Named("Bool".to_string(), vec![])),

          
@@ 26,8 26,14 @@ impl Type {
         }
     }
 
-    fn get_generic_names(&self) -> HashSet<String> {
-        fn helper(t: &Type, accm: &mut HashSet<String>) {
+    /// TODO:
+    /// The ordering here is a little crazy 'cause we have to
+    /// match names with arg positions, so if we have type T(@Foo, @Bar)
+    /// and we instantiate it later with $T(I32, F(Bool)) then it has
+    /// to be semi sane.
+    /// ...or something.  Think about this more.
+    fn get_generic_names(&self) -> Vec<String> {
+        fn helper(t: &Type, accm: &mut Vec<String>) {
             match t {
                 Type::Named(_, ts) => {
                     for t in ts {

          
@@ 46,11 52,11 @@ impl Type {
                     }
                 }
                 Type::Generic(s) => {
-                    accm.insert(s.clone());
+                    accm.push(s.clone());
                 }
             }
         }
-        let mut accm = HashSet::new();
+        let mut accm = vec![];
         helper(self, &mut accm);
         accm
     }

          
M fml/src/parser.rs +42 -23
@@ 375,7 375,18 @@ impl<'input> Parser<'input> {
     /// typedef = "type" ident "=" type
     fn parse_typedef(&mut self) -> ast::Decl {
         let name = self.expect_ident();
-        let params = vec![];
+        let mut params = vec![];
+        if self.peek_expect(T::LParen.discr()) {
+            while !self.peek_is(T::RParen.discr()) {
+                self.expect(T::At);
+                let name = self.expect_ident();
+                params.push(name);
+                if !self.peek_expect(T::Comma.discr()) {
+                    break;
+                }
+            }
+            self.expect(T::RParen);
+        }
 
         self.expect(T::Equals);
         let ty = self.parse_type();

          
@@ 428,13 439,14 @@ impl<'input> Parser<'input> {
 
     fn parse_fn_type(&mut self) -> Type {
         // TODO: Parse generic stuffs?
-        let params = self.parse_fn_type_args();
+        let params = self.parse_type_list();
         self.expect(T::Colon);
         let rettype = self.parse_type();
         Type::Func(params, Box::new(rettype))
     }
 
-    fn parse_fn_type_args(&mut self) -> Vec<Type> {
+    /// type_list = "(" [type {"," type} [","] ")"
+    fn parse_type_list(&mut self) -> Vec<Type> {
         let mut args = vec![];
         self.expect(T::LParen);
 

          
@@ 523,8 535,21 @@ impl<'input> Parser<'input> {
                 // $Foo(Type) expr
                 // or we could have
                 // $Foo (expr)
+                // So for now we just force it to be the first one
+                // But this is Actually Really Bad because it means that we can
+                // NOT follow a type constructor with a parenthesized expression,
+                // such as `$Foo (1 + 2) * 3`
+                let type_params = if self.peek_is(T::LParen.discr()) {
+                    self.parse_type_list()
+                } else {
+                    vec![]
+                };
                 let body = self.parse_expr(0)?;
-                ast::ExprNode::new(ast::Expr::TypeCtor { name, body })
+                ast::ExprNode::new(ast::Expr::TypeCtor {
+                    name,
+                    type_params,
+                    body,
+                })
             }
 
             // Something else not a valid expr

          
@@ 678,36 703,30 @@ impl<'input> Parser<'input> {
     }
 
     fn parse_type(&mut self) -> Type {
-        let t = self.next();
-        match t {
-            Some(Token {
-                kind: T::Ident(ref s),
-                span: _,
-            }) => {
-                if let Some(t) = Type::get_primitive_type(s) {
-                    t.clone()
+        let t = self.next().unwrap_or_else(|| self.error("type", None));
+        match t.kind {
+            T::Ident(ref s) => {
+                let type_params = if self.peek_is(T::LParen.discr()) {
+                    self.parse_type_list()
                 } else {
-                    Type::Named(s.clone(), vec![])
-                }
+                    vec![]
+                };
+                Type::Named(s.clone(), type_params)
             }
-            Some(Token { kind: T::At, .. }) => {
+            T::At => {
                 let s = self.expect_ident();
                 Type::Generic(s)
             }
-            Some(Token {
-                kind: T::LBrace, ..
-            }) => {
+            T::LBrace => {
                 let tuptype = self.parse_tuple_type();
                 tuptype
             }
-            Some(Token { kind: T::Fn, .. }) => {
+            T::Fn => {
                 let fntype = self.parse_fn_type();
                 fntype
             }
-            Some(Token {
-                kind: T::Struct, ..
-            }) => self.parse_struct_type(),
-            other => self.error("type", other),
+            T::Struct => self.parse_struct_type(),
+            _ => self.error("type", Some(t)),
         }
     }
 

          
M fml/src/typeck.rs +31 -6
@@ 212,6 212,8 @@ impl Tck {
                     // generic type actually exists in the function's scope or such,
                     // when naming an unknown generic name this gives "type mismatch"
                     // rather than "unknown name".  Guess it works for now though.
+                    //
+                    // TODO: The scoping here is still bananas, figure it out
                     let tid = self.insert(TypeInfo::Unknown);
                     named_types.insert(s.clone(), tid);
                     TypeInfo::Ref(tid)

          
@@ 225,7 227,13 @@ impl Tck {
                 let inst_ret = self.instantiate(named_types, rettype);
                 TypeInfo::Func(inst_args, inst_ret)
             }
-            Type::Struct(_body) => todo!(),
+            Type::Struct(body) => {
+                let inst_body = body
+                    .iter()
+                    .map(|(nm, ty)| (nm.clone(), self.instantiate(named_types, ty)))
+                    .collect();
+                TypeInfo::Struct(inst_body)
+            }
         };
         self.insert(typeinfo)
     }

          
@@ 489,17 497,34 @@ fn typecheck_expr(
             tck.set_expr_type(expr, typeid);
             Ok(typeid)
         }
-        TypeCtor { name, body } => {
+        TypeCtor {
+            name,
+            type_params,
+            body,
+        } => {
             let named_type = symtbl.get_type(name).expect("Unknown type constructor");
             println!("Got type named {}: is {:?}", name, named_type);
-            let tid = tck.insert_known(&named_type);
+            // Ok if we have declared type params we gotta instantiate them
+            // to match the type's generics.
+            let type_param_names = named_type.get_generic_names();
+            assert_eq!(type_params.len(), type_param_names.len());
+            let mut type_mapping = HashMap::new();
+            for (name, ty) in type_param_names.iter().zip(type_params.iter()) {
+                let tid = tck.insert_known(ty);
+                type_mapping.insert(name.clone(), tid);
+            }
+            let tid = tck.instantiate(&mut type_mapping, &named_type);
+
+            //let tid = tck.insert_known(&named_type);
             let body_type = typecheck_expr(tck, symtbl, body)?;
             println!("Expected type is {:?}, body type is {:?}", tid, body_type);
             tck.unify(symtbl, tid, body_type)?;
             println!("Done unifying type ctor");
             // The type the expression returns
             // TODO: Generic params for type constructors
-            let constructed_type = tck.insert_known(&Type::Named(name.clone(), vec![]));
+            //let constructed_type = tck.insert_known(&Type::Named(name.clone(), vec![]));
+            let constructed_type =
+                tck.insert_known(&Type::Named(name.clone(), type_params.clone()));
             tck.set_expr_type(expr, constructed_type);
             Ok(tid)
         }

          
@@ 526,13 551,13 @@ pub fn typecheck(ast: &ast::Ast) {
                 let t = typecheck_func_body(Some(name), tck, symtbl, signature, body);
                 t.unwrap_or_else(|e| {
                     tck.print_types();
-                    panic!("Error while typechecking function {}:\n{:?}", name, e)
+                    panic!("Error while typechecking function {}:\n{}", name, e)
                 });
             }
             TypeDef { name, params, ty } => {
                 // Make sure that there are no unbound generics in the typedef
                 // that aren't mentioned in the params.
-                let generic_names = ty.get_generic_names();
+                let generic_names: HashSet<String> = ty.get_generic_names().into_iter().collect();
                 let param_names: HashSet<String> = params.iter().cloned().collect();
                 let difference: Vec<_> = generic_names
                     .symmetric_difference(&param_names)

          
M fml/tests/main.rs +7 -0
@@ 125,3 125,10 @@ fn test_struct4() {
     let src = include_str!("test_struct4.gt");
     let _output = fml::compile("test_struct4.gt", src);
 }
+
+#[test]
+#[should_panic]
+fn test_struct5() {
+    let src = include_str!("test_struct5.gt");
+    let _output = fml::compile("test_struct5.gt", src);
+}

          
M fml/tests/test_struct4.gt +5 -1
@@ 14,6 14,10 @@ fn main(): I32 =
         .a = 3,
         .b = false
     })
-    --let f2: struct a: I32, b: Bool end = id(struct { a = 3, b = false })
+
+    let f1: Foo(Bool) = id($Foo(Bool) {
+        .a = true,
+        .b = false
+    })
     3
 end

          
A => fml/tests/test_struct5.gt +23 -0
@@ 0,0 1,23 @@ 
+
+type Foo(@Thing) = struct
+    a: @Blarg,
+    b: Bool
+end
+
+fn id(x: @T): @T =
+    x
+end
+
+
+fn main(): I32 =
+    let f1: Foo(I32) = id($Foo(I32) {
+        .a = 3,
+        .b = false
+    })
+
+    let f1: Foo(Bool) = id($Foo(Bool) {
+        .a = true,
+        .b = false
+    })
+    3
+end

          
M fml/tests/test_typedef1.gt +1 -1
@@ 2,7 2,7 @@ 
 type Foo = I32
 
 fn thing(i: I32): Foo =
-    $Foo(i)
+    $Foo i
 end
 
 

          
M fml/tests/test_typedef2.gt +1 -1
@@ 2,7 2,7 @@ 
 type Foo = {I32, Bool}
 
 fn thing(i: I32): Foo =
-    $Foo({i, false})
+    $Foo {i, false}
 end