~jojo/kapreolo

parametric type synonyms
nullary type synonyms
formatting & add rustfmt.toml
move polyfun :for to beginning of Fun
add `cast` (truncating + (s/z)extending + reinterpreting)

Much like Rust's `as`, but no float, pointer, and bool casting for
now.
limit polytypes (Forall) to just functions (PolyFun)

Only having polytype functions will be easier to handle in the lower
layers, without really sacrificing any useful functionality of the language.
Replace interpreter with cranelift based JIT backend

Basically feature parity. Some more complex polymorphic stuff we can't
quite do yet, but we'll get to that later.

Also rework `base` a fair bit. Basically, make it more low level &
machine like. It's a relatively thin abstraction over cranelift at
this point. This results in `jit.rs` not being overly long (624 SLOC)
while `abase.rs` is now about 3 times bigger than before. `abase.rs`
now contains logic for dynamic dispatch with value witness tables,
among other things.

Added a bit of `Const` evaluation & baked it into `abase_operation`.
Means we can do sizeof-calculations using the same methods as normal.
make vwt an actual table & include alignment
jit block params
re-impl abase of tuples (construct & access)

Now, all tuples are stored indirectly, and construction & accessing
happens in a getelementptr+load/store kind of fashion.
remove Size & StaticSize in favour of Operand & Const

With the upcoming changes related to calculating alignment & field
offset, Size would kind of become baggage. Better to improve our
Const-evaluation system instead.
simplify jit by moving callconv & converge logic to abase
store temporaries on heap if size is runtime & too big

I'd prefer to use `alloca` like they do here in Swift:
https://youtu.be/ctS8FzqcRug?t=329.
But I suppose that there are cons to treating the stack like that as
well, I guess.

With the implemented approach, we prepare a stack slot which is 4 machine
words big for the temporary of statically unknown size to be stored
in. If it (at runtime) turns out to be bigger than that, we `malloc`
and store it on the heap. We don't properly free this memory though,
so currently we have a leak. I want to rely on a GC to clean this up
in the future. Same for closure captures when the time comes.
put target machine pointer size in BodyGenerator for convenience
Add 2 (failing) tests for problems with how we gen poly funs
fix some errors not printed in tests
jit polytype functions & sketch out dynamic dispatch with VWTs

VWT = value witness table. Each type variable in the signature of a
function gets a VWT passed along as a function parameter. This is /
will be our approach to dynamic dispatch. The VWT will include
implementations for stuff like allocating, copying, & destroying
values of the type.
comment/remove ZeroInit, tuple stuff in backend, & Converge::Block

(for now, at least)

This stuff is currently unused, and will probably be implemented in a
different way later on.
jit initializer functions with system-v calling convention

or there will be errors when calling them from Rust as `extern "C" fn`
when they actually use an ABI-unstable tail calling convention. By
coincidence this has not been an issue yet, but it will be as soon as
we get some functions with more complex signatures (i.e. in the
upcoming commit that will add primitive dynamic dispatch)
let's go with more primitive types in Base. ints & ptr

I think it will do us good to move Base slightly lower. More like
Cranelift & Mir, in that we only really work with integer types,
bools, and pointers. No "structure of this and that" type, no
"function from this to that" type. Let's keep those kinds of
abstractions in Kapo, and move Base closer to the machine. It's all
just bits & bytes in the end.
Next