Remove old typechecking proof-of-concept
58 files changed, 1 insertions(+), 3680 deletions(-)

R fml/Cargo.lock => 
R fml/Cargo.toml => 
R fml/src/ast.rs => 
R fml/src/lib.rs => 
R fml/src/main.rs => 
R fml/src/parser.rs => 
R fml/src/typeck.rs => 
R fml/tests/main.rs => 
R fml/tests/programs/test1.gt => 
R fml/tests/programs/test2.gt => 
R fml/tests/programs/test_array1.gt => 
R fml/tests/programs/test_array2.gt => 
R fml/tests/programs/test_array3.gt => 
R fml/tests/programs/test_enum1.gt => 
R fml/tests/programs/test_failure.gt => 
R fml/tests/programs/test_forever1.gt => 
R fml/tests/programs/test_forever2.gt => 
R fml/tests/programs/test_lambda1.gt => 
R fml/tests/programs/test_lambda2.gt => 
R fml/tests/programs/test_lambda3.gt => 
R fml/tests/programs/test_lambda4.gt => 
R fml/tests/programs/test_lambda5.gt => 
R fml/tests/programs/test_let1.gt => 
R fml/tests/programs/test_module1.gt => 
R fml/tests/programs/test_module2.gt => 
R fml/tests/programs/test_module3.gt => 
R fml/tests/programs/test_module4.gt => 
R fml/tests/programs/test_module5.gt => 
R fml/tests/programs/test_module6.gt => 
R fml/tests/programs/test_module7.gt => 
R fml/tests/programs/test_module_specialization1.gt => 
R fml/tests/programs/test_struct1.gt => 
R fml/tests/programs/test_struct2.gt => 
R fml/tests/programs/test_struct3.gt => 
R fml/tests/programs/test_struct4.gt => 
R fml/tests/programs/test_struct5.gt => 
R fml/tests/programs/test_struct6.gt => 
R fml/tests/programs/test_struct7.gt => 
R fml/tests/programs/test_sum1.gt => 
R fml/tests/programs/test_sum2.gt => 
R fml/tests/programs/test_tuple1.gt => 
R fml/tests/programs/test_tuple2.gt => 
R fml/tests/programs/test_tuple3.gt => 
R fml/tests/programs/test_tuple4.gt => 
R fml/tests/programs/test_typedef1.gt => 
R fml/tests/programs/test_typedef2.gt => 
R fml/tests/programs/test_typedef3.gt => 
R fml/tests/programs/test_typedef4.gt => 
R fml/tests/programs/test_typedef5.gt => 
R fml/tests/programs/test_typedef5_failure.gt => 
R fml/tests/programs/test_typedef6.gt => 
R fml/tests/programs/test_typedef7.gt => 
R fml/tests/programs/test_typedef8.gt => 
R fml/tests/programs/test_unnamed_failure1.gt => 
R fml/tests/programs/test_unnamed_failure2.gt => 
R fml/tests/programs/test_unnamed_failure3.gt => 
R fml/tests/programs/test_unnamed_generic.gt => 
M src/passes/generic_infer.rs
R fml/Cargo.lock =>  +0 -320
@@ 1,320 0,0 @@ 
-# This file is automatically @generated by Cargo.
-# It is not intended for manual editing.
-version = 3
-
-[[package]]
-name = "aho-corasick"
-version = "0.7.20"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac"
-dependencies = [
- "memchr",
-]
-
-[[package]]
-name = "argh"
-version = "0.1.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a7e7e4aa7e40747e023c0761dafcb42333a9517575bbf1241747f68dd3177a62"
-dependencies = [
- "argh_derive",
- "argh_shared",
-]
-
-[[package]]
-name = "argh_derive"
-version = "0.1.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "69f2bd7ff6ed6414f4e5521bd509bae46454bbd513801767ced3f21a751ab4bc"
-dependencies = [
- "argh_shared",
- "heck",
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "argh_shared"
-version = "0.1.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "47253b98986dafc7a3e1cf3259194f1f47ac61abb57a57f46ec09e48d004ecda"
-
-[[package]]
-name = "beef"
-version = "0.5.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1"
-
-[[package]]
-name = "codespan-reporting"
-version = "0.11.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"
-dependencies = [
- "termcolor",
- "unicode-width",
-]
-
-[[package]]
-name = "fm"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "671381339b1671872f5725caff3a4bd05be68a0d8adf2d8a7ff6c16395bbe35a"
-dependencies = [
- "regex",
-]
-
-[[package]]
-name = "fml"
-version = "0.1.0"
-dependencies = [
- "argh",
- "codespan-reporting",
- "fnv",
- "lang_tester",
- "logos",
- "once_cell",
-]
-
-[[package]]
-name = "fnv"
-version = "1.0.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
-
-[[package]]
-name = "getopts"
-version = "0.2.21"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5"
-dependencies = [
- "unicode-width",
-]
-
-[[package]]
-name = "heck"
-version = "0.3.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
-dependencies = [
- "unicode-segmentation",
-]
-
-[[package]]
-name = "hermit-abi"
-version = "0.2.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
-dependencies = [
- "libc",
-]
-
-[[package]]
-name = "lang_tester"
-version = "0.7.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d63df63b7380a561f2c38b0a67ebe3a24b87ff2928391d0b5f8f3cfd39fe112c"
-dependencies = [
- "fm",
- "getopts",
- "libc",
- "num_cpus",
- "termcolor",
- "threadpool",
- "wait-timeout",
- "walkdir",
-]
-
-[[package]]
-name = "libc"
-version = "0.2.139"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
-
-[[package]]
-name = "logos"
-version = "0.12.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bf8b031682c67a8e3d5446840f9573eb7fe26efe7ec8d195c9ac4c0647c502f1"
-dependencies = [
- "logos-derive",
-]
-
-[[package]]
-name = "logos-derive"
-version = "0.12.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a1d849148dbaf9661a6151d1ca82b13bb4c4c128146a88d05253b38d4e2f496c"
-dependencies = [
- "beef",
- "fnv",
- "proc-macro2",
- "quote",
- "regex-syntax",
- "syn",
-]
-
-[[package]]
-name = "memchr"
-version = "2.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
-
-[[package]]
-name = "num_cpus"
-version = "1.15.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b"
-dependencies = [
- "hermit-abi",
- "libc",
-]
-
-[[package]]
-name = "once_cell"
-version = "1.13.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1"
-
-[[package]]
-name = "proc-macro2"
-version = "1.0.40"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7"
-dependencies = [
- "unicode-ident",
-]
-
-[[package]]
-name = "quote"
-version = "1.0.20"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804"
-dependencies = [
- "proc-macro2",
-]
-
-[[package]]
-name = "regex"
-version = "1.7.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733"
-dependencies = [
- "aho-corasick",
- "memchr",
- "regex-syntax",
-]
-
-[[package]]
-name = "regex-syntax"
-version = "0.6.27"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244"
-
-[[package]]
-name = "same-file"
-version = "1.0.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
-dependencies = [
- "winapi-util",
-]
-
-[[package]]
-name = "syn"
-version = "1.0.98"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd"
-dependencies = [
- "proc-macro2",
- "quote",
- "unicode-ident",
-]
-
-[[package]]
-name = "termcolor"
-version = "1.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
-dependencies = [
- "winapi-util",
-]
-
-[[package]]
-name = "threadpool"
-version = "1.8.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa"
-dependencies = [
- "num_cpus",
-]
-
-[[package]]
-name = "unicode-ident"
-version = "1.0.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "15c61ba63f9235225a22310255a29b806b907c9b8c964bcbd0a2c70f3f2deea7"
-
-[[package]]
-name = "unicode-segmentation"
-version = "1.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99"
-
-[[package]]
-name = "unicode-width"
-version = "0.1.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
-
-[[package]]
-name = "wait-timeout"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6"
-dependencies = [
- "libc",
-]
-
-[[package]]
-name = "walkdir"
-version = "2.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56"
-dependencies = [
- "same-file",
- "winapi",
- "winapi-util",
-]
-
-[[package]]
-name = "winapi"
-version = "0.3.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
-dependencies = [
- "winapi-i686-pc-windows-gnu",
- "winapi-x86_64-pc-windows-gnu",
-]
-
-[[package]]
-name = "winapi-i686-pc-windows-gnu"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
-
-[[package]]
-name = "winapi-util"
-version = "0.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
-dependencies = [
- "winapi",
-]
-
-[[package]]
-name = "winapi-x86_64-pc-windows-gnu"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

          
R fml/Cargo.toml =>  +0 -25
@@ 1,25 0,0 @@ 
-[package]
-name = "fml"
-version = "0.1.0"
-authors = ["Simon Heath <icefox@dreamquest.io>"]
-edition = "2018"
-
-
-[[bin]]
-name = "fmlc"
-path = "src/main.rs"
-
-[[test]]
-name = "endtoend"
-path = "tests/main.rs"
-harness = false
-
-
-[dependencies]
-argh = "0.1"
-codespan-reporting = "0.11"
-fnv = "1"
-logos = "0.12"
-once_cell = "1"
-lang_tester = "0.7"
-

          
R fml/src/ast.rs =>  +0 -289
@@ 1,289 0,0 @@ 
-//! Abstract syntax tree.
-//!
-//! Should be a *pretty exact* representation of the source code,
-//! including things like parentheses and comments.  That way we can
-//! eventually use the same structure for a code formatter and not
-//! have it nuke anything.
-//!
-//! Though code formatters have different constraints and priorities, if they have line wrapping
-//! and stuff at least.  So, it might not be a particularly great code formatter.
-
-use std::sync::Mutex;
-
-use fnv::FnvHashMap;
-
-use crate::*;
-
-/// Literal value
-#[derive(Debug, Clone, PartialEq)]
-pub enum Literal {
-    /// An integer of some kind
-    Integer(i32),
-    /// A boolean
-    Bool(bool),
-    /// This is kinda weird 'cause we can't parse it,
-    /// but we can lower our enums to it.
-    /// First string is the enum name, second is the value.
-    EnumLit(String, String),
-}
-
-/// A function type signature
-#[derive(Debug, Clone, PartialEq)]
-pub struct Signature<T = Type> {
-    /// Parameters
-    pub params: Vec<(String, T)>,
-    /// Return type
-    pub rettype: T,
-}
-
-impl Signature {
-    /// Turn the function signature into a Lambda
-    pub fn as_type(&self) -> Type {
-        let paramtypes = self.params.iter().map(|(_nm, ty)| ty.clone()).collect();
-        Type::Func(paramtypes, Box::new(self.rettype.clone()))
-    }
-}
-
-#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
-pub struct AstId(usize);
-
-impl AstId {
-    /// Creates a new globally unique AstId
-    fn new() -> AstId {
-        let mut val = AST_ID.lock().unwrap();
-        let new_val = *val + 1;
-        let new_id = AstId(*val);
-        *val = new_val;
-        new_id
-    }
-}
-
-/// An AST node wrapper that contains information
-/// common to all AST nodes.
-#[derive(Debug, Clone, PartialEq)]
-pub struct ExprNode {
-    pub node: Box<Expr>,
-    pub id: AstId,
-}
-
-use once_cell::sync::Lazy;
-static AST_ID: Lazy<Mutex<usize>> = once_cell::sync::Lazy::new(|| Mutex::new(0));
-
-impl ExprNode {
-    pub fn new(expr: Expr) -> Self {
-        let new_id = AstId::new();
-        Self {
-            node: Box::new(expr),
-            id: new_id,
-        }
-    }
-}
-
-/// Any expression.
-/// So, basically anything not a top-level decl.
-#[derive(Debug, Clone, PartialEq)]
-pub enum Expr {
-    Lit {
-        val: Literal,
-    },
-    Var {
-        name: String,
-    },
-    Let {
-        varname: String,
-        typename: Option<Type>,
-        init: ExprNode,
-    },
-    Lambda {
-        signature: Signature,
-        body: Vec<ExprNode>,
-    },
-    Funcall {
-        func: ExprNode,
-        params: Vec<ExprNode>,
-    },
-    TupleCtor {
-        body: Vec<ExprNode>,
-    },
-    StructCtor {
-        body: FnvHashMap<String, ExprNode>,
-    },
-    ArrayCtor {
-        body: Vec<ExprNode>,
-    },
-    // Create a new sum type.
-    // Like EnumLit, this is generated for you by the
-    // compiler.
-    SumCtor {
-        name: String,
-        variant: String,
-        body: ExprNode,
-    },
-    // Wrap a new type.
-    // Like EnumLit, this is generated by the compiler.
-    TypeCtor {
-        name: String,
-        type_params: Vec<Type>,
-        body: ExprNode,
-    },
-    // Opposite of TypeCtor
-    TypeUnwrap {
-        e: ExprNode,
-    },
-    StructRef {
-        e: ExprNode,
-        name: String,
-    },
-    ArrayRef {
-        e: ExprNode,
-        idx: ExprNode,
-    },
-}
-
-impl Expr {
-    /// Shortcut function for making literal bools
-    /// Shortcut function for making literal integers
-    pub const fn int(i: i32) -> Expr {
-        Expr::Lit {
-            val: Literal::Integer(i),
-        }
-    }
-}
-
-/// A top-level declaration in the source file.
-#[derive(Debug, Clone, PartialEq)]
-pub enum Decl {
-    Function {
-        name: String,
-        signature: Signature,
-        body: Vec<ExprNode>,
-    },
-    TypeDef {
-        name: String,
-        params: Vec<String>,
-        ty: Type,
-    },
-    ConstDef {
-        name: String,
-        // ty: Type,
-        init: ExprNode,
-    },
-}
-
-/// A compilable chunk of AST.
-///
-/// Currently, basically a compilation unit.
-#[derive(Debug, Clone, Default, PartialEq)]
-pub struct Ast {
-    pub decls: Vec<Decl>,
-}
-
-/// To implement enum values we just juggle enum decl's
-/// to create a new const def too.
-impl Ast {
-    fn lower_typedef(accm: &mut Vec<Decl>, name: &str, ty: &Type, params: &[String]) {
-        use Decl::*;
-        match ty {
-            // For `type Foo = enum Foo, Bar, Baz end`
-            // synthesize
-            // const Foo = {
-            //     .Foo = <magic unprintable thing>
-            //     .Bar = <magic unprintable thing>
-            //     .Baz = <magic unprintable thing>
-            // }
-            Type::Enum(ts) => {
-                let struct_body: FnvHashMap<_, _> = ts
-                    .iter()
-                    .map(|s| {
-                        let e = ExprNode::new(Expr::Lit {
-                            val: Literal::EnumLit(name.to_owned(), s.clone()),
-                        });
-                        (s.clone(), e)
-                    })
-                    .collect();
-                let init_val = ExprNode::new(Expr::StructCtor { body: struct_body });
-                let new_constdef = ConstDef {
-                    name: name.to_owned(),
-                    init: init_val,
-                };
-                accm.push(new_constdef);
-            }
-            // For `type Foo = sum X {}, Y Thing end`
-            // synthesize
-            // const Foo = {
-            //     .X = fn({}) Foo = <magic unprintable thing> end
-            //     .Y = fn(Thing) Foo = <magic unprintable thing> end
-            // }
-            //
-            // Maybe also something like this???
-            // type X = {}
-            // type Y = Thing
-            // TODO: What do we do with the generics...
-            Type::Sum(body, _generics) => {
-                let struct_body: FnvHashMap<_, _> = body
-                    .iter()
-                    .map(|(variant_name, variant_type)| {
-                        let signature = ast::Signature {
-                            params: vec![("x".into(), variant_type.clone())],
-                            rettype: Type::Named(name.to_owned(), vec![]),
-                        };
-                        // Just return the value passed to it wrapped
-                        // in a constructor of some kind...?
-                        let body = vec![ExprNode::new(Expr::SumCtor {
-                            name: name.into(),
-                            variant: variant_name.into(),
-                            body: ExprNode::new(Expr::Var { name: "x".into() }),
-                        })];
-                        let e = ExprNode::new(Expr::Lambda { signature, body });
-                        //println!("{} is {:#?}", variant_name, e);
-                        (variant_name.clone(), e)
-                    })
-                    .collect();
-                let init_val = ExprNode::new(Expr::StructCtor { body: struct_body });
-                let new_constdef = ConstDef {
-                    name: name.to_owned(),
-                    init: init_val,
-                };
-                accm.push(new_constdef);
-            }
-            // For other types, we create a constructor function to build them.
-            other => {
-                let type_params: Vec<_> =
-                    params.iter().map(|s| Type::Generic(s.to_owned())).collect();
-                let signature = ast::Signature {
-                    params: vec![("x".into(), other.clone())],
-                    rettype: Type::Named(name.to_owned(), type_params.clone()),
-                };
-                // The generated function just returns the value passed to it wrapped
-                // in a type constructor
-                let body = vec![ExprNode::new(Expr::TypeCtor {
-                    name: name.into(),
-                    type_params,
-                    body: ExprNode::new(Expr::Var { name: "x".into() }),
-                })];
-                //println!("{} is {:#?}", variant_name, e);
-                let new_fundecl = Function {
-                    name: name.to_owned(),
-                    signature,
-                    body,
-                };
-                accm.push(new_fundecl);
-            }
-        }
-    }
-    pub fn lower(self) -> Self {
-        let mut out = vec![];
-        for decl in self.decls.into_iter() {
-            use Decl::*;
-            match &decl {
-                Function { .. } => out.push(decl),
-                ConstDef { .. } => out.push(decl),
-                TypeDef { name, ty, params } => {
-                    out.push(decl.clone());
-                    Self::lower_typedef(&mut out, name, ty, params);
-                }
-            }
-        }
-        Ast { decls: out }
-    }
-}

          
R fml/src/lib.rs =>  +0 -188
@@ 1,188 0,0 @@ 
-//! Garnet compiler guts.
-
-use fnv::FnvHashMap;
-
-pub mod ast;
-pub mod parser;
-pub mod typeck;
-
-/*
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
-pub enum PrimType {
-    I32,
-    Bool,
-}
-*/
-
-/// A concrete type that has been fully inferred
-#[derive(Clone, Debug, PartialEq, Eq)]
-pub enum Type {
-    //Primitive(PrimType),
-    /// A C-like enum.
-    /// For now we pretend there's no underlying values attached to
-    /// the name.
-    ///
-    /// ... I seem to have ended up with anonymous enums somehow.
-    /// Not sure how I feel about this.
-    Enum(Vec<String>),
-    Named(String, Vec<Type>),
-    Func(Vec<Type>, Box<Type>),
-    /// The vec is the name of any generic type bindings in there
-    /// 'cause we need to keep track of those apparently.
-    Struct(FnvHashMap<String, Type>, Vec<Type>),
-    /// Sum type.
-    /// I guess this is ok?
-    ///
-    /// Like structs, contains a list of generic bindings.
-    Sum(FnvHashMap<String, Type>, Vec<Type>),
-    /// Arrays are just a type and a number.
-    Array(Box<Type>, usize),
-    /// A generic type parameter
-    Generic(String),
-}
-
-impl Type {
-    /// Search through the type and return any generic types in it.
-    fn collect_generic_names(&self) -> Vec<String> {
-        fn helper(t: &Type, accm: &mut Vec<String>) {
-            match t {
-                //Type::Primitive(_) => (),
-                Type::Enum(_ts) => (),
-                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, generics) => {
-                    for (_, ty) in body {
-                        helper(ty, accm);
-                    }
-                    // This makes me a little uneasy
-                    // 'cause I thiiiink the whole point of the names on
-                    // the struct is to list *all* the generic names in it...
-                    // but we could have nested definitions
-                    // like Foo(@T) ( Bar(@G) ( {@T, @G} ) )
-                    for ty in generics {
-                        helper(ty, accm);
-                    }
-                }
-                // Just like structs
-                Type::Sum(body, generics) => {
-                    for (_, ty) in body {
-                        helper(ty, accm);
-                    }
-                    for ty in generics {
-                        helper(ty, accm);
-                    }
-                }
-                Type::Array(ty, _size) => {
-                    helper(ty, accm);
-                }
-                Type::Generic(s) => {
-                    // Deduplicating these things while maintaining ordering
-                    // is kinda screwy.
-                    // This works, it's just, yanno, also O(n^2)
-                    // Could use a set to check membership , but fuckit for now.
-                    if !accm.contains(s) {
-                        accm.push(s.clone());
-                    }
-                }
-            }
-        }
-        let mut accm = vec![];
-        helper(self, &mut accm);
-        accm
-    }
-
-    /// Returns the type parameters *specified by the toplevel type*.
-    /// Does *not* recurse to all types below it!
-    /// ...except for function args apparently.
-    fn get_type_params(&self) -> Vec<String> {
-        fn helper(t: &Type, accm: &mut Vec<String>) {
-            match t {
-                //Type::Primitive(_) => (),
-                Type::Enum(_ts) => (),
-                Type::Named(_, generics) => {
-                    for g in generics {
-                        helper(g, accm);
-                    }
-                }
-                Type::Func(args, rettype) => {
-                    for t in args {
-                        helper(t, accm);
-                    }
-                    helper(rettype, accm)
-                }
-                Type::Struct(_body, generics) => {
-                    for g in generics {
-                        helper(g, accm);
-                    }
-                }
-                Type::Sum(_body, generics) => {
-                    for g in generics {
-                        helper(g, accm);
-                    }
-                }
-                Type::Array(ty, _size) => {
-                    helper(ty, accm);
-                }
-                Type::Generic(s) => {
-                    // Deduplicating these things while maintaining ordering
-                    // is kinda screwy.
-                    // This works, it's just, yanno, also O(n^2)
-                    // Could use a set to check membership , but fuckit for now.
-                    if !accm.contains(s) {
-                        accm.push(s.clone())
-                    }
-                }
-            }
-        }
-        let mut accm = vec![];
-        helper(self, &mut accm);
-        println!("Found type params for {:?}: {:?}", self, accm);
-        accm
-    }
-}
-
-/// A identifier to uniquely refer to our type terms
-#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
-pub struct TypeId(usize);
-
-/// Information about a type term
-#[derive(Clone, Debug, PartialEq, Eq)]
-pub enum TypeInfo {
-    /// No information about the type of this type term
-    Unknown,
-    /// This type term is the same as another type term
-    Ref(TypeId),
-    Enum(Vec<String>),
-    /// N-ary type constructor.
-    /// It could be Int()
-    /// or List(Int)
-    /// or List(T)
-    Named(String, Vec<TypeId>),
-    /// This type term is definitely a function
-    Func(Vec<TypeId>, TypeId),
-    /// This is definitely some kind of struct
-    Struct(FnvHashMap<String, TypeId>),
-    /// Definitely a sum type
-    Sum(FnvHashMap<String, TypeId>),
-    Array(TypeId, usize),
-    /// This is some generic type that has a name like @A
-    /// AKA a type parameter.
-    TypeParam(String),
-}
-
-pub fn compile(filename: &str, src: &str) -> Vec<u8> {
-    let mut parser = parser::Parser::new(filename, src);
-    let ast = parser.parse();
-    let ast2 = ast.lower();
-    typeck::typecheck(&ast2);
-    vec![]
-}

          
R fml/src/main.rs =>  +0 -79
@@ 1,79 0,0 @@ 
-//! The main compiler driver program.
-
-use std::path::PathBuf;
-
-use argh::FromArgs;
-
-/// compiler
-#[derive(Debug, FromArgs)]
-struct Opt {
-    /// save generated Rust code?
-    //#[argh(switch, short = 's')]
-    //save: bool,
-
-    /// run resulting program immediately, handy for unit tests.  Does not produce an executable.
-    //#[argh(switch, short = 'r')]
-    //run: bool,
-
-    /// output file name
-    //#[argh(option, short = 'o')]
-    //out: Option<PathBuf>,
-
-    /// input files
-    #[argh(positional)]
-    file: PathBuf,
-}
-
-fn main() -> std::io::Result<()> {
-    //let opt = parse_args();
-    let opt: Opt = argh::from_env();
-
-    let src = std::fs::read_to_string(&opt.file)?;
-    let _output = fml::compile(&opt.file.to_str().unwrap(), &src);
-    /*
-    let mut rust_file;
-    // Output to file
-    {
-        rust_file = opt.file.clone();
-        rust_file.set_extension("rs");
-        std::fs::write(&rust_file, &output)?;
-    }
-    // Invoke rustc
-    let exe_file = if let Some(out) = opt.out {
-        out
-    } else {
-        let mut exe_file = opt.file.clone();
-        exe_file.set_extension(std::env::consts::EXE_EXTENSION);
-        exe_file
-    };
-    use std::process::{Command, Stdio};
-    let res = Command::new("rustc")
-        .stdin(Stdio::null())
-        .stdout(Stdio::inherit())
-        .arg("-o")
-        .arg(&exe_file)
-        .arg(&rust_file)
-        .output()
-        .expect("Failed to execute rustc");
-    if !res.status.success() {
-        dbg!(&res);
-        panic!("Generated Rust code that could not be compiled");
-    }
-
-    // delete Rust files if we want to
-    if !opt.save {
-        std::fs::remove_file(rust_file).unwrap();
-    }
-
-    // Run output program if we want to
-    if opt.run {
-        let res = Command::new(&exe_file)
-            .stdin(Stdio::inherit())
-            .stdout(Stdio::inherit())
-            .output();
-        std::fs::remove_file(&exe_file).unwrap();
-        res.expect("Failed to run program");
-    }
-    */
-    Ok(())
-}

          
R fml/src/parser.rs =>  +0 -896
@@ 1,896 0,0 @@ 
-use std::mem::Discriminant as Discr;
-use std::ops::Range;
-
-use codespan_reporting as cs;
-use codespan_reporting::diagnostic::{Diagnostic, Label};
-use fnv::FnvHashMap;
-use logos::{Lexer, Logos};
-
-use crate::ast;
-use crate::*;
-
-fn eat_block_comment(lex: &mut Lexer<TokenKind>) -> String {
-    let mut nest_depth = 1;
-    while nest_depth != 0 {
-        const DELIMITER_BYTES: usize = 2;
-        let next_bit = &lex.remainder().get(..DELIMITER_BYTES);
-        // Lexer::bump() works in bytes, not chars, so we have to track the
-        // number of bytes we are stepping forward so we don't try to lex
-        // the middle of a UTF-8 char.
-        let bytes_to_advance = match next_bit {
-            Some("/-") => {
-                nest_depth += 1;
-                DELIMITER_BYTES
-            }
-            Some("-/") => {
-                nest_depth -= 1;
-                DELIMITER_BYTES
-            }
-            Some(other) => other
-                .chars()
-                .next()
-                .unwrap_or_else(|| panic!("Invalid UTF-8 in input file?  This should probably never happen otherwise."))
-                .len_utf8(),
-            None => panic!("Unclosed block comment?"),
-        };
-        lex.bump(bytes_to_advance);
-    }
-    String::from("")
-}
-
-#[allow(missing_docs)]
-#[derive(Logos, Debug, PartialEq, Clone)]
-pub enum TokenKind {
-    #[regex("[a-zA-Z_][a-zA-Z0-9_]*", |lex| lex.slice().to_owned())]
-    Ident(String),
-    #[regex("true|false", |lex| lex.slice().parse())]
-    Bool(bool),
-    #[regex("[0-9][0-9_]*", |lex| lex.slice().parse())]
-    Integer(i32),
-
-    // Decl stuff
-    #[token("fn")]
-    Fn,
-    #[token("type")]
-    Type,
-
-    // Keywords
-    #[token("let")]
-    Let,
-    #[token("end")]
-    End,
-    #[token("struct")]
-    Struct,
-    #[token("const")]
-    Const,
-    #[token("enum")]
-    Enum,
-    #[token("sum")]
-    Sum,
-
-    // Punctuation
-    #[token("(")]
-    LParen,
-    #[token(")")]
-    RParen,
-    #[token("{")]
-    LBrace,
-    #[token("}")]
-    RBrace,
-    #[token("[")]
-    LBracket,
-    #[token("]")]
-    RBracket,
-    #[token(",")]
-    Comma,
-    #[token(".")]
-    Period,
-    #[token(":")]
-    Colon,
-    #[token(";")]
-    Semicolon,
-    #[token("=")]
-    Equals,
-    #[token("@")]
-    At,
-    #[token("$")]
-    Dollar,
-
-    // We save comment strings so we can use this same
-    // parser as a reformatter or such.
-    #[regex(r"--.*\n", |lex| lex.slice().to_owned())]
-    #[regex(r"/-.*", eat_block_comment)]
-    Comment(String),
-
-    #[error]
-    #[regex(r"[ \t\n\f]+", logos::skip)]
-    Error,
-}
-
-impl TokenKind {
-    /// Shortcut for std::mem::discriminant()
-    fn discr(&self) -> Discr<Self> {
-        std::mem::discriminant(self)
-    }
-}
-
-#[derive(Debug, PartialEq, Clone)]
-pub struct Token {
-    pub kind: TokenKind,
-    pub span: Range<usize>,
-}
-
-impl Token {
-    fn new(kind: TokenKind, span: Range<usize>) -> Self {
-        Token { kind, span }
-    }
-}
-
-use self::TokenKind as T;
-
-// This is not dead code but sometimes cargo thinks some of it fields are, since their usage is
-// cfg'd out in unit tests.
-#[allow(dead_code)]
-struct ErrorReporter {
-    files: cs::files::SimpleFiles<String, String>,
-    file_id: usize,
-    config: cs::term::Config,
-}
-
-impl ErrorReporter {
-    fn new(filename: &str, src: &str) -> Self {
-        use codespan_reporting::files::SimpleFiles;
-        let mut files = SimpleFiles::new();
-        let file_id = files.add(filename.to_owned(), src.to_owned());
-
-        Self {
-            files,
-            file_id,
-            config: codespan_reporting::term::Config::default(),
-        }
-    }
-
-    fn error(&self, _diag: &Diagnostic<usize>) -> ! {
-        #[cfg(not(test))]
-        {
-            use codespan_reporting::term::termcolor::{ColorChoice, StandardStream};
-            let writer = StandardStream::stderr(ColorChoice::Always);
-            cs::term::emit(&mut writer.lock(), &self.config, &self.files, _diag)
-                .expect("Could not print error message");
-        }
-        panic!("Error somewhere in parser")
-    }
-}
-
-/// The core parser struct.  It provides basic methods for
-/// manipulating the input stream, and the `parse()` method to
-/// try to drive the given input to completion.
-pub struct Parser<'input> {
-    lex: std::iter::Peekable<logos::SpannedIter<'input, TokenKind>>,
-    source: &'input str,
-    err: ErrorReporter,
-}
-
-impl<'input> Parser<'input> {
-    pub fn new(filename: &str, source: &'input str) -> Self {
-        let lex = TokenKind::lexer(source).spanned().peekable();
-        let err = ErrorReporter::new(filename, source);
-        Parser { lex, source, err }
-    }
-
-    /// Read all its input and returns an Ast.
-    ///
-    /// Currently just panics on error.
-    pub fn parse(&mut self) -> ast::Ast {
-        let mut decls = vec![];
-        while let Some(d) = self.parse_decl() {
-            decls.push(d);
-        }
-        ast::Ast { decls }
-    }
-
-    /// Returns the next token, with span.
-    fn next(&mut self) -> Option<Token> {
-        let t = self.lex.next().map(|(tok, span)| Token::new(tok, span));
-        match t {
-            // Recurse to skip comments
-            Some(Token {
-                kind: T::Comment(_),
-                ..
-            }) => self.next(),
-            _ => t,
-        }
-    }
-
-    /// Peeks the next token, with span.
-    fn peek(&mut self) -> Option<Token> {
-        let t = self
-            .lex
-            .peek()
-            .map(|(tok, span)| Token::new(tok.clone(), span.clone()));
-        match t {
-            // Skip comments
-            Some(Token {
-                kind: T::Comment(_),
-                ..
-            }) => {
-                // This must be self.lex.next(), not just self.next().
-                let _ = self.lex.next();
-                self.peek()
-            }
-            _ => t,
-        }
-    }
-
-    /// Takes a string describing what it expected, and a token that is what
-    /// it actually got.
-    fn error(&self, expected: &str, token_or_eof: Option<Token>) -> ! {
-        let diag = if let Some(got) = token_or_eof {
-            let msg = format!(
-                "Parse error on token {:?} from str {}.  Expected {}",
-                got.kind,
-                &self.source[got.span.clone()],
-                expected
-            );
-            Diagnostic::error()
-                .with_message(msg)
-                .with_labels(vec![Label::primary(self.err.file_id, got.span)])
-        } else {
-            let len = self.source.len();
-            let msg = format!("Parse error on end of file.  Expected {}", expected);
-            Diagnostic::error()
-                .with_message(msg)
-                .with_labels(vec![Label::primary(self.err.file_id, len..len)])
-        };
-        self.err.error(&diag);
-    }
-
-    /// Consume a token, we don't care what it is.
-    /// Presumably because we've already peeked at it.
-    fn drop(&mut self) {
-        self.next();
-    }
-
-    /// Consume a token that doesn't return anything
-    fn expect(&mut self, expected: TokenKind) {
-        match self.next() {
-            Some(t) if t.kind == expected => (),
-            Some(t) => {
-                let msg = format!(
-                    "Parse error on token {:?} from str {}.  Expected token: {:?}",
-                    t.kind,
-                    &self.source[t.span.clone()],
-                    expected
-                );
-                let diag = Diagnostic::error()
-                    .with_message(msg)
-                    .with_labels(vec![Label::primary(self.err.file_id, t.span)]);
-
-                self.err.error(&diag);
-            }
-            None => {
-                let msg = format!(
-                    "Parse error: Got end of input or malformed token.  Expected token: {:?}",
-                    expected
-                );
-                let diag = Diagnostic::error()
-                    .with_message(msg)
-                    .with_labels(vec![Label::primary(self.err.file_id, 0..0)]);
-
-                self.err.error(&diag);
-            }
-        }
-    }
-
-    /// Returns whether the next token in the stream is what is expected.
-    ///
-    /// Basically, we can't (easily) pass in a strongly-typed enum discriminant
-    /// and have only that checked, we have to pass the full enum.
-    /// Ironically, this is exactly the sort of thing I want Garnet to be
-    /// able to do nicely.
-    ///
-    /// ...double ironically, the above paragraph is incorrect as of 1.21,
-    /// because std::mem::discriminant() is better than I thought.
-    fn peek_is(&mut self, expected: Discr<TokenKind>) -> bool {
-        if let Some(got) = self.peek() {
-            std::mem::discriminant(&got.kind) == expected
-        } else {
-            false
-        }
-    }
-
-    /// Returns whether the next token in the stream is what is expected,
-    /// and consume it if so.
-    ///
-    /// I ended up seeing a lot of `if self.peek_is(thing) { self.expect(thing)`
-    /// so maybe this helps.
-    fn peek_expect(&mut self, expected: Discr<TokenKind>) -> bool {
-        if self.peek_is(expected) {
-            self.drop();
-            true
-        } else {
-            false
-        }
-    }
-
-    /// Consume an identifier and return its interned symbol.
-    /// Note this returns a String, not a TypeInfo...
-    fn expect_ident(&mut self) -> String {
-        match self.next() {
-            Some(Token {
-                kind: T::Ident(s), ..
-            }) => s.to_string(),
-            Some(Token { kind, span }) => {
-                let msg = format!("Parse error: got token {:?}.  Expected identifier.", kind,);
-                let diag = Diagnostic::error()
-                    .with_message(msg)
-                    .with_labels(vec![Label::primary(self.err.file_id, span)]);
-
-                self.err.error(&diag);
-            }
-            None => {
-                let msg = format!(
-                    "Parse error: Got end of input or malformed token.  Expected identifier",
-                );
-                let diag = Diagnostic::error()
-                    .with_message(msg)
-                    .with_labels(vec![Label::primary(self.err.file_id, 0..0)]);
-
-                self.err.error(&diag);
-            }
-        }
-    }
-
-    /// Consumes a number and returns it.
-    fn expect_int(&mut self) -> i32 {
-        match self.next() {
-            Some(Token {
-                kind: T::Integer(s),
-                ..
-            }) => s,
-            Some(Token { kind, span }) => {
-                let msg = format!("Parse error: got token {:?}.  Expected integer.", kind,);
-                let diag = Diagnostic::error()
-                    .with_message(msg)
-                    .with_labels(vec![Label::primary(self.err.file_id, span)]);
-
-                self.err.error(&diag);
-            }
-            None => {
-                let msg = format!(
-                    "Parse error: Got end of input or malformed token.  Expected integer.",
-                );
-                let diag = Diagnostic::error()
-                    .with_message(msg)
-                    .with_labels(vec![Label::primary(self.err.file_id, 0..0)]);
-
-                self.err.error(&diag);
-            }
-        }
-    }
-
-    /// Returns None on EOF.
-    fn parse_decl(&mut self) -> Option<ast::Decl> {
-        match self.next() {
-            Some(Token { kind: T::Fn, .. }) => Some(self.parse_fn()),
-            Some(Token { kind: T::Type, .. }) => Some(self.parse_typedef()),
-            Some(Token { kind: T::Const, .. }) => Some(self.parse_const()),
-            Some(other) => self.error("start of decl", Some(other)),
-            None => None,
-        }
-    }
-
-    /// typedef = "type" ident "=" type
-    fn parse_typedef(&mut self) -> ast::Decl {
-        let name = self.expect_ident();
-        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();
-        ast::Decl::TypeDef { name, params, ty }
-    }
-
-    fn parse_const(&mut self) -> ast::Decl {
-        let name = self.expect_ident();
-        self.expect(T::Equals);
-        let init = self
-            .parse_expr(0)
-            .expect("Expected expression after `const ... =`, did not get one");
-        ast::Decl::ConstDef { name, init }
-    }
-
-    fn parse_fn(&mut self) -> ast::Decl {
-        let name = self.expect_ident();
-        let signature = ast::Signature {
-            ..self.parse_fn_signature()
-        };
-        self.expect(T::Equals);
-        let body = self.parse_exprs();
-        self.expect(T::End);
-        ast::Decl::Function {
-            name,
-            signature,
-            body,
-        }
-    }
-
-    /// signature = fn_args [":" typename]
-    fn parse_fn_signature(&mut self) -> ast::Signature {
-        let params = self.parse_fn_args();
-        let rettype = self.parse_type();
-        ast::Signature { params, rettype }
-    }
-
-    /// sig = ident ":" typename
-    /// fn_args = "(" [sig {"," sig} [","]] ")"
-    fn parse_fn_args(&mut self) -> Vec<(String, Type)> {
-        let mut args = vec![];
-        self.expect(T::LParen);
-
-        while let Some((T::Ident(_i), _span)) = self.lex.peek() {
-            let name = self.expect_ident();
-            let tname = self.parse_type();
-            args.push((name, tname));
-
-            if self.peek_expect(T::Comma.discr()) {
-            } else {
-                break;
-            }
-        }
-        self.expect(T::RParen);
-        args
-    }
-
-    fn parse_fn_type(&mut self) -> Type {
-        let params = self.parse_type_list();
-        let rettype = self.parse_type();
-        Type::Func(params, Box::new(rettype))
-    }
-
-    /// type_list = "(" [type {"," type} [","] ")"
-    fn parse_type_list(&mut self) -> Vec<Type> {
-        let mut args = vec![];
-        self.expect(T::LParen);
-
-        while !self.peek_is(T::RParen.discr()) {
-            let tname = self.parse_type();
-            args.push(tname);
-
-            if self.peek_expect(T::Comma.discr()) {
-            } else {
-                break;
-            }
-        }
-        self.expect(T::RParen);
-        args
-    }
-
-    fn parse_tuple_type(&mut self) -> Type {
-        let mut body = vec![];
-        while !self.peek_is(T::RBrace.discr()) {
-            let t = self.parse_type();
-            body.push(t);
-            if self.peek_expect(T::Comma.discr()) {
-            } else {
-                break;
-            }
-        }
-        self.expect(T::RBrace);
-        Type::Named("Tuple".to_string(), body)
-    }
-
-    fn parse_exprs(&mut self) -> Vec<ast::ExprNode> {
-        let mut exprs = vec![];
-        let tok = self.peek();
-        while let Some(e) = self.parse_expr(0) {
-            // Hey and if we have a semicolon after an expr we can just eat it
-            self.peek_expect(T::Semicolon.discr());
-            exprs.push(e);
-        }
-        if exprs.is_empty() {
-            self.error(
-                "non-empty expression block, must have at least one value.",
-                tok,
-            );
-        }
-        exprs
-    }
-
-    /// Returns None if there is no valid expression,
-    /// which usually means the end of a block or such.
-    ///
-    /// This departs from pure recursive descent and uses a Pratt
-    /// parser to parse math expressions and such.
-    fn parse_expr(&mut self, min_bp: usize) -> Option<ast::ExprNode> {
-        let t = self.peek()?;
-        let token = &t.kind;
-        let mut lhs = match token {
-            T::Integer(_) => ast::ExprNode::new(ast::Expr::int(self.expect_int())),
-            T::Bool(b) => {
-                self.drop();
-                ast::ExprNode::new(ast::Expr::Lit {
-                    val: ast::Literal::Bool(*b),
-                })
-            }
-            T::Ident(_) => {
-                let ident = self.expect_ident();
-                ast::ExprNode::new(ast::Expr::Var { name: ident })
-            }
-            T::Struct => self.parse_struct_literal(),
-            T::Let => self.parse_let(),
-            T::Fn => self.parse_lambda(),
-            // Parenthesized expr's
-            T::LParen => {
-                self.drop();
-                let lhs = self.parse_expr(0)?;
-                self.expect(T::RParen);
-                lhs
-            }
-            // Tuple or struct literal
-            T::LBrace => self.parse_constructor(),
-            // Array literal
-            T::LBracket => self.parse_array_constructor(),
-
-            // Something else not a valid expr
-            _x => return None,
-        };
-        // Parse a postfix or infix expression with a given
-        // binding power or greater.
-        while let Some((op_token, _span)) = self.lex.peek().cloned() {
-            // Is our token a postfix op?
-            if let Some((l_bp, ())) = postfix_binding_power(&op_token) {
-                if l_bp < min_bp {
-                    break;
-                }
-                lhs = match op_token {
-                    T::LParen => {
-                        let params = self.parse_function_args();
-                        ast::ExprNode::new(ast::Expr::Funcall { func: lhs, params })
-                    }
-                    // If we see `foo {bar}`
-                    // then parse it as `foo({bar})`
-                    // Thanks Lua!!!
-                    T::LBrace => {
-                        let params = self.parse_constructor();
-                        ast::ExprNode::new(ast::Expr::Funcall {
-                            func: lhs,
-                            params: vec![params],
-                        })
-                    }
-                    T::Period => {
-                        self.expect(T::Period);
-                        let struct_field_name = self.expect_ident();
-                        ast::ExprNode::new(ast::Expr::StructRef {
-                            e: lhs,
-                            name: struct_field_name,
-                        })
-                    }
-                    T::Dollar => {
-                        self.expect(T::Dollar);
-                        ast::ExprNode::new(ast::Expr::TypeUnwrap { e: lhs })
-                    }
-                    _ => return None,
-                };
-                continue;
-            }
-            /*
-            // Is our token an infix op?
-            if let Some((l_bp, r_bp)) = infix_binding_power(&op_token) {
-                if l_bp < min_bp {
-                    break;
-                }
-                self.drop();
-                let rhs = self.parse_expr(r_bp).unwrap();
-                // Not all things that parse like binary operations
-                // actually produce the "BinOp" expression type.
-                lhs = match op_token {
-                    // x = y
-                    T::Equals => ast::Expr::Assign {
-                        lhs: Box::new(lhs),
-                        rhs: Box::new(rhs),
-                    },
-                    _ => {
-                        let bop = bop_for(&op_token).unwrap();
-                        ast::Expr::BinOp {
-                            op: bop,
-                            lhs: Box::new(lhs),
-                            rhs: Box::new(rhs),
-                        }
-                    }
-                };
-                continue;
-            }
-            */
-            // None of the above, so we are done parsing the expr
-            break;
-        }
-        Some(lhs)
-    }
-
-    fn parse_function_args(&mut self) -> Vec<ast::ExprNode> {
-        let mut params = vec![];
-        self.expect(T::LParen);
-        while let Some(expr) = self.parse_expr(0) {
-            params.push(expr);
-            if !self.peek_is(T::Comma.discr()) {
-                break;
-            }
-            self.expect(T::Comma);
-        }
-        self.expect(T::RParen);
-        params
-    }
-
-    /// let = "let" ident ":" typename "=" expr
-    fn parse_let(&mut self) -> ast::ExprNode {
-        self.expect(T::Let);
-        let varname = self.expect_ident();
-        let typename = if self.peek_expect(T::Equals.discr()) {
-            None
-        } else {
-            let t = Some(self.parse_type());
-            self.expect(T::Equals);
-            t
-        };
-        let init = self
-            .parse_expr(0)
-            .expect("Expected expression after `let ... =`, did not get one");
-        ast::ExprNode::new(ast::Expr::Let {
-            varname,
-            typename,
-            init,
-        })
-    }
-
-    /// lambda = "fn" "(" ...args... ")" [":" typename] = {exprs} "end"
-    fn parse_lambda(&mut self) -> ast::ExprNode {
-        self.expect(T::Fn);
-        let signature = self.parse_fn_signature();
-        self.expect(T::Equals);
-        let body = self.parse_exprs();
-        self.expect(T::End);
-        ast::ExprNode::new(ast::Expr::Lambda { signature, body })
-    }
-
-    fn parse_array_constructor(&mut self) -> ast::ExprNode {
-        self.expect(T::LBracket);
-        let mut body = vec![];
-        while let Some(expr) = self.parse_expr(0) {
-            body.push(expr);
-
-            if self.peek_expect(T::Comma.discr()) {
-            } else {
-                break;
-            }
-        }
-        self.expect(T::RBracket);
-        ast::ExprNode::new(ast::Expr::ArrayCtor { body })
-    }
-
-    /// If we see `.foo = bar` in our thing then it's a struct
-    fn parse_constructor(&mut self) -> ast::ExprNode {
-        self.expect(T::LBrace);
-        if self.peek_is(T::Period.discr()) {
-            self.parse_struct_literal()
-        } else {
-            self.parse_tuple_literal()
-        }
-    }
-
-    /// struct constructor = "{" "." ident "=" expr {"," ...} "}"
-    fn parse_struct_literal(&mut self) -> ast::ExprNode {
-        let body = self.parse_struct_lit_fields();
-        self.expect(T::RBrace);
-        ast::ExprNode::new(ast::Expr::StructCtor { body })
-    }
-
-    /// tuple constructor = "{" [expr {"," expr} [","] "}"
-    fn parse_tuple_literal(&mut self) -> ast::ExprNode {
-        let mut body = vec![];
-        while let Some(expr) = self.parse_expr(0) {
-            body.push(expr);
-
-            if self.peek_expect(T::Comma.discr()) {
-            } else {
-                break;
-            }
-        }
-        self.expect(T::RBrace);
-        ast::ExprNode::new(ast::Expr::TupleCtor { body })
-    }
-
-    fn parse_struct_lit_fields(&mut self) -> FnvHashMap<String, ast::ExprNode> {
-        let mut fields = FnvHashMap::default();
-
-        loop {
-            if self.peek_expect(T::Period.discr()) {
-                let name = self.expect_ident();
-                self.expect(T::Equals);
-                let vl = self.parse_expr(0).unwrap();
-                fields.insert(name, vl);
-            } else {
-                break;
-            }
-
-            if self.peek_expect(T::Comma.discr()) {
-            } else {
-                break;
-            }
-        }
-        fields
-    }
-
-    fn parse_type(&mut self) -> Type {
-        let t = self.next().unwrap_or_else(|| self.error("type", None));
-        let mut ty = match t.kind {
-            T::Ident(ref s) => {
-                let type_params = if self.peek_is(T::LParen.discr()) {
-                    self.parse_type_list()
-                } else {
-                    vec![]
-                };
-                Type::Named(s.clone(), type_params)
-            }
-            T::At => {
-                let s = self.expect_ident();
-                Type::Generic(s)
-            }
-            T::LBrace => {
-                let tuptype = self.parse_tuple_type();
-                tuptype
-            }
-            T::Fn => {
-                let fntype = self.parse_fn_type();
-                fntype
-            }
-            T::Struct => self.parse_struct_type(),
-            T::Enum => self.parse_enum_type(),
-            T::Sum => self.parse_sum_type(),
-            // Apparently all our types are prefix, which is weird.
-            // Fix this someday.
-            _ => self.error("type", Some(t)),
-        };
-        // Array?  (Or other postfix shit, this be gettin whack)
-        while self.peek_is(T::LBracket.discr()) {
-            self.expect(T::LBracket);
-            let len = self.expect_int();
-            assert!(len >= 0);
-            self.expect(T::RBracket);
-            ty = Type::Array(Box::new(ty), len as usize);
-        }
-        ty
-    }
-
-    /// isomorphic-ish with parse_type_list()
-    fn parse_struct_type(&mut self) -> Type {
-        let mut fields = FnvHashMap::default();
-
-        loop {
-            match self.lex.peek() {
-                Some((T::Ident(_i), _span)) => {
-                    let name = self.expect_ident();
-                    self.expect(T::Colon);
-                    let vl = self.parse_type();
-                    fields.insert(name, vl);
-                }
-                _ => break,
-            }
-
-            if self.peek_expect(T::Comma.discr()) {
-            } else {
-                break;
-            }
-        }
-        self.expect(T::End);
-        // Pull any @Foo types out of the structure's
-        // declared types
-        // To do this we have to recurse down into any
-        // complex types THOSE may define (such as functions)
-        // and pull out those types too.
-        //
-        // This feels kinda jank but appears to work
-        let generic_names: Vec<_> = fields
-            .iter()
-            .map(|(_nm, ty)| ty.get_type_params())
-            .flatten()
-            .map(|ty| Type::Generic(ty))
-            .collect();
-        Type::Struct(fields, generic_names)
-    }
-
-    /// isomorphic with parse_type_list()
-    fn parse_enum_type(&mut self) -> Type {
-        let mut fields = vec![];
-        while !self.peek_is(T::End.discr()) {
-            let field = self.expect_ident();
-            fields.push(field);
-
-            if self.peek_expect(T::Comma.discr()) {
-            } else {
-                break;
-            }
-        }
-        self.expect(T::End);
-        Type::Enum(fields)
-    }
-
-    /// isomorphic-ish with parse_type_list()
-    fn parse_sum_type(&mut self) -> Type {
-        let mut fields = FnvHashMap::default();
-        while !self.peek_is(T::End.discr()) {
-            let field = self.expect_ident();
-            let ty = self.parse_type();
-            fields.insert(field, ty);
-
-            if self.peek_expect(T::Comma.discr()) {
-            } else {
-                break;
-            }
-        }
-        self.expect(T::End);
-        // Pull any @Foo types out of the structure's
-        // declared types, again just like parse_struct_type above.
-        let generic_names: Vec<_> = fields
-            .iter()
-            .map(|(_nm, ty)| ty.get_type_params())
-            .flatten()
-            .map(|ty| Type::Generic(ty))
-            .collect();
-        Type::Sum(fields, generic_names)
-    }
-}
-
-/// Specifies binding power of postfix operators.
-///
-/// No idea if the precedences for these are sensible,
-/// I think they only actually relate with infix operators,
-/// not each other.
-fn postfix_binding_power(op: &TokenKind) -> Option<(usize, ())> {
-    match op {
-        // "$" is our "type unwrap" operator, which is weird but ok
-        T::Dollar => Some((130, ())),
-        // "(" opening function call args
-        T::LParen => Some((120, ())),
-        // "{" opening function call for single struct arg
-        T::LBrace => Some((119, ())),
-        // "." separating struct refs a la foo.bar
-        T::Period => Some((110, ())),
-        // "[" opening array dereference
-        T::LBracket => Some((100, ())),
-        _x => None,
-    }
-}
-
-/*
-/// Specifies binding power of infix operators.
-/// The binding power on one side should always be trivially
-/// greater than the other, so there's never ambiguity.
-fn infix_binding_power(op: &TokenKind) -> Option<(usize, usize)> {
-    // Right associations are slightly more powerful so we always produce
-    // a deterministic tree.
-    match op {
-        T::Mul | T::Div | T::Mod => Some((100, 101)),
-        T::Plus | T::Minus => Some((90, 91)),
-        T::Lt | T::Gt | T::Lte | T::Gte => Some((80, 81)),
-        T::Equal | T::NotEqual => Some((70, 71)),
-        T::And => Some((60, 61)),
-        // Logical xor has same precedence as or, I guess?  It's sorta an odd duck.
-        T::Or | T::Xor => Some((50, 51)),
-        // Assignment
-        T::Equals => Some((10, 11)),
-
-        _ => None,
-    }
-}
-*/

          
R fml/src/typeck.rs =>  +0 -851
@@ 1,851 0,0 @@ 
-use std::cell::RefCell;
-use std::rc::Rc;
-
-use fnv::{FnvHashMap, FnvHashSet};
-
-use crate::*;
-
-/// Type checking engine
-#[derive(Default)]
-struct Tck {
-    /// Used to generate unique IDs
-    id_counter: usize,
-    /// Binding from type vars to what we know about the type
-    vars: FnvHashMap<TypeId, TypeInfo>,
-    /// What we know about the type of each node in the AST.
-    types: FnvHashMap<ast::AstId, TypeId>,
-}
-
-impl Tck {
-    /// Save the type variable associated with the given expr
-    fn set_expr_type(&mut self, expr: &ast::ExprNode, ty: TypeId) {
-        assert!(
-            self.types.get(&expr.id).is_none(),
-            "Redefining known type, not suuuuure if this is bad or not.  Probably is though, since we should always be changing the meaning of an expr's associated type variable instead."
-        );
-        self.types.insert(expr.id, ty);
-    }
-
-    /// Panics if the expression's type is not set.
-    fn get_expr_type(&mut self, expr: &ast::ExprNode) -> TypeId {
-        *self.types.get(&expr.id).unwrap_or_else(|| {
-            panic!(
-                "Could not get type of expr with ID {:?}!\nExpr was {:?}",
-                expr.id, expr
-            )
-        })
-    }
-
-    /// Create a new type term with whatever we have about its type
-    pub fn insert(&mut self, info: TypeInfo) -> TypeId {
-        // Generate a new ID for our type term
-        self.id_counter += 1;
-        let id = TypeId(self.id_counter);
-        assert!(self.vars.get(&id).is_none(), "Can't happen");
-        self.vars.insert(id, info);
-        id
-    }
-
-    /// Create a new type term out of a known type, such as if we
-    /// declare a var's type.
-    pub fn insert_known(&mut self, t: &Type) -> TypeId {
-        // Recursively insert all subtypes.
-        let tinfo = match t {
-            //Type::Primitive(_) => todo!(),
-            Type::Enum(vals) => TypeInfo::Enum(vals.clone()),
-            Type::Named(s, args) => {
-                let new_args = args.iter().map(|t| self.insert_known(t)).collect();
-                TypeInfo::Named(s.clone(), new_args)
-            }
-            Type::Func(args, rettype) => {
-                let new_args = args.iter().map(|t| self.insert_known(t)).collect();
-                let new_rettype = self.insert_known(rettype);
-                TypeInfo::Func(new_args, new_rettype)
-            }
-            Type::Generic(s) => TypeInfo::TypeParam(s.to_string()),
-            Type::Array(ty, len) => {
-                let new_body = self.insert_known(&ty);
-                TypeInfo::Array(new_body, *len)
-            }
-            // TODO: Generics?
-            Type::Struct(body, _names) => {
-                let new_body = body
-                    .iter()
-                    .map(|(nm, t)| (nm.clone(), self.insert_known(t)))
-                    .collect();
-                TypeInfo::Struct(new_body)
-            }
-            // TODO: Generics?
-            Type::Sum(body, _names) => {
-                let new_body = body
-                    .iter()
-                    .map(|(nm, t)| (nm.clone(), self.insert_known(t)))
-                    .collect();
-                TypeInfo::Sum(new_body)
-            }
-        };
-        self.insert(tinfo)
-    }
-
-    /// Panics on invalid field name or not a struct type
-    pub fn get_struct_field_type(
-        &mut self,
-        symtbl: &Symtbl,
-        struct_type: TypeId,
-        field_name: &str,
-    ) -> TypeId {
-        use TypeInfo::*;
-        match self.vars[&struct_type].clone() {
-            Ref(t) => self.get_struct_field_type(symtbl, t, field_name),
-            Struct(body) => body.get(field_name).cloned().unwrap_or_else(|| {
-                panic!(
-                    "Struct has no field {}, valid fields are: {:#?}",
-                    field_name, body
-                )
-            }),
-            other => {
-                panic!(
-                    "Tried to get struct field {} from non-struct type {:?}",
-                    field_name, other
-                )
-            }
-        }
-    }
-
-    /// Make the types of two type terms equivalent (or produce an error if
-    /// there is a conflict between them)
-    pub fn unify(&mut self, symtbl: &Symtbl, a: TypeId, b: TypeId) -> Result<(), String> {
-        println!("> Unifying {:?} with {:?}", self.vars[&a], self.vars[&b]);
-        // If a == b then it's a little weird but shoooooould be fine
-        // as long as we don't get any mutual recursion or self-recursion
-        // involved
-        // Per MBones:
-        // Yes it makes sense. The unifier is tasked with solving literally whatever equations you throw at it, and this is an important edge case to check for (to avoid accidentally making cyclic datastructures). (The correct action from the unifier is to succeed with no updates, since it's already equal to itself)
-        if a == b {
-            return Ok(());
-        }
-        use TypeInfo::*;
-        match (self.vars[&a].clone(), self.vars[&b].clone()) {
-            // Follow any references
-            (Ref(a), _) => self.unify(symtbl, a, b),
-            (_, Ref(b)) => self.unify(symtbl, a, b),
-
-            // When we don't know anything about either term, assume that
-            // they match and make the one we know nothing about reference the
-            // one we may know something about
-            (Unknown, _) => {
-                self.vars.insert(a, TypeInfo::Ref(b));
-                Ok(())
-            }
-            (_, Unknown) => {
-                self.vars.insert(b, TypeInfo::Ref(a));
-                Ok(())
-            }
-
-            // For type constructors, if their names are the same we try
-            // to unify their args
-            (Named(n1, args1), Named(n2, args2)) => {
-                if n1 == n2 && args1.len() == args2.len() {
-                    for (arg1, arg2) in args1.iter().zip(args2.iter()) {
-                        self.unify(symtbl, *arg1, *arg2)?;
-                    }
-                    Ok(())
-                } else {
-                    panic!(
-                        "Mismatch between types {}({:?}) and {}({:?})",
-                        n1, args1, n2, args2
-                    )
-                }
-            }
-            // When unifying complex types, we must check their sub-types. This
-            // can be trivially implemented for tuples, sum types, etc.
-            (Func(a_i, a_o), Func(b_i, b_o)) => {
-                if a_i.len() != b_i.len() {
-                    return Err(String::from("Arg lists are not same length"));
-                }
-                for (arg_a, arg_b) in a_i.iter().zip(b_i) {
-                    self.unify(symtbl, *arg_a, arg_b)?;
-                }
-                self.unify(symtbl, a_o, b_o)
-            }
-            (Struct(body1), Struct(body2)) => {
-                for (nm, t1) in body1.iter() {
-                    let t2 = body2[nm];
-                    self.unify(symtbl, *t1, t2)?;
-                }
-                // Now we just do it again the other way around
-                // which is a dumb but effective way of making sure
-                // struct2 doesn't have any fields that struct1 doesn't.
-                for (nm, t2) in body2.iter() {
-                    let t1 = body1[nm];
-                    self.unify(symtbl, t1, *t2)?;
-                }
-                Ok(())
-            }
-            (Sum(body1), Sum(body2)) => {
-                // Same as struct types
-                for (nm, t1) in body1.iter() {
-                    let t2 = body2[nm];
-                    self.unify(symtbl, *t1, t2)?;
-                }
-                for (nm, t2) in body2.iter() {
-                    let t1 = body1[nm];
-                    self.unify(symtbl, t1, *t2)?;
-                }
-                Ok(())
-            }
-            (Array(body1, len1), Array(body2, len2)) if len1 == len2 => {
-                self.unify(symtbl, body1, body2)
-            }
-            // For declared type parameters like @T they match if their names match.
-            // TODO: And if they have been declared?  Not sure we can ever get to
-            // here if that's the case.
-            (TypeParam(s1), TypeParam(s2)) if s1 == s2 => Ok(()),
-            // If no previous attempts to unify were successful, raise an error
-            (a, b) => {
-                self.print_types();
-                Err(format!("Conflict between {:?} and {:?}", a, b))
-            }
-        }
-    }
-
-    /// Attempt to reconstruct a concrete type from the given type term ID. This
-    /// may fail if we don't yet have enough information to figure out what the
-    /// type is.
-    pub fn reconstruct(&self, id: TypeId) -> Result<Type, String> {
-        use TypeInfo::*;
-        match &self.vars[&id] {
-            Unknown => Err(format!("Cannot infer type for type ID {:?}", id)),
-            Ref(id) => self.reconstruct(*id),
-            Enum(ts) => Ok(Type::Enum(ts.clone())),
-            Named(s, args) => {
-                let arg_types: Result<Vec<_>, _> =
-                    args.iter().map(|x| self.reconstruct(*x)).collect();
-                Ok(Type::Named(s.clone(), arg_types?))
-            }
-            Func(args, rettype) => {
-                let real_args: Result<Vec<Type>, String> =
-                    args.into_iter().map(|arg| self.reconstruct(*arg)).collect();
-                Ok(Type::Func(
-                    real_args?,
-                    Box::new(self.reconstruct(*rettype)?),
-                ))
-            }
-            TypeParam(name) => Ok(Type::Generic(name.to_owned())),
-            Struct(body) => {
-                let real_body: Result<FnvHashMap<_, _>, String> = body
-                    .iter()
-                    .map(|(nm, t)| {
-                        let new_t = self.reconstruct(*t)?;
-                        Ok((nm.clone(), new_t))
-                    })
-                    .collect();
-                // TODO: The empty params here feels suspicious, verify.
-                let params = vec![];
-                Ok(Type::Struct(real_body?, params))
-            }
-            Array(ty, len) => {
-                let real_body = self.reconstruct(*ty)?;
-                Ok(Type::Array(Box::new(real_body), *len))
-            }
-            Sum(body) => {
-                let real_body: Result<FnvHashMap<_, _>, String> = body
-                    .iter()
-                    .map(|(nm, t)| {
-                        let new_t = self.reconstruct(*t)?;
-                        Ok((nm.clone(), new_t))
-                    })
-                    .collect();
-                let params = vec![];
-                Ok(Type::Sum(real_body?, params))
-            }
-        }
-    }
-
-    fn print_types(&self) {
-        let mut vars_report: Vec<_> = self.vars.iter().collect();
-        vars_report.sort_by(|(k1, _), (k2, _)| k1.cmp(k2));
-        for (k, v) in vars_report.iter() {
-            print!("  ${} => {:?}\n", k.0, v);
-        }
-    }
-
-    /// Kinda the opposite of reconstruction; takes a concrete type
-    /// and generates a new type with unknown's (type variables) for the generic types (type
-    /// parameters)
-    ///
-    /// The named_types is a *local* binding of generic type names to type variables.
-    /// We use this to make multiple mentions of the same type name, such as
-    /// `id :: T -> T`, all refer to the same type variable.
-    /// Feels Weird but it works.
-    ///
-    /// This has to actually be an empty hashtable on the first instantitaion
-    /// instead of the symtbl, since the symtbl is full of type parameter names from the
-    /// enclosing function and those are what we explicitly want to get away from.
-    fn instantiate(&mut self, t: &Type, known_types: Option<FnvHashMap<String, TypeId>>) -> TypeId {
-        fn helper(tck: &mut Tck, named_types: &mut FnvHashMap<String, TypeId>, t: &Type) -> TypeId {
-            let typeinfo = match t {
-                //Type::Primitive(_) => todo!(),
-                Type::Enum(vals) => TypeInfo::Enum(vals.clone()),
-                Type::Named(s, args) => {
-                    let inst_args: Vec<_> =
-                        args.iter().map(|t| helper(tck, named_types, t)).collect();
-                    TypeInfo::Named(s.clone(), inst_args)
-                }
-                Type::Generic(s) => {
-                    // If we know this is is a particular generic, match wiht it
-                    if let Some(ty) = named_types.get(s) {
-                        TypeInfo::Ref(*ty)
-                    } else {
-                        panic!("Referred to unknown generic named {}", s);
-                    }
-                }
-                Type::Func(args, rettype) => {
-                    let inst_args: Vec<_> =
-                        args.iter().map(|t| helper(tck, named_types, t)).collect();
-                    let inst_ret = helper(tck, named_types, rettype);
-                    TypeInfo::Func(inst_args, inst_ret)
-                }
-                Type::Struct(body, _names) => {
-                    let inst_body = body
-                        .iter()
-                        .map(|(nm, ty)| (nm.clone(), helper(tck, named_types, ty)))
-                        .collect();
-                    TypeInfo::Struct(inst_body)
-                }
-                Type::Array(ty, len) => {
-                    let inst_ty = helper(tck, named_types, &*ty);
-                    TypeInfo::Array(inst_ty, *len)
-                }
-                Type::Sum(body, _names) => {
-                    let inst_body = body
-                        .iter()
-                        .map(|(nm, ty)| (nm.clone(), helper(tck, named_types, ty)))
-                        .collect();
-                    TypeInfo::Sum(inst_body)
-                }
-            };
-            tck.insert(typeinfo)
-        }
-        // We have to pluck any unknowns out of the toplevel type and create
-        // new type vars for them.
-        // We don't worry about binding those vars or such, that is what unification
-        // will do later.
-        // We do have to take any of those unknowns that are actually
-        // known and preserve that knowledge though.
-        let known_types = &mut known_types.unwrap_or_else(Default::default);
-        let type_params = t.get_type_params();
-        // Probably a cleaner way to do this but oh well.
-        // We to through the type params, and if any of them
-        // are unknown we put a new TypeInfo::Unknown in for them.
-        for param in type_params {
-            known_types
-                .entry(param)
-                .or_insert_with(|| self.insert(TypeInfo::Unknown));
-        }
-        helper(self, known_types, t)
-    }
-}
-
-#[derive(Clone, Default)]
-struct ScopeFrame {
-    symbols: FnvHashMap<String, TypeId>,
-    types: FnvHashMap<String, Type>,
-}
-
-/// Basic symbol table that maps names to type ID's
-/// and manages scope.
-/// Looks ugly, works well.
-#[derive(Clone)]
-struct Symtbl {
-    frames: Rc<RefCell<Vec<ScopeFrame>>>,
-}
-
-impl Default for Symtbl {
-    /// We start with an empty toplevel scope existing.
-    fn default() -> Self {
-        Self {
-            frames: Rc::new(RefCell::new(vec![ScopeFrame::default()])),
-        }
-    }
-}
-
-pub struct ScopeGuard {
-    scope: Symtbl,
-}
-
-impl Drop for ScopeGuard {
-    fn drop(&mut self) {
-        self.scope
-            .frames
-            .borrow_mut()
-            .pop()
-            .expect("Scope stack underflow");
-    }
-}
-
-impl Symtbl {
-    fn push_scope(&self) -> ScopeGuard {
-        self.frames.borrow_mut().push(ScopeFrame::default());
-        ScopeGuard {
-            scope: self.clone(),
-        }
-    }
-
-    fn add_var(&self, var: impl AsRef<str>, ty: TypeId) {
-        self.frames
-            .borrow_mut()
-            .last_mut()
-            .expect("Scope stack underflow")
-            .symbols
-            .insert(var.as_ref().to_owned(), ty);
-    }
-
-    /// Checks whether the var exists in the currently alive scopes
-    fn get_var_binding(&self, var: impl AsRef<str>) -> Option<TypeId> {
-        for scope in self.frames.borrow().iter().rev() {
-            let v = scope.symbols.get(var.as_ref());
-            if v.is_some() {
-                return v.cloned();
-            }
-        }
-        return None;
-    }
-
-    fn add_type(&self, name: impl AsRef<str>, ty: &Type) {
-        self.frames
-            .borrow_mut()
-            .last_mut()
-            .expect("Scope stack underflow")
-            .types
-            .insert(name.as_ref().to_owned(), ty.to_owned());
-    }
-
-    fn get_type(&self, ty: impl AsRef<str>) -> Option<Type> {
-        for scope in self.frames.borrow().iter().rev() {
-            let v = scope.types.get(ty.as_ref());
-            if v.is_some() {
-                return v.cloned();
-            }
-        }
-        return None;
-    }
-}
-
-fn infer_lit(lit: &ast::Literal) -> TypeInfo {
-    match lit {
-        ast::Literal::Integer(_) => TypeInfo::Named("I32".to_string(), vec![]),
-        ast::Literal::Bool(_) => TypeInfo::Named("Bool".to_string(), vec![]),
-        ast::Literal::EnumLit(nm, _) => TypeInfo::Named(nm.to_string(), vec![]),
-    }
-}
-fn typecheck_func_body(
-    name: Option<&str>,
-    tck: &mut Tck,
-    symtbl: &Symtbl,
-    signature: &ast::Signature,
-    body: &[ast::ExprNode],
-) -> Result<TypeId, String> {
-    println!(
-        "Typechecking function {:?} with signature {:?}",
-        name, signature
-    );
-    // Insert info about the function signature
-    let mut params = vec![];
-    for (_paramname, paramtype) in &signature.params {
-        let p = tck.insert_known(paramtype);
-        params.push(p);
-    }
-    let rettype = tck.insert_known(&signature.rettype);
-    println!(
-        "signature is: {:?}",
-        TypeInfo::Func(params.clone(), rettype.clone())
-    );
-    let f = tck.insert(TypeInfo::Func(params, rettype));
-
-    // If we have a name (ie, are not a lambda), bind the function's type to its name
-    // A gensym might make this easier/nicer someday, but this works for now.
-    //
-    // Note we do this *before* pushing the scope and checking its body,
-    // so this will add the function's name to the outer scope.
-    if let Some(n) = name {
-        symtbl.add_var(n, f);
-    }
-
-    // Add params to function's scope
-    let _guard = symtbl.push_scope();
-    for (paramname, paramtype) in &signature.params {
-        let p = tck.insert_known(paramtype);
-        symtbl.add_var(paramname, p);
-    }
-
-    // Typecheck body
-    for expr in body {
-        typecheck_expr(tck, symtbl, expr)?;
-        // TODO here: unit type for expressions and such
-    }
-    let last_expr = body.last().expect("empty body, aieeee");
-    let last_expr_type = tck.get_expr_type(last_expr);
-    println!(
-        "Unifying last expr...?  Is type {:?}, we want {:?}",
-        last_expr_type, rettype
-    );
-    tck.unify(symtbl, last_expr_type, rettype)?;
-
-    for expr in body {
-        let t = tck.get_expr_type(expr);
-        tck.reconstruct(t).unwrap();
-    }
-
-    println!(
-        "Typechecked function {}, types are",
-        name.unwrap_or("(lambda)")
-    );
-    tck.print_types();
-    Ok(f)
-}
-
-fn typecheck_expr(tck: &mut Tck, symtbl: &Symtbl, expr: &ast::ExprNode) -> Result<TypeId, String> {
-    use ast::Expr::*;
-    match &*expr.node {
-        Lit { val } => {
-            let lit_type = infer_lit(val);
-            let typeid = tck.insert(lit_type);
-            tck.set_expr_type(expr, typeid);
-            Ok(typeid)
-        }
-        Var { name } => {
-            let ty = symtbl
-                .get_var_binding(name)
-                .unwrap_or_else(|| panic!("unbound var: {:?}", name));
-            tck.set_expr_type(expr, ty);
-            Ok(ty)
-        }
-        Let {
-            varname,
-            typename,
-            init,
-        } => {
-            typecheck_expr(tck, symtbl, init)?;
-            let init_expr_type = tck.get_expr_type(init);
-            // Does our let decl have a type attached to it?
-            let var_type = if let Some(t) = typename {
-                tck.insert_known(t)
-            } else {
-                tck.insert(TypeInfo::Unknown)
-            };
-            tck.unify(symtbl, init_expr_type, var_type)?;
-
-            // A `let` expr returns unit, not the type of `init`
-            let unit_type = tck.insert(TypeInfo::Named("Tuple".to_owned(), vec![]));
-            tck.set_expr_type(expr, unit_type);
-
-            symtbl.add_var(varname, var_type);
-            Ok(var_type)
-        }
-        Lambda { signature, body } => {
-            let t = typecheck_func_body(None, tck, symtbl, signature, body)?;
-            tck.set_expr_type(expr, t);
-            Ok(t)
-        }
-        StructRef { e, name } => {
-            typecheck_expr(tck, symtbl, e)?;
-            let struct_type = tck.get_expr_type(e);
-            println!("Heckin struct...  Type of {:?} is {:?}", e, struct_type);
-            // struct_type is the type of the struct... but the
-            // type of this structref expr is the type of the *field in the struct*.
-            let struct_field_type = tck.get_struct_field_type(symtbl, struct_type, name);
-            println!(
-                "Heckin struct ref...  Type of {:?}.{} is {:?}",
-                e, name, struct_field_type
-            );
-            tck.set_expr_type(expr, struct_field_type);
-
-            match tck.reconstruct(struct_type)? {
-                Type::Struct(body, _names) => Ok(tck.insert_known(&body[name])),
-                Type::Named(s, _args) => {
-                    let hopefully_a_struct = symtbl.get_type(s).unwrap();
-                    match hopefully_a_struct {
-                        Type::Struct(body, _names) => Ok(tck.insert_known(&body[name])),
-                        _other => Err(format!("Yeah I know this is wrong bite me")),
-                    }
-                }
-                other => Err(format!(
-                    "Tried to get field named {} but it is an {:?}, not a struct",
-                    name, other
-                )),
-            }
-        }
-        TupleCtor { body } => {
-            let body_types: Result<Vec<_>, _> = body
-                .iter()
-                .map(|expr| typecheck_expr(tck, symtbl, expr))
-                .collect();
-            let body_types = body_types?;
-            let tuple_type = TypeInfo::Named("Tuple".to_string(), body_types);
-            let typeid = tck.insert(tuple_type);
-            tck.set_expr_type(expr, typeid);
-            Ok(typeid)
-        }
-        Funcall { func, params } => {
-            // Oh, defined generics are "easy".
-            // Each time I call a function I create new type
-            // vars for its generic args.
-            // Apparently that is the "instantiation".
-
-            let func_type = typecheck_expr(tck, symtbl, func)?;
-            // We know this will work because we require full function signatures
-            // on our functions.
-            let actual_func_type = tck.reconstruct(func_type)?;
-            match &actual_func_type {
-                Type::Func(_args, _rettype) => {
-                    println!("Calling function {:?} is {:?}", func, actual_func_type);
-                    // So when we call a function we need to know what its
-                    // type params are.  Then we bind those type parameters
-                    // to things.
-                }
-                _ => panic!("Tried to call something not a function"),
-            }
-
-            // Synthesize what we know about the function
-            // from the call.
-            let mut params_list = vec![];
-            for param in params {
-                typecheck_expr(tck, symtbl, param)?;
-                let param_type = tck.get_expr_type(param);
-                params_list.push(param_type);
-            }
-            // We don't know what the expected return type of the function call
-            // is yet; we make a type var that will get resolved when the enclosing
-            // expression is.
-            let rettype_var = tck.insert(TypeInfo::Unknown);
-            let funcall_var = tck.insert(TypeInfo::Func(params_list.clone(), rettype_var));
-
-            // Now I guess this is where we make a copy of the function
-            // with new generic types.
-            // Is this "instantiation"???
-            // Yes it is.  Differentiate "type parameters", which are the
-            // types a function takes as input (our `Generic` or `TypeParam`
-            // things I suppose), from "type variables" which are the TypeId
-            // we have to solve for.
-            //
-            // So we go through the generics the function declares and create
-            // new type vars for each of them.
-            let heck = tck.instantiate(&actual_func_type, None);
-            tck.unify(symtbl, heck, funcall_var)?;
-
-            tck.set_expr_type(expr, rettype_var);
-            Ok(rettype_var)
-        }
-
-        StructCtor { body } => {
-            let body_types: Result<FnvHashMap<_, _>, _> = body
-                .iter()
-                .map(|(name, expr)| {
-                    // ? in map doesn't work too well...
-                    println!("Checking field {} expr {:?}", name, expr);
-                    match typecheck_expr(tck, symtbl, expr) {
-                        Ok(t) => Ok((name.to_string(), t)),
-                        Err(s) => Err(s),
-                    }
-                })
-                .collect();
-            println!("Typechecking struct ctor: {:?}", body_types);
-            let body_types = body_types?;
-            let struct_type = TypeInfo::Struct(body_types);
-            let typeid = tck.insert(struct_type);
-            tck.set_expr_type(expr, typeid);
-            Ok(typeid)
-        }
-        TypeCtor {
-            name,
-            type_params,
-            body,
-        } => {
-            let named_type = symtbl.get_type(name).expect("Unknown type constructor");
-            println!("Got type named {}: is {:?}", name, 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_args();
-            let type_param_names = named_type.get_type_params();
-            assert_eq!(
-                type_params.len(),
-                type_param_names.len(),
-                "Type '{}' expected params {:?} but got params {:?}",
-                name,
-                type_param_names,
-                type_params
-            );
-            let tid = tck.instantiate(&named_type, None);
-            println!("Instantiated {:?} into {:?}", named_type, tid);
-
-            //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
-            let constructed_type =
-                tck.insert_known(&Type::Named(name.clone(), type_params.clone()));
-            tck.set_expr_type(expr, constructed_type);
-            Ok(constructed_type)
-        }
-        TypeUnwrap { e } => {
-            let mut body_type = typecheck_expr(tck, symtbl, e)?;
-            loop {
-                // I guess we follow TypeInfo references the stupid way?
-                // We don't have a convenient place to recurse for this,
-                // apparently, which is already a Smell but let's see
-                // where this takes us.
-                let well_heck = tck.vars[&body_type].clone();
-                match well_heck.clone() {
-                    TypeInfo::Named(nm, params) => {
-                        println!("Unwrapping type {}{:?}", nm, params);
-                        let t = symtbl
-                            .get_type(&nm)
-                            .expect("Named type doesn't name anything?!!");
-                        println!("Inner type is {:?}", t);
-                        // t is a concrete Type, not a TypeInfo that may have
-                        // unknowns, so we instantiate it to sub out any of its
-                        // type params with new unknowns.
-                        //
-                        // But then we have to bind those type params to
-                        // what we *know* about the type already...
-                        let type_param_names = t.collect_generic_names();
-                        let known_type_params = type_param_names
-                            .iter()
-                            .cloned()
-                            .zip(params.iter().cloned())
-                            .collect();
-                        let inst_t = tck.instantiate(&t, Some(known_type_params));
-                        //let heckin_hecker = tck.insert(well_heck);
-                        //tck.unify(symtbl, inst_t, heckin_hecker)?;
-                        tck.set_expr_type(expr, inst_t);
-                        return Ok(inst_t);
-                    }
-                    TypeInfo::Ref(other) => {
-                        body_type = other;
-                        // and loop to try again
-                    }
-                    other => panic!("Cannot unwrap non-named type {:?}", other),
-                }
-            }
-        }
-        SumCtor {
-            name,
-            variant,
-            body,
-        } => {
-            let named_type = symtbl.get_type(name).expect("Unknown sum type constructor");
-            /*
-            let body_type = typecheck_expr(tck, symtbl, body)?;
-            let well_heck = tck.vars[&body_type].clone();
-            match well_heck.clone() {
-                Type::Sum(sum_body, _generics) => {
-                    todo!()
-                }
-            */
-
-            // This might be wrong, we can probably do it the other way around
-            // like we do with TypeUnwrap: start by checking the inner expr type and make
-            // sure it matches what we expect.  Generics might require that.
-            //
-            // TODO: Generics
-            match named_type.clone() {
-                Type::Sum(sum_body, _generics) => {
-                    let variant_type = &sum_body[variant];
-                    let variant_typeid = tck.insert_known(variant_type);
-                    let body_type = typecheck_expr(tck, symtbl, body)?;
-                    tck.unify(symtbl, variant_typeid, body_type)?;
-
-                    // The expr is the type we expect, our return type is the
-                    // sum type we conjure up
-                    // TODO: Might be easier to have our compiler generate
-                    // the TypeCtor for it?
-                    let rettype = tck.insert_known(&Type::Named(name.clone(), vec![]));
-                    tck.set_expr_type(expr, rettype);
-                    Ok(rettype)
-                }
-                _ => unreachable!("This code is compiler generated, should never happen!"),
-            }
-        }
-        ArrayCtor { body } => {
-            let len = body.len();
-            // So if the body has len 0 we can't know what type it is.
-            // So we create a new unknown and then try unifying it with
-            // all the expressions in the body.
-            let body_type = tck.insert(TypeInfo::Unknown);
-            for expr in body {
-                let expr_type = typecheck_expr(tck, symtbl, expr)?;
-                tck.unify(symtbl, body_type, expr_type)?;
-            }
-            let arr_type = tck.insert(TypeInfo::Array(body_type, len));
-            tck.set_expr_type(expr, arr_type);
-            Ok(arr_type)
-        }
-        ArrayRef { e, idx } => todo!(),
-    }
-}
-
-/// From example code:
-/// "In reality, the most common approach will be to walk your AST, assigning type
-/// terms to each of your nodes with whatever information you have available. You
-/// will also need to call `engine.unify(x, y)` when you know two nodes have the
-/// same type, such as in the statement `x = y;`."
-pub fn typecheck(ast: &ast::Ast) {
-    let tck = &mut Tck::default();
-    let symtbl = &mut Symtbl::default();
-    for decl in &ast.decls {
-        use ast::Decl::*;
-
-        match decl {
-            Function {
-                name,
-                signature,
-                body,
-            } => {
-                let t = typecheck_func_body(Some(name), tck, symtbl, signature, body);
-                t.unwrap_or_else(|e| {
-                    println!("Error, type context is:");
-                    tck.print_types();
-                    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: FnvHashSet<String> =
-                    ty.collect_generic_names().into_iter().collect();
-                let param_names: FnvHashSet<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)
-            }
-            ConstDef { name, init } => {
-                // The init expression is typechecked in its own
-                // scope, since it may theoretically be a `let` or
-                // something that introduces new names inside it.
-                let init_type = {
-                    let _guard = symtbl.push_scope();
-                    let t = typecheck_expr(tck, symtbl, init).unwrap();
-                    t
-                };
-                println!("Typechecked const {}, type is {:?}", name, init_type);
-                symtbl.add_var(name, init_type);
-            }
-        }
-    }
-    // Print out toplevel symbols
-    for (name, id) in &symtbl.frames.borrow().last().unwrap().symbols {
-        println!("value {} type is {:?}", name, tck.reconstruct(*id));
-    }
-}

          
R fml/tests/main.rs =>  +0 -48
@@ 1,48 0,0 @@ 
-//! Test suite that typechecks reference programs from `tests/programs/`
-//! and checks their output.
-//!
-//! Uses the `lang_tester` crate, which is a little wobbly in places,
-//! but the best I can find.
-
-use std::{fs::read_to_string, process::Command};
-
-use lang_tester::LangTester;
-
-static COMMENT_PREFIX: &str = "--";
-
-fn main() {
-    // We just typecheck things and see if they pass
-    LangTester::new()
-        .test_dir("tests/programs/")
-        // Only use files named `*.gt` as test files.
-        .test_file_filter(|p| {
-            p.extension()
-                .map(std::ffi::OsStr::to_str)
-                .unwrap_or(Some(""))
-                .unwrap()
-                == "gt"
-        })
-        // Extract the first sequence of commented line(s) as the tests.
-        .test_extract(|p| {
-            read_to_string(p)
-                .unwrap()
-                .lines()
-                // Skip non-commented lines at the start of the file.
-                .skip_while(|l| !l.starts_with(COMMENT_PREFIX))
-                // Extract consecutive commented lines.
-                .take_while(|l| l.starts_with(COMMENT_PREFIX))
-                // Strip the initial "--" from commented lines.
-                .map(|l| &l[COMMENT_PREFIX.len()..])
-                .collect::<Vec<_>>()
-                .join("\n")
-        })
-        // We have one test commands:
-        //   * `check`: runs garnetc.
-        .test_cmds(move |p| {
-            // Test command 1: check `x.gt`
-            let mut check = Command::new("cargo");
-            check.args(&["run", "--", p.to_str().unwrap()]);
-            vec![("Check", check)]
-        })
-        .run();
-}

          
R fml/tests/programs/test1.gt =>  +0 -41
@@ 1,41 0,0 @@ 
--- Check:
---   status: success
-
-fn foo(x I32) I32 =
-    let y I32 = x
-    y
-end
-
-fn bar() I32 =
-    12
-end
-
-fn baz(x Bool) Bool =
-    x
-end
-
-/-
-fn invalid1(x I32) Bool =
-    let y Bool = x
-    y
-end
-
-fn invalid2(x I32) Bool =
-    let y I32 = x
-    y
-end
--/
-
-fn identity(i @T) @T =
-    i
-end
-
-fn main() I32 =
-    --let x I32 = foo(12)
-    --let y Bool = baz(true)
-    --let a I32 = identity(1)
-    --let b Bool = identity2(true)
-    --let c Bool = identity(false)
-    --let d I32 = identity2(12)
-    12
-end

          
R fml/tests/programs/test2.gt =>  +0 -13
@@ 1,13 0,0 @@ 
--- Check:
---   status: success
-
-fn identity(i @T) @T =
-    let y @T = i
-    y
-end
-
-fn main() I32 =
-    let a I32 = identity(1)
-    let c Bool = identity(false)
-    3
-end

          
R fml/tests/programs/test_array1.gt =>  +0 -17
@@ 1,17 0,0 @@ 
--- Check:
---   status: success
-
-fn identity(i @T) @T =
-    let y @T = i
-    y
-end
-
-fn foo(i @T[3]) @T[3] =
-    i
-end
-
-fn main() I32 =
-    let a I32[4] = identity([1, 2, 3, 4])
-    let b = foo([1, 2, 3])
-    3
-end

          
R fml/tests/programs/test_array2.gt =>  +0 -7
@@ 1,7 0,0 @@ 
--- Check:
---   status: error
-
-fn main() I32 =
-    let a I32[5] = [1, 2, 3, 4]
-    3
-end

          
R fml/tests/programs/test_array3.gt =>  +0 -7
@@ 1,7 0,0 @@ 
--- Check:
---   status: success
-
-fn main() I32 =
-    let a I32[3][3] = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
-    3
-end

          
R fml/tests/programs/test_enum1.gt =>  +0 -18
@@ 1,18 0,0 @@ 
--- Check:
---   status: success
-
-
-type Foo = enum
-    X, Y, Z,
-end
-
-fn thing(i Foo) Foo =
-    i
-end
-
-
-fn main() I32 =
-    let x Foo = Foo.X
-    let y = thing(Foo.Y)
-    3
-end

          
R fml/tests/programs/test_failure.gt =>  +0 -33
@@ 1,33 0,0 @@ 
--- Check:
---   status: error
-
-fn foo(x I32) I32 =
-    let y I32 = x
-    y
-end
-
-fn bar() I32 =
-    12
-end
-
-fn baz(x Bool) Bool =
-    x
-end
-
-fn identity(i @T) @T =
-    i
-end
-
-fn identity2(i @X) @X =
-    identity(i)
-end
-
-fn main() I32 =
-    --let x I32 = foo(12)
-    --let y Bool = baz(true)
-    let a I32 = identity(1)
-    let b Bool = identity2(true)
-    let c Bool = identity(false)
-    let d Bool = identity2(12)
-    3
-end

          
R fml/tests/programs/test_forever1.gt =>  +0 -13
@@ 1,13 0,0 @@ 
--- Check:
---   status: success
-
--- Who doesn't like infinite recursion?
--- Actually I'm not sure what this should do.
--- ...According to OCaml and Rust, the *type checking* is fine.
-fn zoom(i @T) @T =
-    zoom(i)
-end
-
-fn main() I32 =
-    3
-end

          
R fml/tests/programs/test_forever2.gt =>  +0 -12
@@ 1,12 0,0 @@ 
--- Check:
---   status: success
-
-
--- ...Seems legit I guess?
-fn zoom(i I32) I32 =
-    zoom(i)
-end
-
-fn main() I32 =
-    3
-end

          
R fml/tests/programs/test_lambda1.gt =>  +0 -9
@@ 1,9 0,0 @@ 
--- Check:
---   status: success
-
-
-fn main() I32 =
-    let f fn(Bool) Bool = fn(val Bool) Bool = val end
-    let a Bool = f(true)
-    3
-end

          
R fml/tests/programs/test_lambda2.gt =>  +0 -14
@@ 1,14 0,0 @@ 
--- Check:
---   status: success
-
-
-fn apply_bool(f fn(Bool) Bool, arg Bool) Bool =
-    f(arg)
-end
-
-
-fn main() I32 =
-    let f fn(Bool) Bool = fn(val Bool) Bool = val end
-    let a Bool = apply_bool(f, true)
-    3
-end

          
R fml/tests/programs/test_lambda3.gt =>  +0 -14
@@ 1,14 0,0 @@ 
--- Check:
---   status: success
-
-
-fn apply(f fn(@T) @T, arg @T) @T =
-    f(arg)
-end
-
-
-fn main() I32 =
-    let f fn(Bool) Bool = fn(val Bool) Bool = val end
-    let a Bool = apply(f, true)
-    3
-end

          
R fml/tests/programs/test_lambda4.gt =>  +0 -16
@@ 1,16 0,0 @@ 
--- Check:
---   status: success
-
-
-fn apply(f fn(@In) @Out, arg @In) @Out =
-    f(arg)
-end
-
-
-fn main() I32 =
-    let f1 fn(Bool) Bool = fn(val Bool) Bool = val end
-    let f2 fn(Bool) I32 = fn(val Bool) I32 = 4 end
-    let a Bool = apply(f1, true)
-    let b I32 = apply(f2, true)
-    3
-end

          
R fml/tests/programs/test_lambda5.gt =>  +0 -14
@@ 1,14 0,0 @@ 
--- Check:
---   status: error
-
-
-fn apply(f fn(@In) @Out, arg @In) @Out =
-    f(arg)
-end
-
-
-fn main() I32 =
-    let f1 fn(Bool) Bool = fn(val Bool) Bool = val end
-    let a I32 = apply(f1, true)
-    3
-end

          
R fml/tests/programs/test_let1.gt =>  +0 -28
@@ 1,28 0,0 @@ 
--- Check:
---   status: success
-
-
-type Foo(@Thing) = struct
-    a: @Thing,
-    b: Bool
-end
-
-fn id(x @T) @T =
-    x
-end
-
-
-fn main() I32 =
-    let f1 = id(Foo {
-        .a = 3,
-        .b = false
-    })
-
-    let f2 = id(Foo {
-        .a = true,
-        .b = false
-    })
-
-    let f3 = f1
-    3
-end

          
R fml/tests/programs/test_module1.gt =>  +0 -29
@@ 1,29 0,0 @@ 
--- Check:
---   status: success
-
--- A hasher that only accepts basic integers and always returns an
--- integer.  Implements a particular hash algorithm, with optional
--- state in it.
--- Analogous to Rust's std::hash::Hasher
--- We don't have mutation yet, so this just returns a new state.
---
--- No associated types either, so we just make it a type param for now,
--- which surprisingly appears to work.
-type Hasher(@Self, @Out) = struct
-    write: fn(@Self, I32) @Self,
-    finish: fn(@Self) @Out,
-end
-
-type DumbHashState = I32
-
-
-fn main() I32 =
-    let dumbhasher Hasher(DumbHashState, I32) = Hasher {
-        .write = fn(s DumbHashState, i I32) DumbHashState = DumbHashState(i) end,
-        .finish = fn(s DumbHashState) I32 = 3 end
-    }
-    let hash_state = DumbHashState(1)
-    let updated_state = dumbhasher$.write(hash_state, 12)
-    let hash = dumbhasher$.finish(updated_state)
-    hash
-end

          
R fml/tests/programs/test_module2.gt =>  +0 -19
@@ 1,19 0,0 @@ 
--- Check:
---   status: success
-
--- Dummy type
-type String = I32
-
-type Show(@Self) = struct
-    show: fn(@Self) String
-end
-
-const IntShow = Show {
-    .show = fn(x I32) String = String(0) end
-}
-
-fn show(show Show(@T), val @T) String =
-  show$.show(val)
-end
-
-

          
R fml/tests/programs/test_module3.gt =>  +0 -83
@@ 1,83 0,0 @@ 
--- Check:
---   status: success
-
--- Fine let's try something simpler.
-
-type Eq(@Self) = struct
-    eq: fn(@Self, @Self) Bool,
-end
-
--- The name here has to be something other than Eq(I32) 'cause we
--- don't have specialization and we don't attach these names to
--- types in any way.
---
--- To ponder: What if we did attach names to types, or had
--- specialization?  The latter evokes the Instance Problem, the former
--- I suppose is a way around it.
-const IntEq = Eq {
-    .eq = fn(lhs I32, rhs I32) Bool =
-        true
-    end,
-}
-
-type Ord(@Self) = struct
-    cmp: fn(@Self, @Self) I32,
-end
-
-const IntOrd = Ord {
-    .cmp = fn(lhs I32, rhs I32) I32 =
-        0
-    end,
-}
-
-
-type From(@Self, @In) = struct
-    from: fn(@In) @Self
-end
-
-const BoolFromInt = From {
-    .from = fn(i I32) Bool = false end
-}
-
-type List(@T) = struct
-    dummy_data: @T,
-end
-
-
-type Idx(@Self, @Output) = struct
-    idx: fn(@Self, I32) @Output,
-end
-
-type Len(@Self) = struct
-    len: fn(@Self) I32,
-end
-
-const ListLen = Len {
-    .len = fn(self List(@T)) I32 = 0 end
-}
-
-fn module_len(impl Len(@T), l @T) I32 =
-    let total I32 = 0
-    impl$.len(l)
-end
-
--- Specialize it just to make sure everything fits together...
-fn list_len(l List(@T)) I32 =
-    module_len(ListLen, l)
-end
-
-const ListIdx = Idx {
-    .idx = fn(self List(@T), i I32) @T = self$.dummy_data end
-}
-
--- Generalized thingy...
-fn idx(l List(@T)) @T =
-    let total I32 = 3
-    ListIdx$.idx(l, total)
-end
-
--- Can we make another instance for a more specialized type?
-const IntListIdx = Idx {
-    .idx = fn(self List(I32), i I32) I32 = self$.dummy_data end
-}
-

          
R fml/tests/programs/test_module4.gt =>  +0 -16
@@ 1,16 0,0 @@ 
--- Check:
---   status: error
-
-type Idx(@Self, @Output) = struct
-    idx: fn(@Self, I32): @Output,
-end
-
-type List(@T) = struct
-    dummy_data: @T,
-end
-
-fn foo(self List(I32), i I32) Bar =
-  self$.dummy_data
-end
-
-

          
R fml/tests/programs/test_module5.gt =>  +0 -48
@@ 1,48 0,0 @@ 
--- Check:
---   status: error
-
-
-/- From modular implicits paper:
-module type Monad = sig
-  type + 'a t
-  val return : 'a -> 'a t
-  val bind : 'a t -> ( 'a -> 'b t ) -> 'b t
-end
-
-let return { M : Monad } x = M . return x
-
-let (>>=) { M : Monad } m k = M . bind m k
-
-let map { M : Monad } ( m : 'a M . t ) f =
-  m >>= fun x -> return ( f x )
-
-let join { M : Monad } ( m : 'a M . t M . t ) =
-  m >>= fun x -> x
-
-let unless { M : Monad } p ( m : unit M . t ) =
-  if p then return () else m
-
-implicit module Monad_option = struct
-  type 'a t = 'a option
-  let return x = Some x
-  let bind m k =
-    match m with
-    | None -> None
-    | Some x -> k x
-end
-
-implicit module Monad_list = struct
-  type 'a t = 'a list
-  let return x = [ x ]
-  let bind m k = List . concat ( List . map k m )
-end
--/
-
--- The bad news is, we don't seem to be able to implement real monads
--- The good news is, we don't seem to be able to implement real monads
--- I think if we had associated types?
-type Monad(@A, @M) = struct
-   return: fn(@A) @M(@A),
-   bind: fn(@M(@A), fn(@A) @M(@B)) @M(@B)
-end
-

          
R fml/tests/programs/test_module6.gt =>  +0 -35
@@ 1,35 0,0 @@ 
--- Check:
---   status: success
-
--- TODO: BUGGO: This test occasionally fails to pass, *hopefully*
--- because of HashMap ordering shenanigans.  Investigate.
-
--- A generic functional map from a key to a value
-type Map(@T, @K, @V) = struct
-    get: fn(@T, @K) @V,
-    set: fn(@T, @K, @V) @T
-end
-
--- Implement Map for a cell type
-type Cell(@K, @V) = struct
-    key: @K,
-    val: @V
-end
-
-fn make_cell_map(k @K, v @V) Map(Cell(@K, @V), @K, @V) =
-    let module = Map {
-        -- I guess we pretend this always succeeds,
-        -- since I don't really feel like implementing if's
-        .get = fn(t Cell(@K, @V), key @K) @V =
-            t$.val
-        end,
-        -- Just create a new cell with the given key and val
-        .set = fn(t Cell(@K, @V), key @K, val @V) Cell(@K, @V) =
-            Cell {
-                .key = key,
-                .val = val,
-            }
-        end
-    }
-    module
-end

          
R fml/tests/programs/test_module7.gt =>  +0 -44
@@ 1,44 0,0 @@ 
--- Check:
---   status: success
-
-
-/- Another monad-ish attempt, this also gets called map
-From modular implicits paper section 3.4
-
-module type Functor = sig
-  type + 'a t
-  val map : ('a -> 'b) -> 'a t -> 'b t
-end
--/
-
-type Functor(@T1, @T2) = struct
-    map: fn(@T1) @T2,
-end
-
--- Implement Map for a cell type
-type Cell(@V) = struct
-    val: @V
-end
-
-fn make_cell_functor(f fn(@A) @B) Functor(Cell(@A), Cell(@B)) =
-    Functor {
-        .map = fn(cell Cell(@A)) Cell(@B) =
-            Cell {
-                .val = f(cell$.val)
-            }
-        end
-    }
-end
-
-fn f(i Bool) I32 =
-    12
-end
-
-fn main() I32 =
-    let test_cell = Cell {
-        .val = true
-    }
-    let thing = make_cell_functor(f)
-    let test_result Cell(I32) = thing$.map(test_cell)
-    0
-end

          
R fml/tests/programs/test_module_specialization1.gt =>  +0 -18
@@ 1,18 0,0 @@ 
--- Check:
---   status: success
-
-
-type List(@T) = struct
-    dummy_data: @T,
-end
-
-
-type Idx(@Self, @Output) = struct
-    idx: fn(@Self, I32) @Output,
-end
-
--- Can we make an instance for a specialization of List(T)?
-const IntListIdx = Idx {
-    .idx = fn(self List(I32), i I32) I32 = self$.dummy_data end
-}
-

          
R fml/tests/programs/test_struct1.gt =>  +0 -22
@@ 1,22 0,0 @@ 
--- Check:
---   status: success
-
-
-type Foo = struct
-    a: I32,
-    b: Bool
-end
-
-fn id(x @T) @T =
-    x
-end
-
-
-fn main() I32 =
-    let f1 Foo = id(Foo {
-        .a = 3,
-        .b = false
-    })
-    let f2 struct a: I32, b: Bool end = id({ .a = 3, .b = false })
-    3
-end

          
R fml/tests/programs/test_struct2.gt =>  +0 -25
@@ 1,25 0,0 @@ 
--- Check:
---   status: success
-
-
-type Foo = struct
-    a: I32,
-    b: Bool
-end
-
-fn thing(i I32) Foo =
-    Foo {
-        .a = i,
-        .b = false,
-    }
-end
-
-
-fn main() I32 =
-    let f1 Foo = Foo {
-        .a = 3,
-        .b = false
-    }
-    let f2 Foo = thing(4)
-    3
-end

          
R fml/tests/programs/test_struct3.gt =>  +0 -24
@@ 1,24 0,0 @@ 
--- Check:
---   status: error
-
-
-type Foo = struct
-    a: I32,
-    b: Bool
-end
-
-fn thing(i I32) Foo =
-    Foo {
-        .a = i,
-        .b = false,
-    }
-end
-
-
-fn main() I32 =
-    let f1 Foo = Foo {
-        .a = 3,
-    }
-    let f2 Foo = thing(4)
-    3
-end

          
R fml/tests/programs/test_struct4.gt =>  +0 -26
@@ 1,26 0,0 @@