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(¶m_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);