Make sure generics on typedefs are declared
3 files changed, 49 insertions(+), 6 deletions(-)

M fml/src/lib.rs
M fml/src/typeck.rs
M fml/tests/main.rs
M fml/src/lib.rs +30 -1
@@ 1,6 1,6 @@ 
 //! Garnet compiler guts.
 
-use std::collections::HashMap;
+use std::collections::{HashMap, HashSet};
 
 pub mod ast;
 pub mod parser;

          
@@ 25,6 25,35 @@ impl Type {
             _ => None,
         }
     }
+
+    fn get_generic_names(&self) -> HashSet<String> {
+        fn helper(t: &Type, accm: &mut HashSet<String>) {
+            match t {
+                Type::Named(_, ts) => {
+                    for t in ts {
+                        helper(t, accm);
+                    }
+                }
+                Type::Func(args, rettype) => {
+                    for t in args {
+                        helper(t, accm);
+                    }
+                    helper(rettype, accm)
+                }
+                Type::Struct(body) => {
+                    for (_, ty) in body {
+                        helper(ty, accm);
+                    }
+                }
+                Type::Generic(s) => {
+                    accm.insert(s.clone());
+                }
+            }
+        }
+        let mut accm = HashSet::new();
+        helper(self, &mut accm);
+        accm
+    }
 }
 
 /// A identifier to uniquely refer to our type terms

          
M fml/src/typeck.rs +18 -5
@@ 529,11 529,24 @@ pub fn typecheck(ast: &ast::Ast) {
                     panic!("Error while typechecking function {}:\n{:?}", name, e)
                 });
             }
-            TypeDef {
-                name,
-                params: _,
-                ty,
-            } => symtbl.add_type(name, ty),
+            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 param_names: HashSet<String> = params.iter().cloned().collect();
+                let difference: Vec<_> = generic_names
+                    .symmetric_difference(&param_names)
+                    // gramble gramble &String
+                    .map(|s| s.as_str())
+                    .collect();
+                if difference.len() != 0 {
+                    let differences = difference.join(", ");
+                    panic!("Error in typedef {}: Type params do not match generics mentioned in body.  Unmatched types: {}", name, differences);
+                }
+
+                // Remember that we know about a type with this name
+                symtbl.add_type(name, ty)
+            }
         }
     }
     // Print out toplevel symbols

          
M fml/tests/main.rs +1 -0
@@ 95,6 95,7 @@ fn test_typedef2() {
 }
 
 #[test]
+#[should_panic]
 fn test_typedef3() {
     let src = include_str!("test_typedef3.gt");
     let _output = fml::compile("test_typedef3.gt", src);