Ah, TFW the typechecker catches bugs in your test programs.

...but sucks at telling you where it is so you go on a tangent
to try to fix it.
5 files changed, 63 insertions(+), 9 deletions(-)

M src/backend/rust.rs
M src/hir.rs
M src/passes.rs
M src/typeck.rs
M tests/programs/test_sum2.gt
M src/backend/rust.rs +5 -1
@@ 389,7 389,11 @@ fn compile_expr(expr: &hir::ExprNode, tc
                 compile_exprs(body, ";\n", tck)
             )
         }
-        E::Funcall { func, params } => {
+        E::Funcall {
+            func,
+            params,
+            type_params: _,
+        } => {
             // We have to store an intermediate value for the func, 'cause
             // Rust has problems with things like this:
             // fn f1() -> i32 { 1 }

          
M src/hir.rs +17 -1
@@ 143,9 143,18 @@ impl Expr {
                 }
                 writeln!(f, ")")?;
             }
-            Funcall { func, params } => {
+            Funcall {
+                func,
+                params,
+                type_params,
+            } => {
                 write!(f, "(funcall ")?;
                 func.write(0, f)?;
+                write!(f, "[")?;
+                for (nm, ty) in type_params {
+                    write!(f, "{}={},", nm, ty.get_name())?;
+                }
+                write!(f, "] ")?;
                 for b in params {
                     b.write(indent + 1, f)?;
                     write!(f, " ")?;

          
@@ 358,6 367,12 @@ pub enum Expr {
     Funcall {
         func: ExprNode,
         params: Vec<ExprNode>,
+        /// Explicit type parameters, specified a la Rust's turbofish.
+        /// We don't actually have a syntax for this yet, certain functions
+        /// have these generated during lowering passes.
+        /// If a type param is not specified, the type checker will
+        /// attempt to infer it.
+        type_params: BTreeMap<Sym, Type>,
     },
     Break,
     Return {

          
@@ 604,6 619,7 @@ fn lower_expr(expr: &ast::Expr) -> ExprN
             Funcall {
                 func: nfunc,
                 params: nparams,
+                type_params: Default::default(),
             }
         }
         E::Break => Break,

          
M src/passes.rs +25 -2
@@ 31,7 31,7 @@ pub fn run_passes(ir: Ir) -> Ir {
 pub fn run_typechecked_passes(ir: Ir, tck: &mut typeck::Tck) -> Ir {
     // let passes: &[TckPass] = &[nameify, enum_to_int];
     //let passes: &[TckPass] = &[nameify, struct_to_tuple];
-    let passes: &[TckPass] = &[struct_to_tuple];
+    let passes: &[TckPass] = &[struct_to_tuple, monomorphize];
     let res = passes.iter().fold(ir, |prev_ir, f| f(prev_ir, tck));
     println!();
     println!("{}", res);

          
@@ 159,12 159,17 @@ fn expr_map(expr: ExprNode, f: &mut dyn 
         E::Return { retval } => E::Return {
             retval: expr_map(retval, f),
         },
-        E::Funcall { func, params } => {
+        E::Funcall {
+            func,
+            params,
+            type_params,
+        } => {
             let new_func = expr_map(func, f);
             let new_params = exprs_map(params, f);
             E::Funcall {
                 func: new_func,
                 params: new_params,
+                type_params,
             }
         }
         E::Lambda { signature, body } => E::Lambda {

          
@@ 482,7 487,25 @@ fn lambda_lift(ir: Ir) -> Ir {
 
 //////  Monomorphization //////
 
+/// Ok, so what we have to do here is this:
+/// FIRST, we scan through the entire program and find
+/// all functions with generic params that are called,
+/// and figure out what their generics are.  (The expr's
+/// type should have this info.)
+///
+/// Then we have to go through and find where each of those functions
+/// is defined, which should be at the toplevel since we've done
+/// lambda-lifting.  We make a copy of them with the types
+/// substituted, with a new name.
+///
+/// Then we have to rewrite the function call names to point at the monomorphized
+/// functions.  We can do this while collecting them, since we generate the
+/// names from the signature.
 fn monomorphize(ir: Ir, _tck: &mut typeck::Tck) -> Ir {
+    let mut functioncalls: BTreeSet<(Sym, Vec<Type>)> = Default::default();
+    fn mangle_generic_name(s: Sym, tys: &[Type]) -> Sym {
+        todo!()
+    }
     ir
 }
 

          
M src/typeck.rs +13 -2
@@ 1034,7 1034,11 @@ fn typecheck_expr(
             tck.set_expr_type(expr, rettype);
             Ok(rettype)
         }
-        Funcall { func, params } => {
+        Funcall {
+            func,
+            params,
+            type_params,
+        } => {
             // Oh, defined generics are "easy".
             // Each time I call a function I create new type
             // vars for its generic args.

          
@@ 1081,7 1085,14 @@ fn typecheck_expr(
             //
             // 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);
+            //
+            // We also create type variables for any type paramss we've been
+            // given values to
+            let input_type_params = type_params
+                .iter()
+                .map(|(nm, ty)| (*nm, tck.insert_known(ty)))
+                .collect();
+            let heck = tck.instantiate(&actual_func_type, Some(input_type_params));
             tck.unify(symtbl, heck, funcall_var)?;
 
             tck.set_expr_type(expr, rettype_var);

          
M tests/programs/test_sum2.gt +3 -3
@@ 21,12 21,12 @@ always infer that from function args" an
 Welp! 
 -/
 type Option(@T) = sum
+    Some @T,
     None {},
-    Some @T,
 end
 
 fn main() {} =
-    let x = Option.None {}
-    let y = Option.Some(12)
+    let x Option(I32) = Option.None {}
+    let y Option(I32) = Option.Some(12)
 end