Experiment with flattening our tree-based HIR into a vec of nodes.

It's certainly possible, but doing it is at least as much of a pain in the
ass as writing it was in the first place.  And I doubt the benefit
is that high, since we tend to allocate all our IR nodes at once
anyway so locality is probably decentish.  I'm sure the benefit is
PRESENT, but doesn't matter right now.  I just wanted to see what
the API would look like and, as expected, there are some annoying
wrinkles.
3 files changed, 1686 insertions(+), 0 deletions(-)

A => src/hir2.rs
A => src/passes2.rs
A => src/passes2/lambda_lift.rs
A => src/hir2.rs +1049 -0
@@ 0,0 1,1049 @@ 
+//! High-level intermediate representation, basically what you get after immediate parsing and
+//! where all the typechecking and other kinds of checking happen.  Slightly simplified over the
+//! AST -- basically the result of a lowering/desugaring pass.  This might give us a nice place to
+//! do other simple optimization-like things like constant folding, dead code detection, etc.
+//!
+//! Currently it's not really lowered by much!  If's and maybe loops or something.
+//! It's mostly a layer of indirection for further stuff to happen to, so we can change
+//! the parser without changing the typechecking and codegen.
+//!
+//! This is an EXPERIMENT for having our IR all shoved down into a single vec
+//! with everything in it just arrays.
+
+use std::fmt;
+use std::sync::atomic::{AtomicUsize, Ordering};
+
+use crate::ast;
+pub use crate::ast::{BOp, IfCase, Literal, Signature, UOp};
+use crate::*;
+
+/// Expression ID.  Used for associating individual expressions to types, and whatever
+/// other metadata we feel like.  That way we can keep that data in tables instead of
+/// having to mutate the HIR tree.
+/// The only guarantees the number offers is it will be unique.
+#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub struct Eid(usize);
+
+/// Slightly less ugly horrible atomic global for storing index values for
+/// expression ID's, so each expression gets a unique ID.
+static NEXT_EID: AtomicUsize = AtomicUsize::new(0);
+
+impl Eid {
+    fn new() -> Self {
+        // We may be able to use Ordering::Relaxed here since we don't
+        // care what the value actually is, just that it increments
+        // atomically.  Eh, it's fine.
+        let current = NEXT_EID.fetch_add(1, Ordering::AcqRel);
+        // Check for overflow, since fetch_add can't do it.
+        // It returns the previous value, so if the next value
+        // is < than it, we've overflowed.
+        if (current.wrapping_add(1)) < current {
+            panic!("Integer overflow incrementing Eid; is your program absolutely enormous?");
+        }
+        Eid(current)
+    }
+}
+
+/// Expression index to an expr node in our flat list thing.
+#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub struct EIdx(usize);
+
+/// An expression node with a unique ID attached.
+/// Expressions are assigned types by the typechecker
+/// via an `Eid`->type map.
+#[derive(Debug, Clone)]
+pub struct ExprNode {
+    /// expression
+    pub e: Expr,
+    /// Expression ID
+    pub id: Eid,
+    /// Whether or not the expression is constant,
+    /// ie can be entirely evaluated at compile time.
+    pub is_const: bool,
+}
+
+impl PartialEq for ExprNode {
+    /// ExprNode's must ignore the Eid for equality purposes,
+    /// otherwise they will just *never* be equal.
+    ///
+    /// If you need to compare ExprNode including Eid's, then
+    /// write a new function.  (And then you might as well *only*
+    /// compare the Eid's unless you are purposefully testing for
+    /// ill-constructed nodes.)
+    fn eq(&self, other: &Self) -> bool {
+        self.e == other.e
+    }
+}
+
+impl ExprNode {
+    pub fn write(&self, indent: usize, f: &mut dyn fmt::Write) -> fmt::Result {
+        // TODO: We can make this take a callback that gets called for each
+        // expr node, which prints out types for each node.  Will make the
+        // formatting even more horrible though.
+        self.e.write(indent, f)?;
+        Ok(())
+    }
+}
+
+/*
+impl fmt::Display for Decl {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        use crate::hir2::Decl as D;
+        match self {
+            D::Function {
+                name,
+                signature,
+                body,
+            } => {
+                writeln!(f, "fn {}{} =", name, signature.to_name())?;
+                for e in body {
+                    e.write(1, f)?;
+                }
+                writeln!(f, "\nend")?;
+            }
+
+            D::Const {
+                name,
+                typ: typename,
+                init,
+            } => {
+                write!(f, "const {}: {} = ", name, typename.get_name())?;
+                init.write(1, f)?;
+                writeln!(f)?;
+            }
+            D::TypeDef {
+                name,
+                typedecl,
+                params,
+            } => {
+                writeln!(f, "type {}({:?}) = {}", name, params, typedecl.get_name())?;
+            }
+            D::Import { name, localname } => {
+                writeln!(f, "import {} as {}", name, localname)?;
+            }
+        }
+        Ok(())
+    }
+}
+    */
+
+impl ExprNode {
+    pub fn new(e: Expr) -> Self {
+        ExprNode {
+            e: e,
+            id: Eid::new(),
+            is_const: false,
+        }
+    }
+
+    /// Call the function on the contents
+    /// and turn the result into a new ExprNode *without*
+    /// changing our Eid.
+    ///
+    /// Consumes self to preserve the invariant that no
+    /// Eid's are ever duplicated.
+    pub fn map(self, f: &mut dyn FnMut(Expr) -> Expr) -> Self {
+        ExprNode {
+            e: f(self.e),
+            ..self
+        }
+    }
+
+    /*
+    /// Apply a function with extra state to the given node.
+    /// Unneeded so far, see passes::expr_fold()
+    pub fn fold<S>(self, state: S, f: &dyn Fn(Expr, S) -> (Expr, S)) -> (Self, S) {
+        let (new_e, new_s) = f(*self.e, state);
+        (
+            ExprNode {
+                e: Box::new(new_e),
+                id: self.id,
+            },
+            new_s,
+        )
+    }
+    */
+}
+
+/// An expression.
+#[derive(Debug, Clone, PartialEq)]
+pub enum Expr {
+    Lit {
+        val: Literal,
+    },
+    Var {
+        name: Sym,
+    },
+    BinOp {
+        op: BOp,
+        lhs: EIdx,
+        rhs: EIdx,
+    },
+    UniOp {
+        op: UOp,
+        rhs: EIdx,
+    },
+    Block {
+        body: Vec<EIdx>,
+    },
+    Let {
+        varname: Sym,
+        typename: Option<Type>,
+        init: EIdx,
+        mutable: bool,
+    },
+    If {
+        cases: Vec<(EIdx, Vec<EIdx>)>,
+    },
+    Loop {
+        body: Vec<EIdx>,
+    },
+    Lambda {
+        signature: Signature,
+        body: Vec<EIdx>,
+    },
+    Funcall {
+        func: EIdx,
+        params: Vec<EIdx>,
+        type_params: Vec<Type>,
+    },
+    Break,
+    Return {
+        retval: EIdx,
+    },
+    TupleCtor {
+        body: Vec<EIdx>,
+    },
+    StructCtor {
+        body: Vec<(Sym, EIdx)>,
+    },
+    ArrayCtor {
+        body: Vec<EIdx>,
+    },
+    EnumCtor {
+        name: Sym,
+        variant: Sym,
+        value: i32,
+    },
+    // Create a new sum type.
+    // Like EnumCtor, this is generated for you by the
+    // compiler.
+    SumCtor {
+        name: Sym,
+        variant: Sym,
+        body: EIdx,
+    },
+    // Wrap a new type.
+    // Like EnumCtor, this is generated by the compiler.
+    TypeCtor {
+        name: Sym,
+        type_params: Vec<Type>,
+        body: EIdx,
+    },
+    // Opposite of TypeCtor
+    TypeUnwrap {
+        expr: EIdx,
+    },
+    TupleRef {
+        expr: EIdx,
+        elt: usize,
+    },
+    StructRef {
+        expr: EIdx,
+        elt: Sym,
+    },
+    ArrayRef {
+        expr: EIdx,
+        idx: EIdx,
+    },
+    Assign {
+        lhs: EIdx,
+        rhs: EIdx,
+    },
+    Typecast {
+        e: EIdx,
+        to: Type,
+    },
+    Deref {
+        expr: EIdx,
+    },
+    Ref {
+        expr: EIdx,
+    },
+}
+
+impl Expr {
+    /// Shortcut function for making literal bools
+    pub fn bool(ir: &mut Ir, b: bool) -> EIdx {
+        ir.insert(Self::Lit {
+            val: Literal::Bool(b),
+        })
+    }
+
+    /// Shortcut function for making literal integers
+    pub fn int(ir: &mut Ir, i: i128) -> EIdx {
+        ir.insert(Self::Lit {
+            val: Literal::Integer(i),
+        })
+    }
+
+    /// Shortcut function for making literal unit
+    pub fn unit(ir: &mut Ir) -> EIdx {
+        ir.insert(Self::TupleCtor { body: vec![] })
+    }
+
+    pub fn write(&self, indent: usize, f: &mut dyn fmt::Write) -> fmt::Result {
+        todo!()
+        /*
+        use Expr::*;
+        for _ in 0..(indent * 2) {
+            write!(f, " ")?;
+        }
+        match self {
+            Lit { val } => {
+                write!(f, "{}", val)?;
+            }
+            Var { name } => write!(f, "{}", name)?,
+            BinOp { op, lhs, rhs } => {
+                write!(f, "({:?} ", op)?;
+                lhs.write(indent, f)?;
+                rhs.write(indent, f)?;
+                writeln!(f, ")")?;
+            }
+            UniOp { op, rhs } => {
+                write!(f, "({:?} ", op)?;
+                rhs.write(indent, f)?;
+                writeln!(f, ")")?;
+            }
+            Block { body } => {
+                writeln!(f, "(block")?;
+                for b in body {
+                    b.write(indent + 1, f)?;
+                }
+                writeln!(f, ")")?;
+            }
+            Let {
+                varname,
+                typename,
+                init,
+                mutable,
+            } => {
+                let m = if *mutable { " mut" } else { "" };
+                let type_str = typename
+                    .as_ref()
+                    .map(|inner| inner.get_name())
+                    .unwrap_or(Cow::from(""));
+                write!(f, "(let {}{} {} = ", &*varname.val(), m, type_str)?;
+                init.write(0, f)?;
+                writeln!(f, ")")?;
+            }
+            If { cases } => {
+                writeln!(f, "(if")?;
+                for (case, arm) in cases {
+                    case.write(indent + 1, f)?;
+                    for expr in arm {
+                        expr.write(indent + 2, f)?;
+                    }
+                }
+                writeln!(f, ")")?;
+            }
+            Loop { body } => {
+                writeln!(f, "(loop")?;
+                for b in body {
+                    b.write(indent + 1, f)?;
+                }
+                writeln!(f, ")")?;
+            }
+            Lambda { signature, body } => {
+                writeln!(f, "(lambda {}", signature.to_name())?;
+                for b in body {
+                    b.write(indent + 1, f)?;
+                }
+                writeln!(f, ")")?;
+            }
+            Funcall {
+                func,
+                params,
+                type_params,
+            } => {
+                write!(f, "(funcall ")?;
+                func.write(0, f)?;
+                for b in params {
+                    b.write(indent + 1, f)?;
+                    write!(f, " ")?;
+                }
+                write!(f, "|")?;
+                for ty in type_params {
+                    write!(f, "{},", ty.get_name())?;
+                }
+                write!(f, ")")?;
+            }
+            Break => {
+                writeln!(f, "(break)")?;
+            }
+            Return { retval } => {
+                write!(f, "(return ")?;
+                retval.write(0, f)?;
+                writeln!(f)?;
+            }
+            EnumCtor {
+                name,
+                variant,
+                value,
+            } => {
+                write!(f, "(enumval {} {} {})", name, &*variant.val(), value)?;
+            }
+            TupleCtor { body } => {
+                write!(f, "(tuple ")?;
+                for b in body {
+                    b.write(0, f)?;
+                    write!(f, " ")?;
+                }
+                write!(f, ")")?;
+            }
+            StructCtor { body } => {
+                writeln!(f, "(struct")?;
+                for (nm, expr) in body {
+                    write!(f, "{} = ", &*nm.val())?;
+                    expr.write(0, f)?;
+                    writeln!(f)?;
+                }
+                writeln!(f, ")")?;
+            }
+            ArrayCtor { body } => {
+                writeln!(f, "(array")?;
+                for b in body {
+                    b.write(indent + 1, f)?;
+                }
+                write!(f, ")")?;
+            }
+            SumCtor {
+                name,
+                variant,
+                body,
+            } => {
+                write!(f, "(sum {} {} ", name, &*variant.val())?;
+                body.write(0, f)?;
+                write!(f, ")")?;
+            }
+            TypeCtor {
+                name,
+                type_params: _,
+                body,
+            } => {
+                // TODO: type_params
+                write!(f, "(typector {} ", name)?;
+                body.write(0, f)?;
+                write!(f, ")")?;
+            }
+            TypeUnwrap { expr } => {
+                write!(f, "(typeunwrap ")?;
+                expr.write(0, f)?;
+                write!(f, ")")?;
+            }
+            TupleRef { expr, elt } => {
+                write!(f, "(tupleref ")?;
+                expr.write(0, f)?;
+                write!(f, " {})", elt)?;
+            }
+            StructRef { expr, elt } => {
+                write!(f, "(structref ")?;
+                expr.write(0, f)?;
+                write!(f, " {})", elt)?;
+            }
+            ArrayRef { expr: e, idx } => {
+                write!(f, "(arrayref ")?;
+                e.write(0, f)?;
+                write!(f, " ")?;
+                idx.write(0, f)?;
+                write!(f, ")")?;
+            }
+            Assign { lhs, rhs } => {
+                write!(f, "(assign ")?;
+                lhs.write(0, f)?;
+                write!(f, " ")?;
+                rhs.write(0, f)?;
+                write!(f, ")")?;
+            }
+            Typecast { e, to } => {
+                write!(f, "(cast {} ", to.get_name())?;
+                e.write(0, f)?;
+                write!(f, ")")?;
+            }
+            Deref { expr } => {
+                write!(f, "(ref ")?;
+                expr.write(0, f)?;
+                write!(f, ")")?;
+            }
+            Ref { expr } => {
+                write!(f, "(ref ")?;
+                expr.write(0, f)?;
+                write!(f, ")")?;
+            }
+        }
+        Ok(())
+        */
+    }
+}
+
+impl ExprNode {
+    /// Shortcut function for making literal bools
+    pub fn bool(b: bool) -> Self {
+        Self::new(Expr::Lit {
+            val: Literal::Bool(b),
+        })
+    }
+
+    /// Shortcut function for making literal integers
+    pub fn int(i: i128) -> Self {
+        Self::new(Expr::Lit {
+            val: Literal::Integer(i),
+        })
+    }
+
+    /// Shortcut function for making literal unit
+    pub fn unit() -> Self {
+        Self::new(Expr::TupleCtor { body: vec![] })
+    }
+}
+/// A top-level declaration in the source file.
+/// Like ExprNode, contains a type annotation.
+#[derive(Debug, Clone, PartialEq)]
+pub enum Decl {
+    Function {
+        name: Sym,
+        signature: Signature,
+        body: Vec<EIdx>,
+    },
+    Const {
+        name: Sym,
+        typ: Type,
+        init: EIdx,
+    },
+    TypeDef {
+        name: Sym,
+        params: Vec<Sym>,
+        typedecl: Type,
+    },
+    Import {
+        name: Sym,
+        localname: Sym,
+    },
+}
+
+/// A compilable chunk of IR.
+///
+/// Currently, basically a compilation unit.
+#[derive(Debug, Clone, Default)]
+pub struct Ir {
+    pub decls: Vec<Decl>,
+    pub exprs: Vec<ExprNode>,
+    pub filename: String,
+    pub modulename: String,
+}
+
+impl Ir {
+    fn new(filename: &str, modulename: &str) -> Self {
+        Ir {
+            decls: vec![],
+            exprs: vec![],
+            filename: filename.to_owned(),
+            modulename: modulename.to_owned(),
+        }
+    }
+    fn insert(&mut self, expr: Expr) -> EIdx {
+        let en = ExprNode::new(expr);
+        self.exprs.push(en);
+        EIdx(self.exprs.len() - 1)
+    }
+
+    fn get(&self, eidx: EIdx) -> &ExprNode {
+        self.exprs
+            .get(eidx.0)
+            .expect("Invalid expr idx, should never happen!")
+    }
+}
+
+impl fmt::Display for Ir {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        todo!()
+        /*
+        for decl in &self.decls {
+            write!(f, "{}", decl)?;
+        }
+        Ok(())
+        */
+    }
+}
+
+/// Transforms AST into HIR
+pub fn lower(ast: &ast::Ast) -> Ir {
+    let mut ir = Ir {
+        decls: vec![],
+        exprs: vec![],
+        filename: ast.filename.clone(),
+        modulename: ast.modulename.clone(),
+    };
+    for d in ast.decls.iter() {
+        lower_decl(&mut ir, d)
+    }
+
+    ir
+}
+
+fn lower_lit(lit: &ast::Literal) -> Literal {
+    lit.clone()
+}
+
+fn lower_bop(bop: &ast::BOp) -> BOp {
+    *bop
+}
+
+fn lower_uop(uop: &ast::UOp) -> UOp {
+    *uop
+}
+
+fn lower_signature(sig: &ast::Signature) -> Signature {
+    sig.clone()
+}
+
+/// This is the biggie currently
+fn lower_expr(ir: &mut Ir, expr: &ast::Expr) -> EIdx {
+    use ast::Expr as E;
+    use Expr::*;
+    let new_exp = match expr {
+        E::Lit { val } => Lit {
+            val: lower_lit(val),
+        },
+        E::Var { name } => Var { name: *name },
+        E::BinOp { op, lhs, rhs } => {
+            let nop = lower_bop(op);
+            let nlhs = lower_expr(ir, lhs);
+            let nrhs = lower_expr(ir, rhs);
+            BinOp {
+                op: nop,
+                lhs: nlhs,
+                rhs: nrhs,
+            }
+        }
+        E::UniOp { op, rhs } => {
+            let nop = lower_uop(op);
+            let nrhs = lower_expr(ir, rhs);
+            UniOp { op: nop, rhs: nrhs }
+        }
+        E::Block { body } => {
+            let nbody = lower_exprs(ir, body);
+            Block { body: nbody }
+        }
+        E::Let {
+            varname,
+            typename,
+            init,
+            mutable,
+        } => {
+            let ninit = lower_expr(ir, init);
+            Let {
+                varname: *varname,
+                typename: typename.clone(),
+                init: ninit,
+                mutable: *mutable,
+            }
+        }
+        E::If { cases, falseblock } => {
+            // One of the actual transformations, this makes all if/else statements
+            // into essentially a switch: `if ... else if ... else if ... else if true ... end`
+            // This is more consistent and easier to handle for typechecking.
+            assert!(!cases.is_empty(), "Should never happen");
+            let mut cases: Vec<_> = cases
+                .iter()
+                .map(|case| (lower_expr(ir, &case.condition), lower_exprs(ir, &case.body)))
+                .collect();
+            // Add the "else" case, which we can just make `else if true then...`
+            // No idea if this is a good idea, but it makes life easier right
+            // this instant, so.  Hasn't bit me yet, so it's not a *bad* idea.
+            let nelse_case = Expr::bool(ir, true);
+            // Empty false block becomes a false block that returns unit
+            let false_exprs = if falseblock.is_empty() {
+                lower_exprs(ir, &[ast::Expr::TupleCtor { body: vec![] }])
+            } else {
+                lower_exprs(ir, falseblock)
+            };
+            cases.push((nelse_case, false_exprs));
+            If { cases }
+        }
+        E::Loop { body } => {
+            let nbody = lower_exprs(ir, body);
+            Loop { body: nbody }
+        }
+        E::While { cond, body } => {
+            // While loops just get turned into a Loop containing
+            // if not cond then break end
+            let inverted_cond = E::UniOp {
+                op: UOp::Not,
+                rhs: cond.clone(),
+            };
+            let test = lower_expr(ir, &inverted_cond);
+            let brk = vec![ir.insert(Break)];
+            // As per above, we need to always have an "else" end case
+            let else_case = Expr::bool(ir, true);
+            let else_exprs = lower_exprs(ir, &[ast::Expr::TupleCtor { body: vec![] }]);
+            let if_expr = ir.insert(If {
+                cases: vec![(test, brk), (else_case, else_exprs)],
+            });
+            let mut nbody = lower_exprs(ir, body);
+            nbody.insert(0, if_expr);
+            Loop { body: nbody }
+        }
+        E::Lambda { signature, body } => {
+            let nsig = lower_signature(signature);
+            let nbody = lower_exprs(ir, body);
+            Lambda {
+                signature: nsig,
+                body: nbody,
+            }
+        }
+        E::Funcall {
+            func,
+            params,
+            typeparams,
+        } => {
+            let nfunc = lower_expr(ir, func);
+            let nparams = lower_exprs(ir, params);
+            Funcall {
+                func: nfunc,
+                params: nparams,
+                type_params: typeparams.clone(),
+            }
+        }
+        E::Break => Break,
+        E::Return { retval: e } => Return {
+            retval: lower_expr(ir, e),
+        },
+        E::TupleCtor { body } => {
+            /*
+            // Turn {foo, bar} into {_0: foo, _1: bar}
+                let body_pairs = body
+                    .into_iter()
+                    .enumerate()
+                    .map(|(i, vl)| (Sym::new(format!("_{}", i)), lower_expr(ir, vl)))
+                    .collect();
+                Expr::StructCtor { body: body_pairs }
+                */
+            let body = lower_exprs(ir, body);
+            Expr::TupleCtor { body }
+        }
+        E::StructCtor { body } => {
+            let lowered_body = body
+                .iter()
+                .map(|(nm, expr)| (*nm, lower_expr(ir, expr)))
+                .collect();
+            Expr::StructCtor { body: lowered_body }
+        }
+        E::ArrayRef { expr, idx } => Expr::ArrayRef {
+            expr: lower_expr(ir, expr),
+            idx: lower_expr(ir, idx),
+        },
+        E::TupleRef { expr, elt } => Expr::TupleRef {
+            expr: lower_expr(ir, expr),
+            elt: *elt,
+        },
+        E::StructRef { expr, elt } => Expr::StructRef {
+            expr: lower_expr(ir, expr),
+            elt: *elt,
+        },
+        E::TypeUnwrap { expr } => Expr::TypeUnwrap {
+            expr: lower_expr(ir, expr),
+        },
+        E::Deref { expr } => Expr::Deref {
+            expr: lower_expr(ir, expr),
+        },
+        E::Ref { expr } => Expr::Ref {
+            expr: lower_expr(ir, expr),
+        },
+        E::Assign { lhs, rhs } => Expr::Assign {
+            lhs: lower_expr(ir, lhs),
+            rhs: lower_expr(ir, rhs),
+        },
+        E::ArrayCtor { body } => Expr::ArrayCtor {
+            body: lower_exprs(ir, body.as_slice()),
+        },
+    };
+    ir.insert(new_exp)
+}
+
+/// handy shortcut to lower Vec<ast::Expr>
+fn lower_exprs(ir: &mut Ir, exprs: &[ast::Expr]) -> Vec<EIdx> {
+    exprs.iter().map(|e| lower_expr(ir, e)).collect()
+}
+fn lower_typedef(ir: &mut Ir, name: Sym, ty: &Type, params: &[Sym]) {
+    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: Vec<_> = ts
+                .iter()
+                .map(|(enumname, enumval)| {
+                    // TODO: Enum vals?
+                    let e = ir.insert(Expr::EnumCtor {
+                        name,
+                        variant: *enumname,
+                        value: *enumval,
+                    });
+                    let e2 = ir.insert(Expr::TypeCtor {
+                        name,
+                        type_params: vec![],
+                        body: e,
+                    });
+                    (*enumname, e2)
+                })
+                .collect();
+            let init_val = ir.insert(Expr::StructCtor { body: struct_body });
+            let struct_signature = ts
+                .iter()
+                //.map(|(enumname, _enumval)| (*enumname, ty.clone()))
+                .map(|(enumname, _enumval)| (*enumname, Type::Named(name, vec![])))
+                .collect();
+            // Enums cannot have type parameters, so this works.
+            let init_type = Type::Struct(struct_signature, vec![]);
+            let new_constdef = Const {
+                name,
+                typ: init_type,
+                init: init_val,
+            };
+            ir.decls.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...  Right now they just
+        // get stuffed into the constructor functions verbatim.
+        Type::Sum(body, generics) => {
+            trace!("Lowering sum type {}", name);
+            let struct_body: Vec<_> = body
+                .iter()
+                .map(|(variant_name, variant_type)| {
+                    let paramname = Sym::new("x");
+                    let signature = ast::Signature {
+                        params: vec![(paramname, variant_type.clone())],
+                        rettype: Type::Named(name, generics.clone()),
+                        typeparams: generics.clone(),
+                    };
+                    // Just return the value passed to it wrapped
+                    // in a constructor of some kind...?
+                    let contents = ir.insert(Expr::Var { name: paramname });
+                    let body = vec![ir.insert(Expr::SumCtor {
+                        name,
+                        variant: *variant_name,
+                        body: contents,
+                    })];
+                    let e = ir.insert(Expr::Lambda { signature, body });
+                    //println!("{} is {:#?}", variant_name, e);
+                    (*variant_name, e)
+                })
+                .collect();
+            let init_val = ir.insert(Expr::StructCtor { body: struct_body });
+            let struct_typebody = body
+                .iter()
+                .map(|(variant_name, variant_type)| {
+                    // The function inside the struct has the signature
+                    // `fn(variant_type) name`
+                    (
+                        *variant_name,
+                        Type::Func(
+                            vec![variant_type.clone()],
+                            Box::new(Type::Named(name, generics.clone())),
+                            generics.clone(),
+                        ),
+                    )
+                })
+                .collect();
+            let struct_type = Type::Struct(struct_typebody, generics.clone());
+            let new_constdef = Const {
+                name: name.to_owned(),
+                typ: struct_type,
+                init: init_val,
+            };
+            //println!("Lowered to {:#?}", &new_constdef);
+            ir.decls.push(new_constdef);
+        }
+        // For other types, we create a constructor function to build them.
+        other => {
+            let s = Sym::new("x");
+            trace!("Lowering params {:?}", params);
+            let type_params: Vec<_> = params.iter().map(|s| Type::Generic(*s)).collect();
+            let signature = ast::Signature {
+                params: vec![(s, other.clone())],
+                rettype: Type::Named(name.to_owned(), type_params.clone()),
+                typeparams: type_params.clone(),
+            };
+            // The generated function just returns the value passed to it wrapped
+            // in a type constructor
+            let contents = ir.insert(Expr::Var { name: s });
+            let body = vec![ir.insert(Expr::TypeCtor {
+                name,
+                type_params,
+                body: contents,
+            })];
+            //println!("{} is {:#?}", variant_name, e);
+            let new_fundecl = Function {
+                name: name.to_owned(),
+                signature,
+                body,
+            };
+            ir.decls.push(new_fundecl);
+        }
+    }
+}
+
+/// Lower an AST decl to IR.
+///
+/// Is there a more elegant way of doing this than passing an accumulator?
+/// Returning a vec is lame.  Return an iterator?  Sounds like work.
+fn lower_decl(ir: &mut Ir, decl: &ast::Decl) {
+    use ast::Decl as D;
+    match decl {
+        D::Function {
+            name,
+            signature,
+            body,
+            ..
+        } => {
+            let body = lower_exprs(ir, body);
+            ir.decls.push(Decl::Function {
+                name: *name,
+                signature: lower_signature(signature),
+                body,
+            })
+        }
+        D::Const {
+            name,
+            typename,
+            init,
+            ..
+        } => {
+            let init = lower_expr(ir, init);
+            ir.decls.push(Decl::Const {
+                name: *name,
+                typ: typename.clone(),
+                init,
+            })
+        }
+        // this needs to generate the typedef AND the type constructor
+        // declaration.  Plus the type deconstructor.
+        D::TypeDef {
+            name,
+            typedecl,
+            doc_comment: _,
+            params,
+        } => {
+            lower_typedef(ir, *name, typedecl, params);
+            ir.decls.push(Decl::TypeDef {
+                name: *name,
+                params: params.clone(),
+                typedecl: typedecl.clone(),
+            });
+        }
+        D::Import { name, rename } => ir.decls.push(Decl::Import {
+            name: *name,
+            localname: rename.unwrap_or(*name),
+        }),
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::ast::Expr as A;
+    use crate::hir2::Expr as I;
+    use crate::hir2::ExprNode as EN;
+
+    /// Does `return;` turn into `return ();`?
+    /// Not doing that to make parsing simpler, since we don't
+    /// actually have semicolons.
+    #[test]
+    fn test_return_none() {
+        let ir = &mut Ir::new("", "");
+        let input = A::Return {
+            retval: Box::new(A::unit()),
+        };
+        let retval = Expr::unit(ir);
+        let output = ir.insert(I::Return { retval });
+        let res = lower_expr(ir, &input);
+        assert_eq!(&res, &output);
+    }
+
+    /*
+    /// Do we turn chained if's properly into nested ones?
+    /// Single if.
+    #[test]
+    fn test_if_lowering_single() {
+        let input = A::If {
+            cases: vec![ast::IfCase {
+                condition: Box::new(A::bool(false)),
+                body: vec![A::int(1)],
+            }],
+            falseblock: vec![A::int(2)],
+        };
+        let output = EN::new(I::If {
+            cases: vec![
+                (EN::bool(false), vec![EN::int(1)]),
+                (EN::bool(true), vec![EN::int(2)]),
+            ],
+        });
+        let res = lower_expr(ir, &input);
+        assert_eq!(&res, &output);
+    }
+
+    /// Do we turn chained if's properly into nested ones?
+    /// Chained if/else's
+    #[test]
+    fn test_if_lowering_chained() {
+        let input = A::If {
+            cases: vec![
+                ast::IfCase {
+                    condition: Box::new(A::bool(false)),
+                    body: vec![A::int(1)],
+                },
+                ast::IfCase {
+                    condition: Box::new(A::bool(true)),
+                    body: vec![A::int(2)],
+                },
+            ],
+            falseblock: vec![A::int(3)],
+        };
+        let output = EN::new(I::If {
+            cases: vec![
+                (EN::bool(false), vec![EN::int(1)]),
+                (EN::bool(true), vec![EN::int(2)]),
+                (EN::bool(true), vec![EN::int(3)]),
+            ],
+        });
+        let res = lower_expr(ir, &input);
+        assert_eq!(&res, &output);
+    }
+
+    /// Do we panic if we get an impossible if with no cases?
+    #[test]
+    #[should_panic]
+    fn test_if_nothing() {
+        let input = A::If {
+            cases: vec![],
+            falseblock: vec![],
+        };
+        let _ = lower_expr(ir, &input);
+    }
+    */
+}

          
A => src/passes2.rs +574 -0
@@ 0,0 1,574 @@ 
+//! Transformation/optimization passes that function on the IR.
+//! May happen before or after typechecking, either way.
+//! For now each is a function from `IR -> IR`, with some recursion
+//! scheme helper functions.  There's two types of passes, one that
+//! happens before type checking and one that happens after.
+//!
+//! It could probably be more efficient, especially with
+//! IR nodes flattened into a vec, but that doesn't matter for now.
+//! We just allocate our way to happiness.
+//!
+//! TODO: A pass that might be useful would be "alpha renaming".
+//! Essentially you walk through your entire compilation unit and
+//! rename everything to a globally unique name.  This means that
+//! scope vanishes, for example, and anything potentially
+//! ambiguous becomes unambiguous.  It can come after typechecking tho.
+//!
+//!
+//! TODO: Passes that need to be done still:
+//!  * Closure conversion
+//!  * Monomorphization
+//!  * Pointerification, turning large struct values into pointers?  Maybe.
+//!  * Turning all enums into ints?  Maybe.
+
+// Not 100% convinced that having a submodule for each pass is
+// necessary, since many of the passes are pretty small, but
+// oh well.
+
+mod lambda_lift;
+use crate::hir2::{Decl as D, EIdx, Expr as E, ExprNode, Ir};
+use crate::*;
+
+type Pass = fn(Ir) -> Ir;
+
+/// Tck has to be mutable because we may change the return type
+/// of expr nodes.
+type TckPass = fn(Ir, &mut typeck::Tck) -> Ir;
+
+pub fn run_passes(ir: Ir) -> Ir {
+    // TODO: It may be more efficient to compose passes rather than fold them?
+    // That way they don't need to build the full intermediate IR each time.
+    // You can use the recursion scheme pattern for this.
+    // That will take some nontrivial restructuring of expr_map though, will also need
+    // a decl_map or something that can compose multiple passes together.
+    // Probably not *difficult*, but tricksy.
+    let passes: &[Pass] = &[lambda_lift::lambda_lift];
+    passes.iter().fold(ir, |prev_ir, f| f(prev_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] = &[];
+    let res = passes.iter().fold(ir, |prev_ir, f| f(prev_ir, tck));
+    res
+}
+
+/// Handy do-nothing combinator
+fn id<T>(thing: T) -> T {
+    thing
+}
+
+/// This is the core recusion scheme that takes a function and applies
+/// it to an expression, and all its subexpressions.
+/// The function is `&mut FnMut` so if it needs to have access to more
+/// data between calls besides the expression itself, just smuggle it
+/// into the closure.  Trying to make this generic over different funcs
+/// signatures is probably not worth the effort
+///
+/// To handle multiple expressions this will turn more into a fold()
+/// than a map(), it will take an accumulator that gets threaded through
+/// everything...  That gets *very weird* though and I manage to pour
+/// my coffee on my cat this morning so let's give that a miss for now.
+/// As it is, this can only transform subtrees into other subtrees.
+///
+/// We don't really need a version of this for decls, 'cause decls
+/// can't be nested.
+///
+/// The function has to take and return an ExprNode, not an expr,
+/// because they may have to look up the type of the expression.
+///
+/// This can take a function to call to transform the current node
+/// before descending into sub-expressions
+/// sub-nodes, and one to call after.  Use expr_map_pre()
+/// and expr_map_post() to just do a pre-traversal or a post-traversal
+/// specifically.
+fn expr_map(
+    ir: &Ir,
+    out: &mut Ir,
+    expr: ExprNode,
+    f_pre: &mut dyn FnMut(ExprNode) -> EIdx,
+    f_post: &mut dyn FnMut(ExprNode) -> EIdx,
+) -> hir2::EIdx {
+    let thing = f_pre(expr);
+    let exprfn = &mut |e| match e {
+        // Nodes with no recursive expressions.
+        // I list all these out explicitly instead of
+        // using a catchall so it doesn't fuck up if we change
+        // the type of Expr.
+        E::Lit { .. } => e,
+        E::Var { .. } => e,
+        E::Break => e,
+        E::EnumCtor { .. } => e,
+        E::TupleCtor { body } => E::TupleCtor {
+            body: exprs_map(out, body, f_pre, f_post),
+        },
+        E::StructCtor { body } => {
+            let new_body = body
+                .into_iter()
+                .map(|(sym, vl)| (sym, expr_map(ir, out, vl, f_pre, f_post)))
+                .collect();
+            E::StructCtor { body: new_body }
+        }
+        E::TypeCtor {
+            name,
+            type_params,
+            body,
+        } => E::TypeCtor {
+            name,
+            type_params,
+            body: expr_map(ir, out, body, f_pre, f_post),
+        },
+        E::SumCtor {
+            name,
+            variant,
+            body,
+        } => E::SumCtor {
+            name,
+            variant,
+            body: expr_map(ir, out, body, f_pre, f_post),
+        },
+        E::ArrayCtor { body } => E::ArrayCtor {
+            body: exprs_map(out, body, f_pre, f_post),
+        },
+        E::TypeUnwrap { expr } => E::TypeUnwrap {
+            expr: expr_map(ir, out, expr, f_pre, f_post),
+        },
+        E::TupleRef { expr, elt } => E::TupleRef {
+            expr: expr_map(ir, out, expr, f_pre, f_post),
+            elt,
+        },
+        E::StructRef { expr, elt } => E::StructRef {
+            expr: expr_map(ir, out, expr, f_pre, f_post),
+            elt,
+        },
+        E::ArrayRef { expr, idx } => E::ArrayRef {
+            expr: expr_map(ir, out, expr, f_pre, f_post),
+            idx,
+        },
+        E::Assign { lhs, rhs } => E::Assign {
+            lhs: expr_map(ir, out, lhs, f_pre, f_post), // TODO: Think real hard about lvalues
+            rhs: expr_map(ir, out, rhs, f_pre, f_post),
+        },
+        E::BinOp { op, lhs, rhs } => E::BinOp {
+            op,
+            lhs: expr_map(ir, out, lhs, f_pre, f_post),
+            rhs: expr_map(ir, out, rhs, f_pre, f_post),
+        },
+        E::UniOp { op, rhs } => E::UniOp {
+            op,
+            rhs: expr_map(ir, out, rhs, f_pre, f_post),
+        },
+        E::Block { body } => E::Block {
+            body: exprs_map(out, body, f_pre, f_post),
+        },
+        E::Let {
+            varname,
+            typename,
+            init,
+            mutable,
+        } => E::Let {
+            varname,
+            typename,
+            init: expr_map(ir, out, init, f_pre, f_post),
+            mutable,
+        },
+        E::If { cases } => {
+            let new_cases = cases
+                .into_iter()
+                .map(|(test, case)| {
+                    let new_test = expr_map(ir, out, test, f_pre, f_post);
+                    let new_cases = exprs_map(out, case, f_pre, f_post);
+                    (new_test, new_cases)
+                })
+                .collect();
+            E::If { cases: new_cases }
+        }
+        E::Loop { body } => E::Loop {
+            body: exprs_map(out, body, f_pre, f_post),
+        },
+        E::Return { retval } => E::Return {
+            retval: expr_map(ir, out, retval, f_pre, f_post),
+        },
+        E::Funcall {
+            func,
+            params,
+            type_params,
+        } => {
+            let new_func = expr_map(ir, out, func, f_pre, f_post);
+            let new_params = exprs_map(out, params, f_pre, f_post);
+            E::Funcall {
+                func: new_func,
+                params: new_params,
+                type_params,
+            }
+        }
+        E::Lambda { signature, body } => E::Lambda {
+            signature,
+            body: exprs_map(out, body, f_pre, f_post),
+        },
+        E::Typecast { e, to } => E::Typecast {
+            e: expr_map(ir, out, e, f_pre, f_post),
+            to,
+        },
+        E::Ref { expr } => E::Ref {
+            expr: expr_map(ir, out, expr, f_pre, f_post),
+        },
+        E::Deref { expr } => E::Deref {
+            expr: expr_map(ir, out, expr, f_pre, f_post),
+        },
+    };
+    let post_thing = thing.map(exprfn);
+    f_post(post_thing)
+}
+
+pub fn expr_map_pre(expr: ExprNode, f: &mut dyn FnMut(ExprNode) -> ExprNode) -> ExprNode {
+    expr_map(ir, out, expr, f, &mut id)
+}
+
+pub fn expr_map_post(expr: ExprNode, f: &mut dyn FnMut(ExprNode) -> ExprNode) -> ExprNode {
+    expr_map(ir, out, expr, &mut id, f)
+}
+
+/// Map functions over a list of exprs.
+fn exprs_map(
+    in: &Ir,
+    out: &mut Ir,
+    exprs: Vec<ExprNode>,
+    f_pre: &mut dyn FnMut(ExprNode) -> EIdx,
+    f_post: &mut dyn FnMut(ExprNode) -> EIdx,
+) -> Vec<EIdx> {
+    exprs
+        .into_iter()
+        .map(|e| expr_map(ir, out, e, f_pre, f_post))
+        .collect()
+}
+
+fn exprs_map_pre(
+    out: &mut Ir,
+    exprs: Vec<ExprNode>,
+    f: &mut dyn FnMut(ExprNode) -> ExprNode,
+) -> Vec<EIdx> {
+    exprs_map(out, exprs, f, &mut id)
+}
+
+fn _exprs_map_post(
+    out: &mut Ir,
+    exprs: Vec<ExprNode>,
+    f: &mut dyn FnMut(ExprNode) -> ExprNode,
+) -> Vec<EIdx> {
+    exprs_map(out, exprs, &mut id, f)
+}
+
+fn decl_map_pre(
+    out: &mut Ir,
+    decl: D,
+    fe: &mut dyn FnMut(ExprNode) -> ExprNode,
+    ft: &mut dyn FnMut(Type) -> Type,
+) {
+    decl_map(out, decl, fe, &mut id, ft)
+}
+
+fn decl_map_post(
+    out: &mut Ir,
+    decl: D,
+    fe: &mut dyn FnMut(ExprNode) -> ExprNode,
+    ft: &mut dyn FnMut(Type) -> Type,
+) {
+    decl_map(out, decl, &mut id, fe, ft)
+}
+
+/// Takes a decl and applies the given expr and type transformers to any
+/// expression in it.
+///
+/// Technically not very necessary, since decls can't contain other decls,
+/// but happens often enough that it's worth having.
+fn decl_map(
+    out: &mut Ir,
+    decl: D,
+    fe_pre: &mut dyn FnMut(ExprNode) -> ExprNode,
+    fe_post: &mut dyn FnMut(ExprNode) -> ExprNode,
+    ft: &mut dyn FnMut(Type) -> Type,
+) {
+    let d = match decl {
+        D::Function {
+            name,
+            signature,
+            body,
+        } => D::Function {
+            name,
+            signature: signature_map(signature, ft),
+            body: exprs_map(out, body, fe_pre, fe_post),
+        },
+        D::Const { name, typ, init } => D::Const {
+            name,
+            typ: type_map(typ, ft),
+            init: expr_map(ir, out, init, fe_pre, fe_post),
+        },
+        D::TypeDef {
+            name,
+            params,
+            typedecl,
+        } => D::TypeDef {
+            name,
+            params,
+            typedecl: type_map(typedecl, ft),
+        },
+        D::Import { .. } => decl,
+    };
+    out.decls.push(d)
+}
+
+fn types_map(typs: Vec<Type>, f: &mut dyn FnMut(Type) -> Type) -> Vec<Type> {
+    typs.into_iter().map(|t| type_map(t, f)).collect()
+}
+
+/// Recursion scheme to turn one type into another.
+fn type_map(typ: Type, f: &mut dyn FnMut(Type) -> Type) -> Type {
+    /// Really this is only here to be cute, it's used a grand total of twice.
+    /// There's probably some horrible combinator chain we could use to make
+    /// it generic over any iterator, if we want to make life even harder for
+    /// ourself.
+    fn types_map_btree<K>(
+        typs: BTreeMap<K, Type>,
+        f: &mut dyn FnMut(Type) -> Type,
+    ) -> BTreeMap<K, Type>
+    where
+        K: Ord,
+    {
+        typs.into_iter()
+            .map(|(key, ty)| (key, type_map(ty, f)))
+            .collect()
+    }
+    let res = match typ {
+        Type::Struct(fields, generics) => {
+            let fields = types_map_btree(fields, f);
+            Type::Struct(fields, generics)
+        }
+        Type::Sum(fields, generics) => {
+            let new_fields = types_map_btree(fields, f);
+            Type::Sum(new_fields, generics)
+        }
+        Type::Array(ty, len) => Type::Array(Box::new(type_map(*ty, f)), len),
+        Type::Func(args, rettype, typeparams) => {
+            let new_args = types_map(args, f);
+            let new_rettype = type_map(*rettype, f);
+            let new_typeparams = types_map(typeparams, f);
+            Type::Func(new_args, Box::new(new_rettype), new_typeparams)
+        }
+        // Not super sure whether this is necessary, but can't hurt.
+        Type::Named(nm, tys) => Type::Named(nm, types_map(tys, f)),
+        Type::Prim(_) => typ,
+        Type::Never => typ,
+        Type::Enum(_) => typ,
+        Type::Generic(_) => typ,
+        Type::Uniq(t) => {
+            let new_t = type_map(*t, f);
+            Type::Uniq(Box::new(new_t))
+        }
+    };
+    f(res)
+}
+
+/// Produce a new signature by transforming the types
+fn signature_map(sig: hir::Signature, f: &mut dyn FnMut(Type) -> Type) -> hir::Signature {
+    let new_params = sig
+        .params
+        .into_iter()
+        .map(|(sym, ty)| (sym, type_map(ty, f)))
+        .collect();
+    hir::Signature {
+        params: new_params,
+        rettype: type_map(sig.rettype, f),
+        typeparams: types_map(sig.typeparams, f),
+    }
+}
+
+pub fn generate_type_name(typ: &Type) -> String {
+    match typ {
+        Type::Enum(fields) => {
+            let fieldnames: Vec<_> = fields
+                .iter()
+                .map(|(nm, vl)| format!("F{}_{}", nm, vl))
+                .collect();
+            let fieldstr = fieldnames.join("_");
+            format!("__Enum{}", fieldstr)
+        }
+        Type::Func(params, rettype, typeparams) => {
+            let paramnames: Vec<String> = params.iter().map(generate_type_name).collect();
+            let paramstr = paramnames.join("_");
+            let retname = generate_type_name(rettype);
+            let tparamnames: Vec<String> = typeparams.iter().map(generate_type_name).collect();
+            let tparamstr = tparamnames.join("_");
+            format!("__Func__{}__{}_{}", paramstr, retname, tparamstr)
+        }
+        Type::Struct(body, _) => {
+            let fieldnames: Vec<_> = body
+                .iter()
+                .map(|(nm, ty)| format!("{}_{}", nm, generate_type_name(ty)))
+                .collect();
+            let fieldstr = fieldnames.join("_");
+            format!("__Struct__{}", fieldstr)
+        }
+        Type::Sum(body, _) => {
+            let fieldnames: Vec<_> = body
+                .iter()
+                .map(|(nm, ty)| format!("{}_{}", nm, generate_type_name(ty)))
+                .collect();
+            let fieldstr = fieldnames.join("_");
+            format!("__Sum__{}", fieldstr)
+        }
+        Type::Generic(name) => {
+            format!("__G{}", name)
+        }
+        Type::Named(name, fields) => {
+            let field_names: Vec<_> = fields.iter().map(generate_type_name).collect();
+            let field_str = field_names.join("_");
+            format!("__Named{}__{}", name, field_str)
+        }
+        Type::Prim(p) => p.get_name().into_owned(),
+        Type::Never => format!("!"),
+        Type::Array(t, len) => {
+            format!("__Arr{}__{}", generate_type_name(t), len)
+        }
+        Type::Uniq(t) => {
+            format!("__Uniq__{}", generate_type_name(t))
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    #[test]
+    fn test_expr_map(ir, )) {
+        fn swap_binop_args(expr: ExprNode) -> ExprNode {
+            trace!("Swapping {:?}", expr);
+            expr.map(&mut |e| match e {
+                E::BinOp { op, lhs, rhs } => E::BinOp {
+                    op,
+                    lhs: rhs,
+                    rhs: lhs,
+                },
+                other => other,
+            })
+        }
+        // Make sure this works for trivial exppressions
+        let inp = ExprNode::new(E::BinOp {
+            op: hir::BOp::Add,
+            lhs: ExprNode::int(3),
+            rhs: ExprNode::int(4),
+        });
+        let desired = ExprNode::new(E::BinOp {
+            op: hir::BOp::Add,
+            lhs: ExprNode::int(4),
+            rhs: ExprNode::int(3),
+        });
+        let out = expr_map_pre(inp, &mut swap_binop_args);
+        assert_eq!(out, desired);
+
+        // Make sure it recurses properly for deeper trees.
+        let inp2 = ExprNode::new(E::BinOp {
+            op: hir::BOp::Add,
+            lhs: ExprNode::new(E::BinOp {
+                op: hir::BOp::Sub,
+                lhs: ExprNode::int(100),
+                rhs: ExprNode::int(200),
+            }),
+            rhs: ExprNode::int(4),
+        });
+        let desired2 = ExprNode::new(E::BinOp {
+            op: hir::BOp::Add,
+            lhs: ExprNode::int(4),
+            rhs: ExprNode::new(E::BinOp {
+                op: hir::BOp::Sub,
+                lhs: ExprNode::int(200),
+                rhs: ExprNode::int(100),
+            }),
+        });
+        let out2 = expr_map_pre(inp2, &mut swap_binop_args);
+        assert_eq!(out2, desired2);
+    }
+
+    /// Test whether our pre-traversal expr map works properly.
+    #[test]
+    fn test_expr_pretraverse() {
+        let inp = ExprNode::new(E::Block {
+            body: vec![ExprNode::new(E::Var {
+                name: Sym::new("foo"),
+            })],
+        });
+        // Make a transformer that renames vars, and check in the Block
+        // body whether the inner var has been transformed.
+        let f = &mut |e: ExprNode| {
+            let helper = &mut |e| match e {
+                E::Var { .. } => E::Var {
+                    name: Sym::new("bar!"),
+                },
+                E::Block { body } => {
+                    // Has the name within the body been transformed yet?
+                    {
+                        let inner = &*body[0].e;
+                        assert_eq!(
+                            inner,
+                            &E::Var {
+                                name: Sym::new("foo")
+                            }
+                        );
+                    }
+                    E::Block { body }
+                }
+                _other => _other,
+            };
+            e.map(helper)
+        };
+        let outp = expr_map_pre(inp, f);
+        // Make sure that the actual transformation has done what we expected.
+        let expected = ExprNode::new(E::Block {
+            body: vec![ExprNode::new(E::Var {
+                name: Sym::new("bar!"),
+            })],
+        });
+        assert_eq!(outp, expected);
+    }
+
+    /// Similar to above with the case in helper() reversed
+    #[test]
+    fn test_expr_posttraverse() {
+        let inp = ExprNode::new(E::Block {
+            body: vec![ExprNode::new(E::Var {
+                name: Sym::new("foo"),
+            })],
+        });
+        let f = &mut |e: ExprNode| {
+            let helper = &mut |e| match e {
+                E::Var { .. } => E::Var {
+                    name: Sym::new("bar!"),
+                },
+                E::Block { body } => {
+                    // Has the name within the body been transformed yet?
+                    {
+                        let inner = &*body[0].e;
+                        assert_eq!(
+                            inner,
+                            &E::Var {
+                                name: Sym::new("bar!")
+                            }
+                        );
+                    }
+                    E::Block { body }
+                }
+                _other => _other,
+            };
+            e.map(helper)
+        };
+        let outp = expr_map_post(inp, f);
+        // Make sure that the actual transformation has done what we expected.
+        let expected = ExprNode::new(E::Block {
+            body: vec![ExprNode::new(E::Var {
+                name: Sym::new("bar!"),
+            })],
+        });
+        assert_eq!(outp, expected);
+    }
+}

          
A => src/passes2/lambda_lift.rs +63 -0
@@ 0,0 1,63 @@ 
+//! Lambda lifting.
+//!
+//! Basically take any function expression and turn it into
+//! a standalone function.
+//!
+//! Note this does NOT do closure conversion yet; this will break
+//! closures. We will need a spearate pass operating before this to take a
+//! closure, define an environment type for it, and turn it into
+//! a function taking an arg of that environment type.
+
+use crate::passes2::*;
+use crate::*;
+
+/// Lambda lift a single expr, creating a new toplevel function if necessary.
+fn lambda_lift_expr(out: &mut Ir, expr: ExprNode, output_funcs: &mut Vec<D>) -> EIdx {
+    let result = &mut |e| match e {
+        E::Lambda { signature, body } => {
+            // This is actually the only important bit.
+            // TODO: Make a more informative name, maybe including the file and line number or
+            // such.
+            let lambda_name = INT.gensym("lambda");
+            let function_decl = D::Function {
+                name: lambda_name,
+                signature,
+                body: exprs_map_pre(out, body, &mut |e| lambda_lift_expr(e, output_funcs)),
+            };
+            output_funcs.push(function_decl);
+            E::Var { name: lambda_name }
+        }
+        x => x,
+    };
+    expr.map(result)
+}
+
+/// A transformation pass that removes lambda expressions and turns
+/// them into function decl's.
+/// TODO: Doesn't handle actual closures yet though.  That should be a
+/// separate pass that happens first?
+///
+/// TODO: Output is a HIR tree that does not have any lambda expr's in it, which
+/// I would like to make un-representable, but don't see a good way
+/// to do yet.  Add a tag type of some kind to the Ir?
+/// Eeeeeeeeh we might just have to check on it later one way or another,
+/// either when doing codegen or when transforming our HIR to a lower-level IR.
+pub(super) fn lambda_lift(ir: Ir) -> Ir {
+    let mut new_functions = vec![];
+    let new_decls: Vec<D> = ir
+        .decls
+        .into_iter()
+        .map(|decl| {
+            decl_map_pre(
+                decl,
+                &mut |e| lambda_lift_expr(e, &mut new_functions),
+                &mut |t| t,
+            )
+        })
+        .collect();
+    new_functions.extend(new_decls.into_iter());
+    Ir {
+        decls: new_functions,
+        ..ir
+    }
+}